【C#】ListViewのソート処理、ヘッダーのマーク変更をやってみた
おはようございます。
今回はリストビューのヘッダークリックでソートし、且つマークを表示してみます。
ファイルの一覧なので、ファイル名とサイズのソートをちょっと変えています。
プログラムは、下記の記事のものを流用します。
スポンサーリンク
プログラムの修正
新規クラスの作成
ListViewItemSorter.cs
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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | usingSystem; usingSystem.Collections; usingSystem.Collections.Generic; usingSystem.IO; usingSystem.Windows.Forms; namespaceWindowsFormsApp1 { publicclassListViewItemSorter:IComparer { /// <summary> /// 比較する方法 /// </summary> publicenumComparerMode { /// <summary> /// 文字列として比較 /// </summary> String, /// <summary> /// 数値(Int32型)として比較 /// </summary> Integer, /// <summary> /// 日時(DataTime型)として比較 /// </summary> DateTime, /// <summary> /// ファイル名として比較 /// </summary> FileName, /// <summary> /// ファイルサイズとして比較 /// </summary> FileSize }; privateComparerMode[]_columnModes; privateint_column; /// <summary> /// 並び替えるListView列の番号 /// </summary> publicintColumn { set { //現在と同じ列の時は、昇順降順を切り替える if(_column==value) { if(Order==SortOrder.Ascending) { Order=SortOrder.Descending; } elseif(Order==SortOrder.Descending) { Order=SortOrder.Ascending; } } _column=value; } get { return_column; } } /// <summary> /// 昇順か降順か /// </summary> publicSortOrderOrder{set;get;} /// <summary> /// 並び替えの方法 /// </summary> publicComparerModeMode{set;get;} /// <summary> /// 列ごとの並び替えの方法 /// </summary> publicComparerMode[]ColumnModes { set { _columnModes=value; } } /// <summary> /// コンストラクタ /// </summary> /// <param name="col">並び替える列の番号</param> /// <param name="ord">昇順か降順か</param> /// <param name="cmod">並び替えの方法</param> publicListViewItemSorter(intcol,SortOrder ord,ComparerMode cmod) { Column=col; Order=ord; Mode=cmod; } publicListViewItemSorter() { Column=0; Order=SortOrder.Ascending; Mode=ComparerMode.String; } //xがyより小さいときはマイナスの数、大きいときはプラスの数、 //同じときは0を返す publicintCompare(objectx,objecty) { if(Order==SortOrder.None) { //並び替えない時 return0; } intresult=0; //ListViewItemの取得 ListViewItem itemx=(ListViewItem)x; ListViewItem itemy=(ListViewItem)y; //並べ替えの方法を決定 if(_columnModes!=null&& _columnModes.Length > Column) { Mode = _columnModes[Column]; } //並び替えの方法別に、xとyを比較する switch(Mode) { caseComparerMode.String: //文字列をとして比較 result=string.Compare(itemx.SubItems[Column].Text, itemy.SubItems[Column].Text); break; caseComparerMode.Integer: //Int32に変換して比較 //.NET Framework 2.0からは、TryParseメソッドを使うこともできる result=int.Parse(itemx.SubItems[Column].Text).CompareTo( int.Parse(itemy.SubItems[Column].Text)); break; caseComparerMode.DateTime: //DateTimeに変換して比較 //.NET Framework 2.0からは、TryParseメソッドを使うこともできる result=DateTime.Compare( DateTime.Parse(itemx.SubItems[Column].Text), DateTime.Parse(itemy.SubItems[Column].Text)); break; caseComparerMode.FileName: result=CompareFileName(itemx.Name,itemy.Name); break; caseComparerMode.FileSize: result=CompareFileSize(itemx.SubItems[Column].Text,itemy.SubItems[Column].Text); break; } //降順の時は結果を+-逆にする if(Order==SortOrder.Descending) { result=-result; } //結果を返す returnresult; } /// <summary> /// ファイル名の比較をします. /// </summary> /// <param name="path1"></param> /// <param name="path2"></param> /// <returns></returns> privateintCompareFileName(stringpath1,stringpath2) { FileInfo fi1=newFileInfo(path1); FileInfo fi2=newFileInfo(path2); if(fi1.Attributes==FileAttributes.Directory&& fi2.Attributes != FileAttributes.Directory) { return -1; } elseif(fi1.Attributes!=FileAttributes.Directory&& fi2.Attributes == FileAttributes.Directory) { return 1; } returnstring.Compare(path1,path2); } /// <summary> /// 単位付きのファイルサイズを比較します. /// </summary> /// <param name="size1"></param> /// <param name="size2"></param> /// <returns></returns> privateintCompareFileSize(stringsizeWithUnit1,stringsizeWithUnit2) { stringsize1="0"; stringunit1=""; stringsize2="0"; stringunit2=""; List<string>units=newList<string>(){"KB","MB","GB"}; if(!string.IsNullOrEmpty(sizeWithUnit1)) { size1=sizeWithUnit1.Split(null)[0]; unit1=sizeWithUnit1.Split(null)[1]; } if(!string.IsNullOrEmpty(sizeWithUnit2)) { size2=sizeWithUnit2.Split(null)[0]; unit2=sizeWithUnit2.Split(null)[1]; } // 単位が違う場合は単位で比較 if(unit1!=unit2) { returnunits.IndexOf(unit1).CompareTo(units.IndexOf(unit2)); } returndecimal.Parse(size1).CompareTo(decimal.Parse(size2)); } } } |
グローバル変数、構造体、定数の追加
下記を追加します。
Form1.cs
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 | privateListViewItemSorter ltvFileListSorter=null; [StructLayout(LayoutKind.Sequential)] publicstructHDITEM { publicInt32 mask; publicInt32 cxy; [MarshalAs(UnmanagedType.LPTStr)] publicStringpszText; publicIntPtr hbm; publicInt32 cchTextMax; publicInt32 fmt; publicInt32 lParam; publicInt32 iImage; publicInt32 iOrder; }; // Parameters for ListView-Headers privateconstInt32 HDI_FORMAT=0x0004; privateconstInt32 HDF_LEFT=0x0000; privateconstInt32 HDF_STRING=0x4000; privateconstInt32 HDF_SORTUP=0x0400; privateconstInt32 HDF_SORTDOWN=0x0200; privateconstInt32 LVM_GETHEADER=0x1000+31; // LVM_FIRST + 31 privateconstInt32 HDM_GETITEM=0x1200+11; // HDM_FIRST + 11 privateconstInt32 HDM_SETITEM=0x1200+12; // HDM_FIRST + 12 /// <summary> /// リストビューヘッダのイメージ登録 /// </summary> /// <param name="Handle"></param> /// <param name="msg"></param> /// <param name="wParam"></param> /// <param name="lParam"></param> /// <returns></returns> [DllImport("user32.dll",EntryPoint="SendMessage")] privatestaticexternIntPtr SendMessageITEM(IntPtr Handle,Int32 msg,IntPtr wParam,refHDITEM lParam); |
初期化処理の修正
初期化時にソートクラスを設定
Form1.cs
1 2 3 4 5 6 7 8 9 10 11 12 | // ソートクラスの設定 ltvFileListSorter=newListViewItemSorter(0,SortOrder.Ascending,ListViewItemSorter.ComparerMode.String); ltvFileListSorter.ColumnModes=newListViewItemSorter.ComparerMode[] { ListViewItemSorter.ComparerMode.FileName, ListViewItemSorter.ComparerMode.String, ListViewItemSorter.ComparerMode.String, ListViewItemSorter.ComparerMode.FileSize }; listView1.ListViewItemSorter=ltvFileListSorter; // ソートマークの設定 SetSortMark(listView1.Handle,0,ltvFileListSorter.Order); |
新規メソッド追加
リストのヘッダがクリックされた際のイベントを追加
Form1.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /// <summary> /// ファイル一覧列クリックイベントハンドラ /// </summary> /// <param name="sender"></param> /// <param name="e"></param> privatevoidlistView1_ColumnClick(objectsender,ColumnClickEventArgse) { ltvFileListSorter.Column=e.Column; listView1.Sort(); // 初期化 foreach(ColumnHeadercinlistView1.Columns) { SetSortMark(listView1.Handle,c.Index,SortOrder.None); } SetSortMark(listView1.Handle,e.Column,ltvFileListSorter.Order); } |
リストヘッダーのマークを設定する処理を追加
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 | /// <summary> /// リストビューのヘッダーソートマークの制御. /// </summary> /// <param name="listViewHandle"></param> /// <param name="colIdx"></param> /// <param name="order"></param> publicstaticvoidSetSortMark(IntPtr listViewHandle,intcolIdx,SortOrder order) { IntPtr hColHeader=SendMessage(listViewHandle,LVM_GETHEADER,IntPtr.Zero,IntPtr.Zero); HDITEM hdItem=newHDITEM(); IntPtr colHeader=newIntPtr(colIdx); hdItem.mask=HDI_FORMAT; IntPtr rtn=SendMessageITEM(hColHeader,HDM_GETITEM,colHeader,refhdItem); if(order==SortOrder.Ascending) { hdItem.fmt&= ~HDF_SORTDOWN; hdItem.fmt|=HDF_SORTUP; } elseif(order==SortOrder.Descending) { hdItem.fmt&= ~HDF_SORTUP; hdItem.fmt|=HDF_SORTDOWN; } elseif(order==SortOrder.None) { hdItem.fmt&= ~HDF_SORTDOWN & ~HDF_SORTUP; } rtn=SendMessageITEM(hColHeader,HDM_SETITEM,colHeader,refhdItem); } |
起動してみる
無事にソート、マークの表示ができました。
まとめ
何故、標準で出来ないのか。
謎は深まりますね。
次回は今のところ未定です。
ではでは。
ディスカッション
コメント一覧
面白く読ませて頂きました。
記事にある、”SetSortMark”の定義はどこにあるのでしょうか。
自分ではわかりませんでした。
記事を読んでいただきありがとうございます。
“SetSortMark”の定義、投稿に漏れがありましたので追記しました。
ご指摘ありがとうございます。
今後もよろしくお願いします。
こちらこそ、良記事を上げて頂きありがとうございます。
“SetSortMark”の定義内容がわかりました。
ご対応ありがとうございます。
ただおそらく、”SendMessageITEM”の定義がないかと思います。
「グローバル変数、構造体、定数の追加」のところに、
[DllImport(“user32.dll”, EntryPoint = “SendMessage”)]
private static extern IntPtr SendMessageITEM(IntPtr Handle, Int32 msg, IntPtr wParam, ref HDITEM lParam);
を追加することで動かすことができました。
もしよろしければなのですが、
Git Hubなどを使って、プログラムを上げていただければ、
漏れがあったとしてもソースを追えばわかるので安心です。
何度も申し訳ありません。
記事を修正しましたのでご確認ください。
Git Hub の件、ご提案ありがとうございます。
近い内に導入させていただきたいと思います。
今後ともよろしくお願いします。