件名どおり、リストビューで、カラム(列・縦のライン)ごとのソート(並び替え)をする方法を書きます。
以下の過去ログを参考にしました。
http://www.activebasic.com/forum/viewtopic.php?t=576
三日ぐらい悩んで完成させたのですが、基本的な事なのになぜか過去ログに載ってない様なので、私の苦労が皆様のお役に立てばと思い、投稿します。
詳しい解説は、私が書くより以下のページへ。言語は違うけど、やってる事は同じなので分かると思います。
http://www.kumei.ne.jp/c_lang/sdk2/sdk_110.htm
■私の環境
OS WinXP Home SP
AB 4.13
■準備
RADでリストビューを貼り付ける。
プロパティで、表示をレポートにする。
リストビューのイベントコードでカラムの1つがクリックされたときを選択。
■コード
コード:
Dim hLV As HWND 'リストビューハンドル
Dim SortLV =1 As Long '昇/降順判定
hLV = GetDlgItem(hMainWnd,ListView1)
Sub MainWnd_ListView1_ColumnClick(ByRef nmListView As NMLISTVIEW) 'リストソート カラムクリック
If SortLV > 0 Then
SortLV = -1
Else
SortLV = 1
End If
SendMessage(hLV, LVM_SORTITEMS, nmListView.iSubItem, AddressOf(CompareProc) As LPARAM)
End Sub
Function CompareProc(lp1 As Long, lp2 As Long, lpSort As Long) As Long
Dim nItem1 As Long '比較のためのコールバック関数
Dim nItem2 As Long
Dim bBuf1[MAX_PATH] As Byte
Dim bBuf2[MAX_PATH] As Byte
Dim lvf As LVFINDINFO
lvf.flags = LVFI_PARAM
lvf.lParam = lp1
nItem1 = SendMessage(hLV, LVM_FINDITEM, -1, VarPtr(lvf) As LPARAM)
lvf.lParam = lp2
nItem2 = SendMessage(hLV, LVM_FINDITEM, -1, VarPtr(lvf) As LPARAM)
GetLVText(nItem1, lpSort, bBuf1)
GetLVText(nItem2, lpSort, bBuf2)
If lpSort = 0 or lpSort = 3 Then 'カラム(列)が0.3の時は数値で比較
nItem1 = Val(bBuf1) As Long
nItem2 = Val(bBuf2) As Long
If nItem1 = nItem2 Then
CompareProc = 0
ElseIf nItem1 > nItem2 Then
CompareProc = 1
Else
CompareProc = -1
End If
Else
CompareProc = lstrcmp(bBuf1, bBuf2)
End If
If SortLV < 0 Then CompareProc = CompareProc * -1 '降順 -1を掛ける。昇順はそのまま。
End Function
Sub GetLVText(ByRef iItem As Long, ByRef iSubItem As Long, ByRef pszText[MAX_PATH] As Byte)
Dim lvi As LVITEM
With lvi
.mask = LVIF_TEXT
.iItem = iItem
.iSubItem = iSubItem
.cchTextMax = MAX_PATH
.pszText = pszText
End With
SendMessage(hLV, LVM_GETITEM, 0, VarPtr(lvi) As LPARAM)
End Sub
Sub SetLVText(ByRef iItem As Long, ByRef iSubItem As Long, ByRef pszText As String)
Dim lvi As LVITEM
With lvi
.iItem = iItem
.iSubItem = iSubItem
.cchTextMax = MAX_PATH
.pszText = StrPtr(pszText)
If .iSubItem = 0 Then
.mask = LVIF_TEXT or LVIF_PARAM
.lParam = iItem
SendMessage(hLV,LVM_INSERTITEM,0,VarPtr(lvi) As LPARAM) '0 = インサート
Else
.mask = LVIF_TEXT
SendMessage(hLV,LVM_SETITEM,0,VarPtr(lvi) As LPARAM) '0 <> セット
End If
End With
End Sub
■補足
まずリストビューにアイテムを入れるのにはSetLVTextを使います。
サブアイテムが0の時にインサートし、その時にLVIF_PARAMを追加します。(これをやらないとソートできない)
ここでは、行(横のライン)の値をそのまま使ってます。
カラムクリックでNMLISTVIEW構造体にどのカラムがクリックされたか入るのでそれを利用します。
昇順/降順はクリックするたびに替わります。
CompareProcの最後の所は
コード:
CompareProc = lstrcmp(bBuf1, bBuf2)
If SortLV < 0 Then CompareProc = CompareProc * -1
で、良いのですが、例えば1~100までの数値をソートした場合、1、10、100、11、12…19、2、20と変な並びになってしまいます。
そこでlpSortにはクリックされたカラムが入ってますので、ここでは1番目と4番目の時は数値にして比較してます。
しかし、文字と数が混ざっている場合は不可能です。ここら辺良いやり方を知っていたら教えて欲しいです。
注意点は、扱える文字列の長さがMAX_PATHですので変えたい場合は、関数を弄ったりして下さい。
setLVText、getLVTextとも引数がByRefになってるので気をつけてください。
リストビューから文字列を得るのにはGetLVTextを使いますが
過去ログの所ではLVM_GETITEMTEXTで文字列をリストビューから取得していて、
それだと設定したlParamの値が取得できないらしく、これに気づくまでに相当無意味な時間を過ごしました。
文字列がByte配列だったり、String型だったりしてますが、それは大目に見てください。
(ここら辺が未だに4.13を使ってる理由になってると思います・汗)
こうした方が速いとか、簡単だとか、上記の数値が順番どおりに並ぶ方法とかあったら教えて下さい。