DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

ListViewの項目を並び替える

広告

ListViewの項目が自動的に並び替えられるようにするには、ListViewのSortingプロパティをSortOrder.Ascending(昇順)またはSortOrder.Descending(降順)にします。ただしこの方法では、サブアイテムを比較しての並び替えや、数字、時間の並び替えが正しく行われません。

これらの問題を解決するためには、ListViewのListViewItemSorterプロパティに、並べ方を定義したIComparerインターフェイスを実装したクラスのインスタンスを設定します。なお、IComparerによる並び替えについては、「コレクション(または配列)内の要素を方法を指定して並び替える(または検索する)」でも説明しています。

まずはごく簡単な例を示します。ここではListViewコントロール(ListView1)で列ヘッダがクリックされた時に、その列の文字列を昇順に並べ替えるようにします。そのために、まずは次のようなクラスを作成します。


[VB.NET]
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
[C#]
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のあるフォームクラス内に記述してください。これで列ヘッダをクリックすると、並び替えられるようになったはずです。

[VB.NET]
'フォームの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
[C#]
//フォームの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クラスは次のようなものです。

[VB.NET]
Imports System 
Imports System.Collections 
Imports System.Windows.Forms 

''' <summary>
''' ListViewの項目の並び替えに使用するクラス
''' </summary>
Public Class ListViewItemComparer
    Implements IComparer

    ''' <summary>
    ''' 比較する方法
    ''' </summary>
    Public Enum ComparerMode
        [String]
        [Integer]
        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(ByVal Value As Integer)
            If _column = value Then
                If _order = SortOrder.Ascending Then
                    _order = SortOrder.Descending
                Else
                    If _order = SortOrder.Descending Then
                        _order = SortOrder.Ascending
                    End If
                End If
            End If
            _column = value
        End Set
    End Property

    ''' <summary>
    ''' 昇順か降順か
    ''' </summary>
    Public Property Order() As SortOrder
        Get
            Return _order
        End Get
        Set(ByVal Value As SortOrder)
            _order = Value
        End Set
    End Property

    ''' <summary>
    ''' 並び替えの方法
    ''' </summary>
    Public Property Mode() As ComparerMode
        Get
            Return _mode
        End Get
        Set(ByVal Value As ComparerMode)
            _mode = Value
        End Set
    End Property

    ''' <summary>
    ''' 列ごとの並び替えの方法
    ''' </summary>
    Public WriteOnly Property ColumnModes() As ComparerMode()
        Set(ByVal Value As ComparerMode())
            _columnModes = Value
        End Set
    End Property

    ''' <summary>
    ''' ListViewItemComparerクラスのコンストラクタ
    ''' </summary>
    ''' <param name="col">並び替える列番号</param>
    ''' <param name="ord">昇順か降順か</param>
    ''' <param name="mthd">並び替えの方法</param>
    Public Sub New(ByVal col As Integer, ByVal ord As SortOrder, _
            ByVal 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
        Dim result As Integer = 0
        'ListViewItemの取得
        Dim itemx As ListViewItem = CType(x, ListViewItem)
        Dim itemy As ListViewItem = CType(y, ListViewItem)

        '並べ替えの方法を決定
        If Not (_columnModes Is Nothing) And _
                _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)
            Case ComparerMode.Integer
                result = Integer.Parse(itemx.SubItems(_column).Text) - _
                    Integer.Parse(itemy.SubItems(_column).Text)
            Case ComparerMode.DateTime
                result = DateTime.Compare( _
                    DateTime.Parse(itemx.SubItems(_column).Text), _
                    DateTime.Parse(itemy.SubItems(_column).Text))
        End Select

        '降順の時は結果を+-逆にする
        If _order = SortOrder.Descending Then
            result = -result
        Else
            If _order = SortOrder.None Then
                result = 0
            End If
        End If
        '結果を返す
        Return result
    End Function
End Class
[C#]
using System;
using System.Collections;
using System.Windows.Forms;

/// <summary>
/// ListViewの項目の並び替えに使用するクラス
/// </summary>
public class ListViewItemComparer : IComparer
{
    /// <summary>
    /// 比較する方法
    /// </summary>
    public enum ComparerMode
    {
        String,
        Integer,
        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="mthd">並び替えの方法</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)
    {
        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:
                result = int.Parse(itemx.SubItems[_column].Text) - 
                    int.Parse(itemy.SubItems[_column].Text);
                break;
            case ComparerMode.DateTime:
                result = DateTime.Compare(
                    DateTime.Parse(itemx.SubItems[_column].Text), 
                    DateTime.Parse(itemy.SubItems[_column].Text));
                break;
        }

        //降順の時は結果を+-逆にする
        if (_order == SortOrder.Descending)
            result = -result;
        else if (_order == SortOrder.None)
            result = 0;

        //結果を返す
        return result;
    }
}

このクラスの使用法は例えば次のようになります。

[VB.NET]
'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
[C#]
//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();
}

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。