ListViewコントロールのAllowColumnReorderプロパティをTrueにしてユーザーがColumnの並び替えをできるようにしたとき、並び替えられたColumnの順番を取得する方法と、Columnの順番を復元する方法を紹介します。
.NET Framework 2.0からは、ColumnHeader.DisplayIndexプロパティが追加されました。これにより、Columnの順番の取得と設定が行えます。(コメントでご報告いただきました。)
以下の例では、フォームにリストビュー(ListView1)と2つのボタン(Button1、Button2)が配置されている時に、Button1をクリックするとListView1のColumnの順番を保存し、Button2をクリックすると復元するようにしています。
'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
//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の順番を取得する方法は、残念ながら.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の順番を取得するものとします。
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
[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コントロールにColumnHeaderを追加するときの順番を変えればいいだけの話ですが、上記の方法のように、LVM_SETCOLUMNORDERARRAYメッセージを使うという方法もあります。
以下にLVM_SETCOLUMNORDERARRAYメッセージを使ってListViewコントロールのColumnの順番を設定する例を示します。ここでは、WindowsフォームにListViewコントロール(ListView1)とボタン(Button1)が配置されており、Button1をクリックした時にListView1のColumnの順番をものと戻す(Index通りの順番に戻す)ようにしています。
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
[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); }
(この記事は、「.NETプログラミング研究 第46号」で紹介したものを基にしています。)