Page 1 of 1
[ListView] Add Column Header SortArrow Icon
Posted: 02 Sep 2021, 06:50
by jNizM
Add and set the Sort Arrow Icon in ListView Column Header
Wait for the ColumnClick Event and get the FMT attribute from the
LVM_GETCOLUMN ListView message and send the
HDF_SORTDOWN or
HDF_SORTUP attribute with the
LVM_SETCOLUMN ListView message.
Code: Select all
MyGui := Gui()
LV := MyGui.Add("ListView", "w300 r8 Grid -LV0x10", ["Column1", "Column2", "Column3"])
loop 7
LV.Add(, A_Index * 1, A_Index * 2, A_Index * 3)
loop 3
LV.ModifyCol(A_Index, "80 Integer")
LV.OnEvent("ColClick", LV_SortArrow)
MyGui.Show()
LV_SortArrow(LV, Column)
{
static HDF_SORTUP := 0x0400
static HDF_SORTDOWN := 0x0200
static LVM_FIRST := 0x1000
static LVM_GETCOLUMNW := LVM_FIRST + 95
static LVM_SETCOLUMNW := LVM_FIRST + 96
static LVCF_FMT := 0x0001
Column -= 1
LVCOLUMNW := Buffer(56, 0)
NumPut("uint", LVCF_FMT, LVCOLUMNW, 0)
SendMessage(LVM_GETCOLUMNW, Column, LVCOLUMNW, LV.hWnd)
fmt := NumGet(LVCOLUMNW, 4, "int")
if (fmt & HDF_SORTUP)
NumPut("int", fmt & ~HDF_SORTUP | HDF_SORTDOWN, LVCOLUMNW, 4)
else if (fmt & HDF_SORTDOWN)
NumPut("int", fmt & ~HDF_SORTDOWN | HDF_SORTUP, LVCOLUMNW, 4)
else
{
loop LV.GetCount("Column")
{
if ((i := A_Index - 1) != Column)
{
SendMessage(LVM_GETCOLUMNW, i, LVCOLUMNW, LV.hWnd)
NumPut("int", NumGet(LVCOLUMNW, 4, "int") & ~(HDF_SORTDOWN | HDF_SORTUP), LVCOLUMNW, 4)
SendMessage(LVM_SETCOLUMNW, i, LVCOLUMNW, LV.hWnd)
}
}
NumPut("int", fmt | HDF_SORTUP, LVCOLUMNW, 4)
}
return SendMessage(LVM_SETCOLUMNW, Column, LVCOLUMNW, LV.hWnd)
}
Thanks to Solar and HotKeyIt for the v1.1 code (
https://autohotkey.com/board/topic/64736-lv-sortarrow-apply-sort-arrows-to-your-listview/)
Re: [ListView] Add Column Header SortArrow Icon
Posted: 02 Sep 2021, 09:21
by kczx3
Nice, keep them coming!
Re: [ListView] Add Column Header SortArrow Icon
Posted: 08 May 2023, 12:59
by iPhilip
@jNizM, Thank you for your implementation. The version below doesn't require looping through all the columns so it may be faster. It takes advantage of the
HDM_GETITEM/
HDM_SETITEM messages.
Code: Select all
LV_SortArrow(LV, Column) {
static HDI_FORMAT := 0x0004
static HDF_SORTDOWN := 0x0200
static HDF_SORTUP := 0x0400
static LVM_FIRST := 0x1000
static HDM_FIRST := 0x1200
static HDM_GETITEM := HDM_FIRST + 11
static HDM_SETITEM := HDM_FIRST + 12
static LVM_GETHEADER := LVM_FIRST + 31
static HDITEM_SIZE := 6 * 4 + 6 * A_PtrSize
static FormatOffset := 3 * 4 + 2 * A_PtrSize
static PrevColumn := 0
HDITEM := Buffer(HDITEM_SIZE)
NumPut 'UInt', HDI_FORMAT, HDITEM, 0
HDR := SendMessage(LVM_GETHEADER, 0, 0, LV)
if PrevColumn && Column != PrevColumn {
SendMessage(HDM_GETITEM, PrevColumn - 1, HDITEM, HDR)
Format := NumGet(HDITEM, FormatOffset, 'Int')
NumPut 'Int', Format & ~(HDF_SORTDOWN | HDF_SORTUP), HDITEM, FormatOffset
SendMessage(HDM_SETITEM, PrevColumn - 1, HDITEM, HDR)
}
PrevColumn := Column
SendMessage(HDM_GETITEM, Column - 1, HDITEM, HDR)
Format := NumGet(HDITEM, FormatOffset, 'Int')
if Format & HDF_SORTDOWN
NumPut 'Int', (Format & ~HDF_SORTDOWN) | HDF_SORTUP, HDITEM, FormatOffset
else if Format & HDF_SORTUP
NumPut 'Int', (Format & ~HDF_SORTUP) | HDF_SORTDOWN, HDITEM, FormatOffset
else
NumPut 'Int', Format | HDF_SORTUP, HDITEM, FormatOffset
return SendMessage(HDM_SETITEM, Column - 1, HDITEM, HDR)
}
Re: [ListView] Add Column Header SortArrow Icon
Posted: 08 May 2023, 13:17
by iPhilip
An alternate approach to the built-in method is to add a character to the header text indicating the direction of the sort. In the implementation below, the character is placed to the left of the text if the column is right-aligned and to the right of the text otherwise.
Code: Select all
LV_SortArrow(LV, Column) {
static SORTUP := Chr(0x25B2) ; (▲) Black Up-Pointing Triangle
static SORTDOWN := Chr(0x25BC) ; (▼) Black Down-Pointing Triangle
static HDI_TEXT := 0x0002
static HDI_FORMAT := 0x0004
static HDF_RIGHT := 0x0001
static LVM_FIRST := 0x1000
static HDM_FIRST := 0x1200
static HDM_GETITEM := HDM_FIRST + 11
static HDM_SETITEM := HDM_FIRST + 12
static LVM_GETHEADER := LVM_FIRST + 31
static HDITEM_SIZE := 6 * 4 + 6 * A_PtrSize
static TextPtrOffset := 2 * 4
static TextSizeOffset := 2 * 4 + 2 * A_PtrSize
static FormatOffset := 3 * 4 + 2 * A_PtrSize
static MaxChars := 260
static PrevColumn := 0
Text := Buffer(2 * MaxChars)
HDITEM := Buffer(HDITEM_SIZE)
NumPut 'UInt', HDI_TEXT | HDI_FORMAT, HDITEM, 0
NumPut 'Ptr', Text.Ptr, HDITEM, TextPtrOffset
NumPut 'Int', MaxChars, HDITEM, TextSizeOffset
HDR := SendMessage(LVM_GETHEADER, 0, 0, LV)
if PrevColumn && Column != PrevColumn {
SendMessage(HDM_GETITEM, PrevColumn - 1, HDITEM, HDR)
Format := NumGet(HDITEM, FormatOffset, 'Int'), String := StrGet(Text)
StrPut Format & HDF_RIGHT ? SubStr(String, 3) : SubStr(String, 1, -2), Text
SendMessage(HDM_SETITEM, PrevColumn - 1, HDITEM, HDR)
}
PrevColumn := Column
SendMessage(HDM_GETITEM, Column - 1, HDITEM, HDR)
Format := NumGet(HDITEM, FormatOffset, 'Int'), String := StrGet(Text)
if Format & HDF_RIGHT
String := RegExMatch(String, '^(' SORTUP '|' SORTDOWN ')(.*)', &Match) ? (Match[1] == SORTDOWN ? SORTUP : SORTDOWN) Match[2] : SORTUP A_Space String
else
String := RegExMatch(String, '(.*)(' SORTUP '|' SORTDOWN ')$', &Match) ? Match[1] (Match[2] == SORTDOWN ? SORTUP : SORTDOWN) : String A_Space SORTUP
StrPut String, Text, MaxChars - 1
return SendMessage(HDM_SETITEM, Column - 1, HDITEM, HDR)
}
- LV_SortArrow.png (7.06 KiB) Viewed 2183 times
Re: [ListView] Add Column Header SortArrow Icon
Posted: 08 May 2023, 17:24
by kczx3
The character approach is probably quite beneficial because I don’t think you can combine a bitmap and use the built-in sort arrows.
Re: [ListView] Add Column Header SortArrow Icon
Posted: 09 May 2023, 04:40
by iPhilip
kczx3 wrote: ↑08 May 2023, 17:24
The character approach is probably quite beneficial because I don’t think you can combine a bitmap and use the built-in sort arrows.
That's correct.
You can display a bitmap to the left (
HDF_BITMAP) or right (
HDF_BITMAP | HDF_BITMAP_ON_RIGHT) of the header text to indicate the direction of the sort. The disadvantage is that it takes up a bit more space in the header. See below.
Code: Select all
LV_SortArrow(LV, Column) {
static OBM_UPARROWD := 32743
static OBM_DNARROWD := 32742
static LR_SHARED := 0x8000
static HDI_FORMAT := 0x0004
static HDI_BITMAP := 0x0010
static HDF_RIGHT := 0x0001
static HDF_BITMAP_ON_RIGHT := 0x1000
static HDF_BITMAP := 0x2000
static LVM_FIRST := 0x1000
static HDM_FIRST := 0x1200
static HDM_GETITEM := HDM_FIRST + 11
static HDM_SETITEM := HDM_FIRST + 12
static LVM_GETHEADER := LVM_FIRST + 31
static HDITEM_SIZE := 6 * 4 + 6 * A_PtrSize
static BitmapOffset := 2 * 4 + 1 * A_PtrSize
static FormatOffset := 3 * 4 + 2 * A_PtrSize
static ColumnState := false
static PrevColumn := 0
HDITEM := Buffer(HDITEM_SIZE)
NumPut 'UInt', HDI_FORMAT | HDI_BITMAP, HDITEM, 0
HDR := SendMessage(LVM_GETHEADER, 0, 0, LV)
if PrevColumn && Column != PrevColumn {
SendMessage(HDM_GETITEM, PrevColumn - 1, HDITEM, HDR)
Format := NumGet(HDITEM, FormatOffset, 'Int')
NumPut 'Int', Format & ~HDF_BITMAP, HDITEM, FormatOffset
SendMessage(HDM_SETITEM, PrevColumn - 1, HDITEM, HDR)
ColumnState := false
}
PrevColumn := Column
ColumnState := !ColumnState
SendMessage(HDM_GETITEM, Column - 1, HDITEM, HDR)
Format := NumGet(HDITEM, FormatOffset, 'Int')
NumPut 'Ptr', DllCall('LoadImageW', 'Ptr', 0, 'Ptr', ColumnState ? OBM_UPARROWD : OBM_DNARROWD, 'UInt', 0, 'Int', 0, 'Int', 0, 'UInt', LR_SHARED, 'Ptr'), HDITEM, BitmapOffset
NumPut 'Int', Format | HDF_BITMAP | (Format & HDF_RIGHT ? 0 : HDF_BITMAP_ON_RIGHT), HDITEM, FormatOffset
return SendMessage(HDM_SETITEM, Column - 1, HDITEM, HDR)
}
- LV_SortArrow3.png (7.32 KiB) Viewed 2123 times