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

ListViewのColumnの順番を取得、設定する

ListViewコントロールのAllowColumnReorderプロパティをTrueにしてユーザーがColumnの並び替えをできるようにしたとき、並び替えられたColumnの順番を取得する方法と、Columnの順番を復元する方法を紹介します。

.NET Framework 2.0以降で、ColumnHeader.DisplayIndexプロパティを使用した方法

.NET Framework 2.0からは、ColumnHeader.DisplayIndexプロパティが追加されました。これにより、Columnの順番の取得と設定が行えます。(コメントでご報告いただきました。)

以下の例では、フォームにリストビュー(ListView1)と2つのボタン(Button1、Button2)が配置されている時に、Button1をクリックするとListView1のColumnの順番を保存し、Button2をクリックすると復元するようにしています。

VB.NET
コードを隠すコードを選択
'Columnの位置
Private columnsIndex As Integer() = Nothing

'Button1のClickイベントハンドラ
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) _
        Handles Button1.Click
    'Columnの位置を保存する
    columnsIndex = New Integer(ListView1.Columns.Count) {}

    Dim i As Integer
    For i = 0 To ListView1.Columns.Count - 1
        columnsIndex(i) = ListView1.Columns(i).DisplayIndex
    Next i
End Sub

'Button2のClickイベントハンドラ
Private Sub Button2_Click(ByVal sender As Object, ByVal e As EventArgs) _
        Handles Button2.Click
    If columnsIndex Is Nothing Then
        Return
    End If

    'Columnの位置を復元する
    Dim i As Integer
    For i = 0 To ListView1.Columns.Count - 1
        ListView1.Columns(i).DisplayIndex = columnsIndex(i)
    Next i
End Sub
C#
コードを隠すコードを選択
//Columnの位置
private int[] columnsIndex = null;

//Button1のClickイベントハンドラ
private void Button1_Click(object sender, EventArgs e)
{
    //Columnの位置を保存する
    columnsIndex = new int[ListView1.Columns.Count];

    for (int i = 0; i < ListView1.Columns.Count; i++)
    {
        columnsIndex[i] = ListView1.Columns[i].DisplayIndex;
    }
}

//Button2のClickイベントハンドラ
private void Button2_Click(object sender, EventArgs e)
{
    if (columnsIndex == null)
        return;

    //Columnの位置を復元する
    for (int i = 0; i < ListView1.Columns.Count; i++)
    {
        ListView1.Columns[i].DisplayIndex = columnsIndex[i];
    }
}

ListViewのColumnの順番を取得する

ListViewコントロールのColumnの順番を取得する方法は、残念ながら.NET Framework 1.1以前では用意されていません。よってこの問題を解決するには、Win32 APIのSendMessage関数を使うことになりそうです。

この問題の解決法として掲示板に寄せられた方法は、LVM_GETHEADERメッセージを使ってListViewコントロールの列ヘッダに関する情報を取得するというものです。この方法に関してはこちらのログを見ていただくこととして、ここではLVM_GETCOLUMNORDERARRAYメッセージを使った方法を紹介します。

LVM_GETCOLUMNORDERARRAYメッセージを使用した場合、Columnの順番は、ColumnHeader.Indexをインデックスとした整数値の1次元配列として取得できます。

以下にその具体例を示します。ここでは、WindowsフォームにListViewコントロール(ListView1)とボタン(Button1)が配置されており、Button1をクリックした時にListView1のColumnの順番を取得するものとします。

VB.NET
コードを隠すコードを選択
Private Declare Auto Function SendMessage Lib "user32.dll" ( _
    ByVal hWnd As IntPtr, ByVal msg As Integer, _
    ByVal wParam As Integer, ByVal lParam() As Integer) As Integer

Private Const LVM_FIRST As Integer = &H1000
Private Const LVM_GETCOLUMNORDERARRAY As Integer = LVM_FIRST + 59

''' <summary>
''' ListViewの列ヘッダの順番を取得する
''' </summary>
''' <param name="lv">対象とするListView</param>
''' <returns>列ヘッダの順番を示した配列</returns>
Private Shared Function GetColumnHeaderOrder( _
        ByVal lv As ListView) As Integer()
    Dim count As Integer = lv.Columns.Count
    Dim order(count) As Integer

    If SendMessage(lv.Handle, LVM_GETCOLUMNORDERARRAY, _
            count, order) = 0 Then
        Throw New ApplicationException( _
            "Columnの順番の取得に失敗しました。")
    End If

    Return order
End Function
C#
コードを隠すコードを選択
[DllImport("user32.dll", CharSet=CharSet.Auto)]
private static extern int SendMessage(
    IntPtr hWnd, int msg, int wParam, int [] lParam);

private const int LVM_FIRST = 0x1000;
private const int LVM_GETCOLUMNORDERARRAY = (LVM_FIRST + 59);

/// <summary>
/// ListViewの列ヘッダの順番を取得する
/// </summary>
/// <param name="lv">対象とするListView</param>
/// <returns>列ヘッダの順番を示した配列</returns>
private static int[] GetColumnHeaderOrder(ListView lv)
{
    int count = lv.Columns.Count;
    int[] order = new int[count];

    if (SendMessage(lv.Handle, LVM_GETCOLUMNORDERARRAY, count, order
            == 0)
        throw new ApplicationException(
            "Columnの順番の取得に失敗しました。");

    return order;
}

//Button1のクリックイベントハンドラ
private void Button1_Click(object sender, System.EventArgs e)
{
    //列ヘッダの順番を取得
    int[] orders = GetColumnHeaderOrder(ListView1);

    //列ヘッダのTextとその順番を表示する
    for (int i = 0; i < ListView1.Columns.Count; i++)
        Console.WriteLine(ListView1.Columns[i].Text +
            ":Position=" + orders[i].ToString());
}

ListViewのColumnの順番を設定する

次にListViewコントロールのColumnの順番を設定する方法を紹介します。

これは、ListViewコントロールにColumnHeaderを追加するときの順番を変えればいいだけの話ですが、上記の方法のように、LVM_SETCOLUMNORDERARRAYメッセージを使うという方法もあります。

以下にLVM_SETCOLUMNORDERARRAYメッセージを使ってListViewコントロールのColumnの順番を設定する例を示します。ここでは、WindowsフォームにListViewコントロール(ListView1)とボタン(Button1)が配置されており、Button1をクリックした時にListView1のColumnの順番をものと戻す(Index通りの順番に戻す)ようにしています。

VB.NET
コードを隠すコードを選択
Private Declare Auto Function SendMessage Lib "user32.dll" ( _
    ByVal hWnd As IntPtr, ByVal msg As Integer, _
    ByVal wParam As Integer, ByVal lParam() As Integer) As Integer

Private Const LVM_FIRST As Integer = &H1000
Private Const LVM_SETCOLUMNORDERARRAY As Integer = LVM_FIRST + 58

''' <summary>
''' ListViewの列ヘッダの順番を設定する
''' </summary>
''' <param name="lv">対象とするListView</param>
''' <param name="order">列ヘッダの順番を示した配列</param>
Private Shared Sub SetColumnHeaderOrder( _
        ByVal lv As ListView, ByVal order() As Integer)
    If SendMessage(lv.Handle, LVM_SETCOLUMNORDERARRAY, _
            order.Length, order) = 0 Then
        Throw New ApplicationException( _
            "Columnの順番の設定に失敗しました。")
    End If
End Sub

'Button1のクリックイベントハンドラ
Private Sub Button1_Click(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles Button1.Click
    '列ヘッダの順番を配列に入れる
    Dim orders(ListView1.Columns.Count - 1) As Integer
    Dim i As Integer
    For i = 0 To orders.Length - 1
        orders(i) = i
    Next i

    '列ヘッダの順番を設定する
    SetColumnHeaderOrder(ListView1, orders)
End Sub
C#
コードを隠すコードを選択
[DllImport("user32.dll", CharSet=CharSet.Auto)]
private static extern int SendMessage(
    IntPtr hWnd, int msg, int wParam, int [] lParam);

private const int LVM_FIRST = 0x1000;
private const int LVM_SETCOLUMNORDERARRAY = (LVM_FIRST + 58);

/// <summary>
/// ListViewの列ヘッダの順番を設定する
/// </summary>
/// <param name="lv">対象とするListView</param>
/// <param name="order">列ヘッダの順番を示した配列</param>
private static void SetColumnHeaderOrder(ListView lv, int[] order)
{
    if (SendMessage(lv.Handle, LVM_SETCOLUMNORDERARRAY,
            order.Length, order) == 0)
        throw new ApplicationException(
            "Columnの順番の設定に失敗しました。");
}

//Button1のクリックイベントハンドラ
private void Button1_Click(object sender, System.EventArgs e)
{
    //列ヘッダの順番を配列に入れる
    int[] orders = new int[ListView1.Columns.Count];
    for (int i = 0; i < orders.Length; i++)
        orders[i] = i;

    //列ヘッダの順番を設定する
    SetColumnHeaderOrder(ListView1, orders);
}
  • 履歴:
  • 2007/1/15 「.NET Framework 2.0以降で、ColumnHeader.DisplayIndexプロパティを使用した方法」を追加。

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

  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。