Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

LV_SortArrow - Apply sort arrows to your ListView


  • Please log in to reply
14 replies to this topic
Solar
  • Members
  • 345 posts
  • Last active: Jan 15 2012 08:11 PM
  • Joined: 03 May 2009
This function will apply sorting arrows to your ListView column headers. It uses the ListView's g-label "ColClick" event to call the function.

Posted Image

Update 2012-01-05 changes:
- Fixed column data not being retained properly. Thanks gwarble.
Update 2011-05-13 changes:
- Reduced function code by 3 lines.
- Very slightly improved performance.
Update 2011-03-24 changes:
- Added set direction support.
- Added return value of LVM_SETCOLUMN.
- Cleaned up code.
Update 2011-03-16 changes:
- Now supports AutoHotkey Basic, AutoHotkey_L (32-bit, 64-bit, ANSI/Unicode).
- Fixed 64-bit support and fmt style application. (thanks HotKeyIt)


AHK Support:
AutoHotkey Basic.
AutoHotkey_L All builds.

Usage:
You only need to include the function and insert 2 lines of code into your ListView's g-label:
If (A_GuiEvent = "ColClick")
	LV_SortArrow(hListView, A_EventInfo)
See full example below.

Function (download):
; LV_SortArrow by Solar. http://www.autohotkey.com/forum/viewtopic.php?t=69642
; h = ListView handle
; c = 1 based index of the column
; d = Optional direction to set the arrow. "asc" or "up". "desc" or "down".
LV_SortArrow(h, c, d="") {
	static ptr, ptrSize, lvColumn, LVM_GETCOLUMN, LVM_SETCOLUMN
	if (!ptr)
		ptr := A_PtrSize ? ("ptr", ptrSize := A_PtrSize) : ("uint", ptrSize := 4)
		,LVM_GETCOLUMN := A_IsUnicode ? (4191, LVM_SETCOLUMN := 4192) : (4121, LVM_SETCOLUMN := 4122)
		,VarSetCapacity(lvColumn, ptrSize + 4), NumPut(1, lvColumn, "uint")
	c -= 1, DllCall("SendMessage", ptr, h, "uint", LVM_GETCOLUMN, "uint", c, ptr, &lvColumn)
	if ((fmt := NumGet(lvColumn, 4, "int")) & 1024) {
		if (d && d = "asc" || d = "up")
			return
		NumPut(fmt & ~1024 | 512, lvColumn, 4, "int")
	} else if (fmt & 512) {
		if (d && d = "desc" || d = "down")
			return
		NumPut(fmt & ~512 | 1024, lvColumn, 4, "int")
	} else {
		Loop % DllCall("SendMessage", ptr, DllCall("SendMessage", ptr, h, "uint", 4127), "uint", 4608)
			if ((i := A_Index - 1) != c)
				DllCall("SendMessage", ptr, h, "uint", LVM_GETCOLUMN, "uint", i, ptr, &lvColumn)
				,NumPut(NumGet(lvColumn, 4, "int") & ~1536, lvColumn, 4, "int")
				,DllCall("SendMessage", ptr, h, "uint", LVM_SETCOLUMN, "uint", i, ptr, &lvColumn)
		NumPut(fmt | (d && d = "desc" || d = "down" ? 512 : 1024), lvColumn, 4, "int")
	}
	return DllCall("SendMessage", ptr, h, "uint", LVM_SETCOLUMN, "uint", c, ptr, &lvColumn)
}
Example (download):
Gui, Add, ListView, r10 hwndhListView gListViewLabel, Sort Me|Sort Me 2
LV_ModifyCol(2, "Integer Left")
Loop 10
	LV_Add("", SubStr("abcdefghij", A_Index, 1), A_Index)
Gui, Add, Button, Section gButton1, Col1 up/asc
Gui, Add, Button, gButton2, Col1 down/desc
Gui, Add, Button, ys gButton3, Col2 up/asc
Gui, Add, Button, gButton4, Col2 down/desc
Gui, Show,, ListView Sort Arrows
Return

; this label will launch when a user interacts with our ListView.
ListViewLabel:
	If (A_GuiEvent = "ColClick") ; A_GuiEvent tells us what kind of event triggered the label.
		LV_SortArrow(hListView, A_EventInfo) ; call the function if our column was clicked.
Return

Button1:
	LV_ModifyCol(1, "Sort"), LV_SortArrow(hListView, 1, "up")
return

Button2:
	LV_ModifyCol(1, "SortDesc"), LV_SortArrow(hListView, 1, "down")
return

Button3:
	LV_ModifyCol(2, "Sort"), LV_SortArrow(hListView, 2, "up")
return

Button4:
	LV_ModifyCol(2, "SortDesc"), LV_SortArrow(hListView, 2, "down")
return

GuiEscape:
GuiClose:
	ExitApp

; LV_SortArrow by Solar. http://www.autohotkey.com/forum/viewtopic.php?t=69642
; h = ListView handle
; c = 1 based index of the column
; d = Optional direction to set the arrow. "asc" or "up". "desc" or "down".
LV_SortArrow(h, c, d="") {
	static ptr, ptrSize, lvColumn, LVM_GETCOLUMN, LVM_SETCOLUMN
	if (!ptr)
		ptr := A_PtrSize ? ("ptr", ptrSize := A_PtrSize) : ("uint", ptrSize := 4)
		,LVM_GETCOLUMN := A_IsUnicode ? (4191, LVM_SETCOLUMN := 4192) : (4121, LVM_SETCOLUMN := 4122)
		,VarSetCapacity(lvColumn, ptrSize + 4), NumPut(1, lvColumn, "uint")
	c -= 1, DllCall("SendMessage", ptr, h, "uint", LVM_GETCOLUMN, "uint", c, ptr, &lvColumn)
	if ((fmt := NumGet(lvColumn, 4, "int")) & 1024) {
		if (d && d = "asc" || d = "up")
			return
		NumPut(fmt & ~1024 | 512, lvColumn, 4, "int")
	} else if (fmt & 512) {
		if (d && d = "desc" || d = "down")
			return
		NumPut(fmt & ~512 | 1024, lvColumn, 4, "int")
	} else {
		Loop % DllCall("SendMessage", ptr, DllCall("SendMessage", ptr, h, "uint", 4127), "uint", 4608)
			if ((i := A_Index - 1) != c)
				DllCall("SendMessage", ptr, h, "uint", LVM_GETCOLUMN, "uint", i, ptr, &lvColumn)
				,NumPut(NumGet(lvColumn, 4, "int") & ~1536, lvColumn, 4, "int")
				,DllCall("SendMessage", ptr, h, "uint", LVM_SETCOLUMN, "uint", i, ptr, &lvColumn)
		NumPut(fmt | (d && d = "desc" || d = "down" ? 512 : 1024), lvColumn, 4, "int")
	}
	return DllCall("SendMessage", ptr, h, "uint", LVM_SETCOLUMN, "uint", c, ptr, &lvColumn)
}
Would appreciate any comments or questions. If you need help, feel free to ask.

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
That is cool, thank you :)

I have taken a look at code and modified it to use Struct(), also corrected SendMessage DllCall to work correct on 64-bit (though not tested).
As I unberstand also other fmt members were not kept, now they are.
Function requires AHK_L++ and _Struct.
#Include <_Struct>
LV_SortArrow(h, i,d=""){
   static C:=new _Struct("UINT mask,int fmt,int cx,LPTSTR pszText,int cchTextMax,int iSubItem,int iImage,int iOrder,int cxMin,int cxDefault,int cxIdeal")
   static LVM_GETCOLUMN:=(A_IsUnicode?0x105f:0x1019),LVM_GETHEADER:=0x101f,HDM_GETITEMCOUNT:=0x1200,LVM_SETCOLUMN:=(A_IsUnicode?0x1060:0x101a)
   i -= 1 ; convert to 0 based index
   C.mask:=1,DllCall("SendMessage","UPTR",h,"uint",LVM_GETCOLUMN,"UPTR",i,"UPTR",C[""])
   If ((fmt:=C.fmt)&1024){
      If (d && d = "asc" || d = "up")
         Return
      C.fmt:=fmt&~1024|512,DllCall("SendMessage","UPTR",h,"uint",LVM_SETCOLUMN,"UPTR",i,"UPTR",C[""])
   } else if (fmt&512){   
      If (d && d = "desc" || d = "down")
         Return
      C.fmt:=fmt&~512|1024,DllCall("SendMessage","UPTR",h,"uint",LVM_SETCOLUMN,"UPTR",i,"UPTR",C[""])
   } else { ; no arrow set. check and remove arrow on other columns
      Loop % DllCall("SendMessage","UPTR",DllCall("SendMessage","UPTR",h,"uint",LVM_GETHEADER,"UPTR",0,"UPTR",0,"UPTR")
                     ,"uint",HDM_GETITEMCOUNT,"UPTR",0,"UPTR",0,"UPTR")
        If (A_Index - 1 != c) ; skip our new column that we already checked.
            DllCall("SendMessage","UPTR",h,"uint",LVM_GETCOLUMN,"UPTR",A_Index-1,"UPTR",C[""])
            ,C.fmt:=C.fmt & ~1536
            ,DllCall("SendMessage","UPTR",h,"uint",LVM_SETCOLUMN,"UPTR",A_Index-1,"UPTR",C[""])
      C.fmt:=C.fmt|(d && d = "desc" || d = "down" ? 512 : 1024),DllCall("SendMessage","UPTR",h,"uint",LVM_SETCOLUMN,"UPTR",i,"UPTR",C[""])
   }
}

Edit:
- 08.08.2011 changed to use _Struct class
- 28.05.2011 added asc/up desc/down

Solar
  • Members
  • 345 posts
  • Last active: Jan 15 2012 08:11 PM
  • Joined: 03 May 2009
Function updated. Now working on AutoHotkey Basic as well as AutoHotkey_L 32-bit and 64-bit (Unicode and ANSI).

corrected SendMessage DllCall to work correct on 64-bit (though not tested).
As I unberstand also other fmt members were not kept, now they are.

These issues have been fixed. See changes in OP.

Solar
  • Members
  • 345 posts
  • Last active: Jan 15 2012 08:11 PM
  • Joined: 03 May 2009
Updated again. Added set direction support.

An issue came up when sorting a column without clicking it. e.g.:
LV_ModifyCol(ColumnNumber, "Sort")
LV_ModifyCol(ColumnNumber, "SortDesc")
Problem: Arrow direction didn't change (because the column wasn't clicked). This is now solved by being able to set arrow direction:
LV_SortArrow(hListView, ColumnNumber, "asc")
LV_SortArrow(hListView, ColumnNumber, "desc")
Each time you sort the listview with LV_ModifyCol or other, call LV_SortArrow and define the arrow direction immediately after:
LV_ModifyCol(1, "SortDesc")
LV_SortArrow(hListView, 1, "desc")


tidbit
  • Administrators
  • 2709 posts
  • Hates playing Janitor
  • Last active: Jan 15 2016 11:37 PM
  • Joined: 09 Mar 2008
Used here :mrgreen::
Posted Image
<!-- m -->http://i.imgur.com/hPpfw.png<!-- m -->

rawr. be very afraid
*poke*
. Populate the AutoHotkey city. Pointless but somewhat fun. .


Solar
  • Members
  • 345 posts
  • Last active: Jan 15 2012 08:11 PM
  • Joined: 03 May 2009

Used here :mrgreen::
<!-- m -->http://i.imgur.com/hPpfw.png<!-- m -->

Cool. :)

I cleaned up the code slightly to save a few lines and very minimally improve performance. Was bored. :roll:

Deo
  • Members
  • 199 posts
  • Last active: Jan 31 2014 03:19 PM
  • Joined: 16 May 2010
so do you know guys, how to place sort arrow somewhere after text, not in the middle of header?
how i see from tidbit's screenshot, he must know it ;)

Deo
  • Members
  • 199 posts
  • Last active: Jan 31 2014 03:19 PM
  • Joined: 16 May 2010
ok, seems like position of arrow depends on windows theme.. lol
i wonder, why do everyone trying to make his code as harder to read as possible, lol

Solar
  • Members
  • 345 posts
  • Last active: Jan 15 2012 08:11 PM
  • Joined: 03 May 2009

i wonder, why do everyone trying to make his code as harder to read as possible, lol

It's not that I'm trying to make it hard to read, it just came out that way when I finished optimizing it. It could be 20 lines longer if I wanted it to be slower and bulky.

  • Guests
  • Last active:
  • Joined: --
no offence, man, but HotKeyIt's code below first post is shorter & more readible. I think few comments would help though.
Anyway, thank you for your work, i'll use it for alpha search tool

As an option - would be nice to have a bitmap variant of sort arrows, for example - take some existing arrow icons from standard windows dlls, convert them to bitmap and use in header. It should not be hard, i've seen some examples in google. It could be useful to have similar arrow icons with any theme across all windows versions. It just looks a bit crappy with windows 7 aero theme lol - arrow hangs in the middle of the header!

Solar
  • Members
  • 345 posts
  • Last active: Jan 15 2012 08:11 PM
  • Joined: 03 May 2009

no offence, man, but HotKeyIt's code below first post is shorter & more readible.

HotKeyIt's code is not updated to support setting up/down arrow manually (third parameter). His code also requires AHK_L and Struct(). Because of Struct(), his version looks cleaner, but as a result is not shorter (because the Struct library is over 350 lines) and is slower.

Mine supports AHK Basic as well as AHK_L. So the extra few lines are justified IMO. The only reason HotKeyIt converted it for Struct() is because I initially posted it without 64-bit support. His version really has no purpose now.

Finally, why do you care about what the code looks like? I consider it finalized so I don't expect anyone to modify it. It does exactly what it's supposed to do and to add anything else to it would take away from it's simple and standard function.

jballi
  • Members
  • 1029 posts
  • Last active:
  • Joined: 01 Oct 2005
Just discovered this. Looks pretty cool. Thanks. :)

gwarble
  • Members
  • 624 posts
  • Last active: Aug 12 2016 07:49 PM
  • Joined: 23 May 2009
nice work, thanks for this

quick question, seems to change a centered column into left justified... probably shouldn't, right?

ahk basic on xp...
- gwarble

Solar
  • Members
  • 345 posts
  • Last active: Jan 15 2012 08:11 PM
  • Joined: 03 May 2009

quick question, seems to change a centered column into left justified... probably shouldn't, right?

You're right. There was an error where the old column data is assigned. The original post is updated and fixed.

I can't believe this bug went unnoticed for almost a year. :roll:

Thank you! :)

gwarble
  • Members
  • 624 posts
  • Last active: Aug 12 2016 07:49 PM
  • Joined: 23 May 2009
Nice, thanks