ファイルやフォルダにタグをつけ検索できるアプリケーションを作成してきましたが、これまでは検索結果はファイルパスでソートされているだけでした。今回はファイルとフォルダに重要度をつけて重要度が高い順にソートして表示できるようにします。
また同じファイルやフォルダでも検索するワードによって重要度が違います。それぞれのワードで表示順序を変更することができるようにします。
まず重要度を管理するオブジェクトを作成し、これをFileTagオブジェクト内に格納します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class FileTag { public string FilePath = ""; public string Text = ""; public List<Importance> Importances = new List<Importance>(); public void SetImportance(string keyword, int score) { Importance imp = Importances.FirstOrDefault(x => x.Keyword == keyword); if(imp != null) imp.Score = score; else { Importance newImp = new Importance(); newImp.Keyword = keyword; newImp.Score = score; Importances.Add(newImp); } } public int GetImportance(string keyword) { Importance imp = Importances.FirstOrDefault(x => x.Keyword == keyword); if(imp != null) return imp.Score; else return 0; } } |
1 2 3 4 5 6 |
public class Importance { public Importance(){} public string Keyword = ""; public int Score = 0; } |
次に検索結果を表示するフォームですが、コントロールを追加します。
重要度を表示するNumericUpDownコントロールとメニューを追加しました。それからリストビューの項目が増えました。
[表示]からアイコンの大きさを変更できるようにしました。IconSizeプロパティが変更されると再度検索がおこなわれ、リストビューが再描画されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
public partial class Form2 : Form { int _iconSize = -1; int IconSize { get { return _iconSize; } set { if(_iconSize != value) { _iconSize = value; SearchFileTags(Keyword); } } } private void MenuItemDetails_Click(object sender, EventArgs e) { IconSize = -1; } private void MenuItemIcon32_Click(object sender, EventArgs e) { IconSize = 32; } private void MenuItemIcon64_Click(object sender, EventArgs e) { IconSize = 64; } private void MenuItemIcon128_Click(object sender, EventArgs e) { IconSize = 128; } private void MenuItemIconFreeSize_Click(object sender, EventArgs e) { FormIconSize f = new FormIconSize(); f.IconSize = IconSize; if(f.ShowDialog() == DialogResult.OK) IconSize = f.IconSize; f.Dispose(); } } |
SearchFileTags(string newKeyword)は検索をして検索結果を表示するためのメソッドです。同じキーワードで自動的に検索しなければならないかもしれないので、フィールド変数に検索ワードを格納しています。
このなかで呼び出されている GetFolderImage()メソッド、GetFileImage(string path)メソッド、GetListItemImage(Image image, int size)メソッドは「画像ファイルなら画像で表示する」と同じものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
public partial class Form2 : Form { string Keyword = ""; void SearchFileTags(string newKeyword) { if(newKeyword == "") return; var selItems = listView1.SelectedItems; if(selItems.Count != 0) { SaveOldData(); SelectedPath = ""; // SelectedPathプロパティに空文字をセットしてもなにも起きない。 } Keyword = newKeyword; Form1 f = (Form1)this.Owner; List<FileTag> fileTags = f.FileTags.Where(x => x.Text.IndexOf(newKeyword) != -1).ToList(); fileTags = fileTags.OrderByDescending(x => x.GetImportance(newKeyword)).ThenBy(x => x.FilePath).ToList(); if(IconSize == -1) listView1.View = View.Details; else listView1.View = View.LargeIcon; // 不要になったイメージリストとイメージは破棄する if(listView1.LargeImageList != null) { ImageList list = listView1.LargeImageList; foreach(Image image in list.Images) image.Dispose(); list.Dispose(); listView1.LargeImageList = null; } ImageList imageList = new ImageList(); listView1.LargeImageList = imageList; listView1.SmallImageList = imageList; if(listView1.View == View.Details) imageList.ImageSize = new Size(16, 16); else imageList.ImageSize = new Size(IconSize, IconSize); imageList.ColorDepth = ColorDepth.Depth32Bit; int imageIndex = 0; listView1.Items.Clear(); listView1.BeginUpdate(); foreach(FileTag fileTag in fileTags) { int imp = fileTag.GetImportance(newKeyword); string comment = fileTag.Text; if(Directory.Exists(fileTag.FilePath)) { DirectoryInfo info = new DirectoryInfo(fileTag.FilePath); ListViewItem item = new ListViewItem(new string[] { info.Name, imp.ToString(), "フォルダ", info.CreationTime.ToString(), info.LastWriteTime.ToString(), "", // フォルダなのでサイズはない comment }); listView1.Items.Add(item); item.Tag = fileTag; Image image = GetFolderImage(); imageList.Images.Add(image); item.ImageIndex = imageIndex; imageIndex++; } if(File.Exists(fileTag.FilePath)) { FileInfo info = new FileInfo(fileTag.FilePath); ListViewItem item = new ListViewItem(new string[] { info.Name, imp.ToString(), "ファイル", info.CreationTime.ToString(), info.LastWriteTime.ToString(), (info.Length/1024 +1).ToString(), comment }); listView1.Items.Add(item); item.Tag = fileTag; Bitmap bitmap = GetFileImage(info.FullName); imageList.Images.Add(bitmap); item.ImageIndex = imageIndex; imageIndex++; } } listView1.EndUpdate(); } } |
上記のメソッドを作成したので、[検索]ボタンが押されたときの処理を変更しました。ここには長々とコードを書かず上記のメソッドを呼び出すだけにしました。
1 2 3 4 5 6 7 |
public partial class Form2 : Form { private void button1_Click(object sender, EventArgs e) { SearchFileTags(textBox1.Text); } } |
SaveOldData()メソッドはリストビューにおいて新しいアイテムが選択されたときに、これまで選択されていたものに対応するデータを保存するために呼び出されるものです。新たに別のキーワードで検索されるときにもこれまで選択されていたものに対応するデータを保存する必要があるので呼び出されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public partial class Form2 : Form { void SaveOldData() { if(SelectedPath == "") return; // ファイル等が削除またはリネームされているかもしれない if(!File.Exists(SelectedPath) && !Directory.Exists(SelectedPath)) { SearchWhenNotExist(); return; } FileInfo oldInfo = new FileInfo(SelectedPath); foreach(ListViewItem item in listView1.Items) { if(item.Text == oldInfo.Name) { var subItem = item.SubItems; subItem[1].Text = numericUpDownImportance.Value.ToString(); subItem[6].Text = richTextBox1.Text; break; } } Form1 f = (Form1)this.Owner; // 既存のコメントはあるはず。ないとおかしい。 FileTag tag = f.FileTags.FirstOrDefault(x => x.FilePath == SelectedPath); if(tag != null) { tag.Text = richTextBox1.Text; tag.SetImportance(Keyword, (int)numericUpDownImportance.Value); } } } |
SearchWhenNotExist()メソッドは選択されたファイルまたはフォルダが存在しないときに呼び出されます。エラーを示すメッセージボックスを表示したあと、検索をやり直します。
1 2 3 4 5 6 7 8 |
public partial class Form2 : Form { void SearchWhenNotExist() { MessageBox.Show("選択されたファイルまたはフォルダが存在しません!", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); SearchFileTags(Keyword); } } |
LoadNewData()メソッドは新しいアイテムが選択されたときに、それに対応するデータをロードして表示するメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public partial class Form2 : Form { void LoadNewData() { if(SelectedPath == "") { _selectedPath = ""; textBoxSelectedPath.Text = ""; richTextBox1.Text = ""; numericUpDownImportance.Value = (int)0; return; } if(!File.Exists(SelectedPath) && !Directory.Exists(SelectedPath)) { SearchWhenNotExist(); return; } Form1 f = (Form1)this.Owner; _selectedPath = SelectedPath; textBoxSelectedPath.Text = SelectedPath; FileTag tag = f.FileTags.FirstOrDefault(x => x.FilePath == _selectedPath); if(tag != null) { richTextBox1.Text = tag.Text; numericUpDownImportance.Value = (int)tag.GetImportance(Keyword); } } } |
リストビューで選択されているアイテムが変更されたときにはイベントハンドラ listView1_SelectedIndexChanged内でSelectedPathプロパティが変更されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public partial class Form2 : Form { private void listView1_SelectedIndexChanged(object sender, EventArgs e) { var selItems = listView1.SelectedItems; // 選択されているアイテムが存在しないときはなにもしない。 if(selItems.Count == 0) return; FileTag tag = (FileTag)selItems[0].Tag; SelectedPath = tag.FilePath; } } |
SelectedPathプロパティは以下のようになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public partial class Form2 : Form { string _selectedPath = ""; string SelectedPath { get { return _selectedPath; } set { // これまで選択されていたアイテムに関連づけられたデータを記憶する SaveOldData(); _selectedPath = value; // 新たに選択されたアイテムに関連づけられたデータを表示する LoadNewData(); } } } |
LoadNewData()メソッドは新たに選択されたアイテムに関連づけられたデータを表示するためのものです。最初にSelectedPathプロパティが有効なパスなのかどうかを調べています。もしSelectedPathプロパティが有効なパスではない場合はエラーメッセージを表示して検索しなおします。ただしSelectedPath == “”のときは何もしません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public partial class Form2 : Form { void LoadNewData() { if(SelectedPath == "") { _selectedPath = ""; textBoxSelectedPath.Text = ""; richTextBox1.Text = ""; numericUpDownImportance.Value = (int)0; return; } if(!File.Exists(SelectedPath) && !Directory.Exists(SelectedPath)) { SearchWhenNotExist(); return; } textBoxSelectedPath.Text = SelectedPath; Form1 f = (Form1)this.Owner; FileTag tag = f.FileTags.FirstOrDefault(x => x.FilePath == _selectedPath); if(tag != null) { richTextBox1.Text = tag.Text; numericUpDownImportance.Value = (int)tag.GetImportance(Keyword); } } } |
リストビューのアイテムがダブルクリックされた場合は有効なパスかどうか調べ、フォルダであればエクスプローラーで開き、ファイルであれば関連付けられているアプリケーションで開きます。有効なパスでなければエラーメッセージを表示して検索をやりなおします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public partial class Form2 : Form { private void listView1_DoubleClick(object sender, EventArgs e) { var sels = listView1.SelectedItems; FileTag tag = (FileTag)sels[0].Tag; if(Directory.Exists(tag.FilePath)) OpenFile(tag.FilePath); else if(File.Exists(tag.FilePath)) OpenFile(tag.FilePath); else SearchWhenNotExist(); } } |
検索結果は重要度順に並ぶようになっていますが、自分で値を指定すると値が大きい順にはならなくなります。
メニューで[重要度順に並べ替える]を選択すると検索結果を重要度順に並べなおします。
1 2 3 4 5 6 7 |
public partial class Form2 : Form { private void MenuItemSort_Click(object sender, EventArgs e) { SearchFileTags(Keyword); } } |