ListViewの項目が自動的に並び替えられるようにするには、ListView.SortingプロパティをSortOrder.Ascending(昇順)またはSortOrder.Descending(降順)にします。ただしこの方法では、サブアイテムを比較しての並び替えができず、数字、時間の並び替えも正しく行われません。
これらの問題を解決するためには、ListView.ListViewItemSorterプロパティに、並べ方を定義したIComparerインターフェイスを実装したクラスのインスタンスを設定します。なお、IComparerによる並び替えについては、「コレクション(または配列)内の要素を方法を指定して並び替える(または検索する)」でも説明しています。
まずはごく簡単な例を示します。ここではListViewコントロール(ListView1)で列ヘッダがクリックされた時に、その列の文字列を昇順に並べ替えるようにします。そのために、まずは次のようなクラスを作成します。
Imports System.Collections Imports System.Windows.Forms ''' <summary> ''' ListViewの項目の並び替えに使用するクラス ''' </summary> Public Class ListViewItemComparer Implements IComparer Private _column As Integer ''' <summary> ''' ListViewItemComparerクラスのコンストラクタ ''' </summary> ''' <param name="col">並び替える列番号</param> Public Sub New(ByVal col As Integer) _column = col End Sub 'xがyより小さいときはマイナスの数、大きいときはプラスの数、 '同じときは0を返す Public Function Compare(ByVal x As Object, ByVal y As Object) _ As Integer Implements System.Collections.IComparer.Compare 'ListViewItemの取得 Dim itemx As ListViewItem = CType(x, ListViewItem) Dim itemy As ListViewItem = CType(y, ListViewItem) 'xとyを文字列として比較する Return String.Compare(itemx.SubItems(_column).Text, _ itemy.SubItems(_column).Text) End Function End Class
using System.Collections; using System.Windows.Forms; /// <summary> /// ListViewの項目の並び替えに使用するクラス /// </summary> public class ListViewItemComparer : IComparer { private int _column; /// <summary> /// ListViewItemComparerクラスのコンストラクタ /// </summary> /// <param name="col">並び替える列番号</param> public ListViewItemComparer(int col) { _column = col; } //xがyより小さいときはマイナスの数、大きいときはプラスの数、 //同じときは0を返す public int Compare(object x, object y) { //ListViewItemの取得 ListViewItem itemx = (ListViewItem) x; ListViewItem itemy = (ListViewItem) y; //xとyを文字列として比較する return string.Compare(itemx.SubItems[_column].Text, itemy.SubItems[_column].Text); } }
このクラスを使って並べ替えを行うには、ListView.ColumnClickイベントハンドラでListViewItemSorterを設定します。次のコードはListViewのあるフォームクラス内に記述してください。これで列ヘッダをクリックすると、並び替えられるようになったはずです。
'フォームのLoadイベントハンドラ Private Sub Form1_Load(ByVal sender As Object, _ ByVal e As EventArgs) Handles MyBase.Load '詳細表示にする ListView1.View = View.Details 'ColumnClickイベントハンドラの追加 AddHandler ListView1.ColumnClick, _ AddressOf ListView1_ColumnClick 'ListViewItemを追加する ListView1.Items.Add( _ New ListViewItem(New String() {"おはよう", "80"})) ListView1.Items.Add( _ New ListViewItem(New String() {"こんにちは", "5"})) ListView1.Items.Add( _ New ListViewItem(New String() {"おやすみ", "100"})) '列の追加 ListView1.Columns.Add("文字列", 100, HorizontalAlignment.Left) ListView1.Columns.Add("数字", 100, HorizontalAlignment.Right) End Sub '列がクリックされた時 Private Sub ListView1_ColumnClick(ByVal sender As Object, _ ByVal e As ColumnClickEventArgs) 'ListViewItemSorterを指定する ListView1.ListViewItemSorter = _ New ListViewItemComparer(e.Column) '並び替える(ListViewItemSorterを設定するとSortが自動的に呼び出される) 'ListView1.Sort() End Sub
//フォームのLoadイベントハンドラ private void Form1_Load(object sender, System.EventArgs e) { //詳細表示にする ListView1.View = View.Details; //ColumnClickイベントハンドラの追加 ListView1.ColumnClick += new ColumnClickEventHandler(ListView1_ColumnClick); //ListViewItemを追加する ListView1.Items.Add( new ListViewItem(new string[] {"おはよう", "80"})); ListView1.Items.Add( new ListViewItem(new string[] {"こんにちは", "5"})); ListView1.Items.Add( new ListViewItem(new string[] {"おやすみ", "100"})); //列の追加 ListView1.Columns.Add( "文字列", 100, HorizontalAlignment.Left); ListView1.Columns.Add( "数字", 100, HorizontalAlignment.Right); } //列がクリックされた時 private void ListView1_ColumnClick( object sender, ColumnClickEventArgs e) { //ListViewItemSorterを指定する ListView1.ListViewItemSorter = new ListViewItemComparer(e.Column); //並び替える(ListViewItemSorterを設定するとSortが自動的に呼び出される) //ListView1.Sort(); }
せっかくなので、上記のListViewItemComparerクラスをさらに拡張して、より多機能にしてみましょう。
まず、昇順、降順を指定できるようにします。さらに、文字列として並び替えるか、それとも、数字や日時として並び替えるかという「並び替え方」を変更できるようにしましょう。
昇順、降順をいちいち指定するのは面倒なので、並び替える列を指定するColumnプロパティで前と同じ列が指定された時は、トグルで昇順、降順が換わるようにします。また、あらかじめ列ごとに並べ替え方を指定できるようにしておきます。
これらの改良を加えたListViewItemComparerクラスは次のようなものです。
Imports System Imports System.Collections Imports System.Windows.Forms ''' <summary> ''' ListViewの項目の並び替えに使用するクラス ''' </summary> Public Class ListViewItemComparer Implements IComparer ''' <summary> ''' 比較する方法 ''' </summary> Public Enum ComparerMode ''' <summary> ''' 文字列として比較 ''' </summary> [String] ''' <summary> ''' 数値(Int32型)として比較 ''' </summary> [Integer] ''' <summary> ''' 日時(DataTime型)として比較 ''' </summary> DateTime End Enum Private _column As Integer Private _order As SortOrder Private _mode As ComparerMode Private _columnModes As ComparerMode() ''' <summary> ''' 並び替えるListView列の番号 ''' </summary> Public Property Column() As Integer '現在と同じ列の時は、昇順降順を切り替える Get Return _column End Get Set(value As Integer) If _column = value Then If _order = SortOrder.Ascending Then _order = SortOrder.Descending ElseIf _order = SortOrder.Descending Then _order = SortOrder.Ascending End If End If _column = value End Set End Property ''' <summary> ''' 昇順か降順か ''' </summary> Public Property Order() As SortOrder Get Return _order End Get Set(value As SortOrder) _order = value End Set End Property ''' <summary> ''' 並び替えの方法 ''' </summary> Public Property Mode() As ComparerMode Get Return _mode End Get Set(value As ComparerMode) _mode = value End Set End Property ''' <summary> ''' 列ごとの並び替えの方法 ''' </summary> Public WriteOnly Property ColumnModes() As ComparerMode() Set(value As ComparerMode()) _columnModes = value End Set End Property ''' <summary> ''' ListViewItemComparerクラスのコンストラクタ ''' </summary> ''' <param name="col">並び替える列の番号</param> ''' <param name="ord">昇順か降順か</param> ''' <param name="cmod">並び替えの方法</param> Public Sub New(col As Integer, ord As SortOrder, cmod As ComparerMode) _column = col _order = ord _mode = cmod End Sub Public Sub New() _column = 0 _order = SortOrder.Ascending _mode = ComparerMode.[String] End Sub 'xがyより小さいときはマイナスの数、大きいときはプラスの数、 '同じときは0を返す Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer _ Implements IComparer.Compare If _order = SortOrder.None Then '並び替えない時 Return 0 End If Dim result As Integer = 0 'ListViewItemの取得 Dim itemx As ListViewItem = DirectCast(x, ListViewItem) Dim itemy As ListViewItem = DirectCast(y, ListViewItem) '並べ替えの方法を決定 If Not (_columnModes Is Nothing) AndAlso _ _columnModes.Length > _column Then _mode = _columnModes(_column) End If '並び替えの方法別に、xとyを比較する Select Case _mode Case ComparerMode.[String] '文字列として比較 result = String.Compare( _ itemx.SubItems(_column).Text, _ itemy.SubItems(_column).Text) Exit Select Case ComparerMode.[Integer] 'Int32に変換して比較 '.NET Framework 2.0からは、TryParseメソッドを使うこともできる result = _ Integer.Parse(itemx.SubItems(_column).Text).CompareTo( _ Integer.Parse(itemy.SubItems(_column).Text)) Exit Select Case ComparerMode.DateTime 'DateTimeに変換して比較 '.NET Framework 2.0からは、TryParseメソッドを使うこともできる result = DateTime.Compare( _ DateTime.Parse(itemx.SubItems(_column).Text), _ DateTime.Parse(itemy.SubItems(_column).Text)) Exit Select End Select '降順の時は結果を+-逆にする If _order = SortOrder.Descending Then result = -result End If '結果を返す Return result End Function End Class
using System; using System.Collections; using System.Windows.Forms; /// <summary> /// ListViewの項目の並び替えに使用するクラス /// </summary> public class ListViewItemComparer : IComparer { /// <summary> /// 比較する方法 /// </summary> public enum ComparerMode { /// <summary> /// 文字列として比較 /// </summary> String, /// <summary> /// 数値(Int32型)として比較 /// </summary> Integer, /// <summary> /// 日時(DataTime型)として比較 /// </summary> DateTime }; private int _column; private SortOrder _order; private ComparerMode _mode; private ComparerMode[] _columnModes; /// <summary> /// 並び替えるListView列の番号 /// </summary> public int Column { set { //現在と同じ列の時は、昇順降順を切り替える if (_column == value) { if (_order == SortOrder.Ascending) { _order = SortOrder.Descending; } else if (_order == SortOrder.Descending) { _order = SortOrder.Ascending; } } _column = value; } get { return _column; } } /// <summary> /// 昇順か降順か /// </summary> public SortOrder Order { set { _order = value; } get { return _order; } } /// <summary> /// 並び替えの方法 /// </summary> public ComparerMode Mode { set { _mode = value; } get { return _mode; } } /// <summary> /// 列ごとの並び替えの方法 /// </summary> public ComparerMode[] ColumnModes { set { _columnModes = value; } } /// <summary> /// ListViewItemComparerクラスのコンストラクタ /// </summary> /// <param name="col">並び替える列の番号</param> /// <param name="ord">昇順か降順か</param> /// <param name="cmod">並び替えの方法</param> public ListViewItemComparer( int col, SortOrder ord, ComparerMode cmod) { _column = col; _order = ord; _mode = cmod; } public ListViewItemComparer() { _column = 0; _order = SortOrder.Ascending; _mode = ComparerMode.String; } //xがyより小さいときはマイナスの数、大きいときはプラスの数、 //同じときは0を返す public int Compare(object x, object y) { if (_order == SortOrder.None) { //並び替えない時 return 0; } int result = 0; //ListViewItemの取得 ListViewItem itemx = (ListViewItem)x; ListViewItem itemy = (ListViewItem)y; //並べ替えの方法を決定 if (_columnModes != null && _columnModes.Length > _column) { _mode = _columnModes[_column]; } //並び替えの方法別に、xとyを比較する switch (_mode) { case ComparerMode.String: //文字列をとして比較 result = string.Compare(itemx.SubItems[_column].Text, itemy.SubItems[_column].Text); break; case ComparerMode.Integer: //Int32に変換して比較 //.NET Framework 2.0からは、TryParseメソッドを使うこともできる result = int.Parse(itemx.SubItems[_column].Text).CompareTo( int.Parse(itemy.SubItems[_column].Text)); break; case ComparerMode.DateTime: //DateTimeに変換して比較 //.NET Framework 2.0からは、TryParseメソッドを使うこともできる result = DateTime.Compare( DateTime.Parse(itemx.SubItems[_column].Text), DateTime.Parse(itemy.SubItems[_column].Text)); break; } //降順の時は結果を+-逆にする if (_order == SortOrder.Descending) { result = -result; } //結果を返す return result; } }
このクラスの使用法は例えば次のようになります。
'ListViewItemSorterに指定するフィールド Dim listViewItemSorter As ListViewItemComparer 'フォームのLoadイベントハンドラ Private Sub Form1_Load(ByVal sender As Object, _ ByVal e As EventArgs) Handles MyBase.Load '詳細表示にする ListView1.View = View.Details 'ColumnClickイベントハンドラの追加 AddHandler ListView1.ColumnClick, _ AddressOf ListView1_ColumnClick 'ListViewItemを追加する ListView1.Items.Add( _ New ListViewItem(New String() {"おはよう", "80"})) ListView1.Items.Add( _ New ListViewItem(New String() {"こんにちは", "5"})) ListView1.Items.Add( _ New ListViewItem(New String() {"おやすみ", "100"})) '列の追加 ListView1.Columns.Add("文字列", 100, HorizontalAlignment.Left) ListView1.Columns.Add("数字", 100, HorizontalAlignment.Right) 'ListViewItemComparerの作成と設定 listViewItemSorter = New ListViewItemComparer listViewItemSorter.ColumnModes = _ New ListViewItemComparer.ComparerMode() _ {ListViewItemComparer.ComparerMode.String, _ ListViewItemComparer.ComparerMode.Integer} 'ListViewItemSorterを指定する ListView1.ListViewItemSorter = listViewItemSorter End Sub '列がクリックされた時 Private Sub ListView1_ColumnClick(ByVal sender As Object, _ ByVal e As ColumnClickEventArgs) 'クリックされた列を設定 listViewItemSorter.Column = e.Column '並び替える ListView1.Sort() End Sub
//ListViewItemSorterに指定するフィールド ListViewItemComparer listViewItemSorter; //フォームのLoadイベントハンドラ private void Form1_Load(object sender, System.EventArgs e) { //詳細表示にする ListView1.View = View.Details; //ColumnClickイベントハンドラの追加 ListView1.ColumnClick += new ColumnClickEventHandler(ListView1_ColumnClick); //ListViewItemを追加する ListView1.Items.Add( new ListViewItem(new string[] {"おはよう", "80"})); ListView1.Items.Add( new ListViewItem(new string[] {"こんにちは", "5"})); ListView1.Items.Add( new ListViewItem(new string[] {"おやすみ", "100"})); //列の追加 ListView1.Columns.Add("文字列", 100, HorizontalAlignment.Left); ListView1.Columns.Add("数字", 100, HorizontalAlignment.Right); //ListViewItemComparerの作成と設定 listViewItemSorter = new ListViewItemComparer(); listViewItemSorter.ColumnModes = new ListViewItemComparer.ComparerMode[] { ListViewItemComparer.ComparerMode.String, ListViewItemComparer.ComparerMode.Integer }; //ListViewItemSorterを指定する ListView1.ListViewItemSorter = listViewItemSorter; } //列がクリックされた時 private void ListView1_ColumnClick( object sender, ColumnClickEventArgs e) { //クリックされた列を設定 listViewItemSorter.Column = e.Column; //並び替える ListView1.Sort(); }