Jump to content

Subclass Listview


Recommended Posts

Trying to sort my items in the list view and was wanting to use my window proc instead of GUIRegisterMsg. My current problem is GUIRegisterMsg with $WM_NOTIFY works and does sort the item but $WM_NOTIFY inside the Window Proc does not work, it's not getting the $LVN_COLUMNCLICK code.

Anyone know why the GUI message $WM_NOTIFY works but the subclassed $WM_NOTIFY does not? In other instances I've used $WM_NOTIFY on controls it has been the same.

#include <GUIConstants.au3>
#include <GuiListView.au3>
#include <WindowsConstants.au3>
#include <WinAPIShellEx.au3>

Global $hNewWindowProc = DllCallbackRegister("NewWindowProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
Global $pNewWindowProc = DllCallbackGetPtr($hNewWindowProc)
Global $hMain = GUICreate("listview items", 220, 250, 100, 200)

Global $idListview = GUICtrlCreateListView("col1|col2|col3  ", 10, 10, 200, 150)
Global $idButton = GUICtrlCreateButton("Value?", 75, 170, 70, 20)
Global $idItem2 = GUICtrlCreateListViewItem("2|2|2", $idListview)
Global $idItem1 = GUICtrlCreateListViewItem("1|1|1", $idListview)
Global $idItem4 = GUICtrlCreateListViewItem("4|4|4", $idListview)
Global $idItem3 = GUICtrlCreateListViewItem("3|3|3", $idListview)
Global $hListView = GUICtrlGetHandle($idListview)

GUISetState(@SW_SHOW)
_WinAPI_SetWindowSubclass($hListView, $pNewWindowProc, 9999)    ; This does not work
;~ GUIRegisterMsg($WM_NOTIFY, WM_NOTIFY)                        ; This works
_GUICtrlListView_RegisterSortCallBack($hListView)

; Loop until the user exits.
While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            ExitLoop

    EndSwitch
WEnd
_GUICtrlListView_UnRegisterSortCallBack($hListView)
DllCallbackFree($hNewWindowProc)

Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam
    Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    Local $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    Local $iCode = DllStructGetData($tNMHDR, "Code")

    Switch $hWndFrom
        Case $hListView
            Switch $iCode
                Case $LVN_COLUMNCLICK ; A column was clicked
                    Local $tInfo = DllStructCreate($tagNMLISTVIEW, $lParam)
                    _GUICtrlListView_SortItems($hWndFrom, DllStructGetData($tInfo, "SubItem"))
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY

Func NewWindowProc($hWnd, $iMsg, $wParam, $lParam, $uIdSubclass, $dwRefData)
    #forceref $hWnd, $iMsg, $wParam, $lParam, $uIdSubclass, $dwRefData
    Switch ($iMsg)
        Case $WM_NOTIFY
            Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
            Local $iCode = DllStructGetData($tNMHDR, "Code")

            Switch ($iCode)
                Case $LVN_COLUMNCLICK
                    Local $tInfo = DllStructCreate($tagNMLISTVIEW, $lParam)
                    _GUICtrlListView_SortItems($hWnd, DllStructGetData($tInfo, "SubItem"))
            EndSwitch
    EndSwitch
    Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>NewWindowProc

 

Link to comment
Share on other sites

Because WM_NOTIFY messages are sent to the parent window of the listview. In your case the parent window is the GUI, so you have to subclass the GUI.

Link to comment
Share on other sites

That makes sense. I did this

ConsoleWrite($hWndFrom & " | " & $hListView & " | " & $hWnd & " | " & _GUICtrlListView_GetHeader($hListView) & @CRLF)

In the NewWindowProc function and see that I'm getting the lParam for the header, not the listview.

After doing some debugging, gathering all of the possible listview codes into an array and seeing what I'm getting in the subclass function I can still use it without having to subclasss the gui. I may end up subclassing the gui later but for now I don't need it. I was already using

Local $tNMHeader = DllStructCreate($tagNMHEADER, $lParam)
Local $iCol = DllStructGetData($tNMHeader, "Item")

In my main program to keep track of the width of the headers. So I just used the click code to sort

Func NewWindowProc($hWnd, $iMsg, $wParam, $lParam, $uIdSubclass, $dwRefData)
    #forceref $hWnd, $iMsg, $wParam, $lParam, $uIdSubclass, $dwRefData
    Switch ($iMsg)
        Case $WM_NOTIFY
            Local $tNMHeader = DllStructCreate($tagNMHEADER, $lParam)
            Local $iCol = DllStructGetData($tNMHeader, "Item")
            Local $iCode = DllStructGetData($tNMHeader, "Code")
            ConsoleWrite($aListViewCodes[Abs($iCode)] & " | " & $iCode & @CRLF)

            Switch ($iCode)
                Case $HDN_ITEMCLICKW
                    _GUICtrlListView_SortItems($hWnd, $iCol)
            EndSwitch
    EndSwitch
    Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>NewWindowProc

This also might be useful for anyone else debugging a listview control. Let you see the variable of the code you're getting.

Spoiler
Global $aListViewCodes[$HDM_SETUNICODEFORMAT + 2]
$aListViewCodes[Abs($NM_CUSTOMDRAW)] = "NM_CUSTOMDRAW"
$aListViewCodes[Abs($HDN_BEGINDRAG)] = "HDN_BEGINDRAG"
$aListViewCodes[Abs($HDN_BEGINTRACK)] = "HDN_BEGINTRACK"
$aListViewCodes[Abs($HDN_DIVIDERDBLCLICK)] = "HDN_DIVIDERDBLCLICK"
$aListViewCodes[Abs($HDN_ENDDRAG)] = "HDN_ENDDRAG"
$aListViewCodes[Abs($HDN_ENDTRACK)] = "HDN_ENDTRACK"
$aListViewCodes[Abs($HDN_FILTERBTNCLICK)] = "HDN_FILTERBTNCLICK"
$aListViewCodes[Abs($HDN_FILTERCHANGE)] = "HDN_FILTERCHANGE"
$aListViewCodes[Abs($HDN_GETDISPINFO)] = "HDN_GETDISPINFO"
$aListViewCodes[Abs($HDN_ITEMCHANGED)] = "HDN_ITEMCHANGED"
$aListViewCodes[Abs($HDN_ITEMCHANGING)] = "HDN_ITEMCHANGING"
$aListViewCodes[Abs($HDN_ITEMCLICK)] = "HDN_ITEMCLICK"
$aListViewCodes[Abs($HDN_ITEMDBLCLICK)] = "HDN_ITEMDBLCLICK"
$aListViewCodes[Abs($HDN_TRACK)] = "HDN_TRACK"
$aListViewCodes[Abs($HDN_BEGINTRACKW)] = "HDN_BEGINTRACKW"
$aListViewCodes[Abs($HDN_DIVIDERDBLCLICKW)] = "HDN_DIVIDERDBLCLICKW"
$aListViewCodes[Abs($HDN_ENDTRACKW)] = "HDN_ENDTRACKW"
$aListViewCodes[Abs($HDN_GETDISPINFOW)] = "HDN_GETDISPINFOW"
$aListViewCodes[Abs($HDN_ITEMCHANGEDW)] = "HDN_ITEMCHANGEDW"
$aListViewCodes[Abs($HDN_ITEMCHANGINGW)] = "HDN_ITEMCHANGINGW"
$aListViewCodes[Abs($HDN_ITEMCLICKW)] = "HDN_ITEMCLICKW"
$aListViewCodes[Abs($HDN_ITEMDBLCLICKW)] = "HDN_ITEMDBLCLICKW"
$aListViewCodes[Abs($HDN_TRACKW)] = "HDN_TRACKW"
$aListViewCodes[Abs($LVN_FIRST)] = "LVN_FIRST"
$aListViewCodes[Abs($LVN_LAST)] = "LVN_LAST"
$aListViewCodes[Abs($LVN_BEGINDRAG)] = "LVN_BEGINDRAG"
$aListViewCodes[Abs($LVN_BEGINLABELEDITA)] = "LVN_BEGINLABELEDITA"
$aListViewCodes[Abs($LVN_BEGINLABELEDITW)] = "LVN_BEGINLABELEDITW"
$aListViewCodes[Abs($LVN_BEGINRDRAG)] = "LVN_BEGINRDRAG"
$aListViewCodes[Abs($LVN_BEGINSCROLL)] = "LVN_BEGINSCROLL"
$aListViewCodes[Abs($LVN_COLUMNCLICK)] = "LVN_COLUMNCLICK"
$aListViewCodes[Abs($LVN_COLUMNDROPDOWN)] = "LVN_COLUMNDROPDOWN"
$aListViewCodes[Abs($LVN_COLUMNOVERFLOWCLICK)] = "LVN_COLUMNOVERFLOWCLICK"
$aListViewCodes[Abs($LVN_DELETEALLITEMS)] = "LVN_DELETEALLITEMS"
$aListViewCodes[Abs($LVN_DELETEITEM)] = "LVN_DELETEITEM"
$aListViewCodes[Abs($LVN_ENDLABELEDITA)] = "LVN_ENDLABELEDITA"
$aListViewCodes[Abs($LVN_ENDLABELEDITW)] = "LVN_ENDLABELEDITW"
$aListViewCodes[Abs($LVN_ENDSCROLL)] = "LVN_ENDSCROLL"
$aListViewCodes[Abs($LVN_GETDISPINFOA)] = "LVN_GETDISPINFOA"
$aListViewCodes[Abs($LVN_GETDISPINFOW)] = "LVN_GETDISPINFOW"
$aListViewCodes[Abs($LVN_GETDISPINFO)] = "LVN_GETDISPINFO"
$aListViewCodes[Abs($LVN_GETEMPTYMARKUP)] = "LVN_GETEMPTYMARKUP"
$aListViewCodes[Abs($LVN_GETINFOTIPA)] = "LVN_GETINFOTIPA"
$aListViewCodes[Abs($LVN_GETINFOTIPW)] = "LVN_GETINFOTIPW"
$aListViewCodes[Abs($LVN_HOTTRACK)] = "LVN_HOTTRACK"
$aListViewCodes[Abs($LVN_INCREMENTALSEARCHA)] = "LVN_INCREMENTALSEARCHA"
$aListViewCodes[Abs($LVN_INCREMENTALSEARCHW)] = "LVN_INCREMENTALSEARCHW"
$aListViewCodes[Abs($LVN_INSERTITEM)] = "LVN_INSERTITEM"
$aListViewCodes[Abs($LVN_ITEMACTIVATE)] = "LVN_ITEMACTIVATE"
$aListViewCodes[Abs($LVN_ITEMCHANGED)] = "LVN_ITEMCHANGED"
$aListViewCodes[Abs($LVN_ITEMCHANGING)] = "LVN_ITEMCHANGING"
$aListViewCodes[Abs($LVN_KEYDOWN)] = "LVN_KEYDOWN"
$aListViewCodes[Abs($LVN_LINKCLICK)] = "LVN_LINKCLICK"
$aListViewCodes[Abs($LVN_MARQUEEBEGIN)] = "LVN_MARQUEEBEGIN"
$aListViewCodes[Abs($LVN_ODCACHEHINT)] = "LVN_ODCACHEHINT"
$aListViewCodes[Abs($LVN_ODFINDITEMA)] = "LVN_ODFINDITEMA"
$aListViewCodes[Abs($LVN_ODFINDITEMW)] = "LVN_ODFINDITEMW"
$aListViewCodes[Abs($LVN_ODFINDITEM)] = "LVN_ODFINDITEM"
$aListViewCodes[Abs($LVN_ODSTATECHANGED)] = "LVN_ODSTATECHANGED"
$aListViewCodes[Abs($LVN_SETDISPINFOA)] = "LVN_SETDISPINFOA"
$aListViewCodes[Abs($LVN_SETDISPINFOW)] = "LVN_SETDISPINFOW"
$aListViewCodes[Abs($NM_RELEASEDCAPTURE)] = "NM_RELEASEDCAPTURE"
$aListViewCodes[$HDM_FIRST] = "HDM_FIRST"
$aListViewCodes[$HDM_CLEARFILTER] = "HDM_CLEARFILTER"
$aListViewCodes[$HDM_CREATEDRAGIMAGE] = "HDM_CREATEDRAGIMAGE"
$aListViewCodes[$HDM_DELETEITEM] = "HDM_DELETEITEM"
$aListViewCodes[$HDM_EDITFILTER] = "HDM_EDITFILTER"
$aListViewCodes[$HDM_GETBITMAPMARGIN] = "HDM_GETBITMAPMARGIN"
$aListViewCodes[$HDM_GETFOCUSEDITEM] = "HDM_GETFOCUSEDITEM"
$aListViewCodes[$HDM_GETIMAGELIST] = "HDM_GETIMAGELIST"
$aListViewCodes[$HDM_GETITEMA] = "HDM_GETITEMA"
$aListViewCodes[$HDM_GETITEMW] = "HDM_GETITEMW"
$aListViewCodes[$HDM_GETITEMCOUNT] = "HDM_GETITEMCOUNT"
$aListViewCodes[$HDM_GETITEMDROPDOWNRECT] = "HDM_GETITEMDROPDOWNRECT"
$aListViewCodes[$HDM_GETITEMRECT] = "HDM_GETITEMRECT"
$aListViewCodes[$HDM_GETORDERARRAY] = "HDM_GETORDERARRAY"
$aListViewCodes[$HDM_GETOVERFLOWRECT] = "HDM_GETOVERFLOWRECT"
$aListViewCodes[$HDM_GETUNICODEFORMAT] = "HDM_GETUNICODEFORMAT"
$aListViewCodes[$HDM_HITTEST] = "HDM_HITTEST"
$aListViewCodes[$HDM_INSERTITEMA] = "HDM_INSERTITEMA"
$aListViewCodes[$HDM_INSERTITEMW] = "HDM_INSERTITEMW"
$aListViewCodes[$HDM_LAYOUT] = "HDM_LAYOUT"
$aListViewCodes[$HDM_ORDERTOINDEX] = "HDM_ORDERTOINDEX"
$aListViewCodes[$HDM_SETBITMAPMARGIN] = "HDM_SETBITMAPMARGIN"
$aListViewCodes[$HDM_SETFILTERCHANGETIMEOUT] = "HDM_SETFILTERCHANGETIMEOUT"
$aListViewCodes[$HDM_SETFOCUSEDITEM] = "HDM_SETFOCUSEDITEM"
$aListViewCodes[$HDM_SETHOTDIVIDER] = "HDM_SETHOTDIVIDER"
$aListViewCodes[$HDM_SETIMAGELIST] = "HDM_SETIMAGELIST"
$aListViewCodes[$HDM_SETITEMA] = "HDM_SETITEMA"
$aListViewCodes[$HDM_SETITEMW] = "HDM_SETITEMW"
$aListViewCodes[$HDM_SETORDERARRAY] = "HDM_SETORDERARRAY"
$aListViewCodes[$HDM_SETUNICODEFORMAT] = "HDM_SETUNICODEFORMAT"

 

 

Link to comment
Share on other sites

WM_NOTIFY messages from the header control are also sent to the parent window - which is the listview. To get WM_NOTIFY messages from the header control you have to subclass the listview as you have done.

If you also are going to subclass the header control don't forget this nasty little issue.

To avoid subclassing the GUI I ususally create a child window and create the listview in the child window. Then you can subclass the child window and avoids receiving messages from the entire GUI. It's quite simple and works flawlessly.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...