Jump to content

How can you CUSTOMDRAW ListView header by State? (CDIS_HOT, etc.)


Go to solution Solved by Nine,

Recommended Posts

Posted (edited)

Ok, I am quite busy at the moment, so I don't have time to clean up and optimize. 

#AutoIt3Wrapper_UseX64=y

; From Nine
#include <GuiConstants.au3>
#include <GuiHeader.au3>
#include <WinAPI.au3>
#include <Misc.au3>
#include <WinAPITheme.au3>
#include <GuiListView.au3>

DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext", "HWND", "DPI_AWARENESS_CONTEXT" - 2)

Global $mHeader[]

Example()

Func Example()
  Local $hGUI = GUICreate("Header ", 500, 300)
  GUISetBkColor(0x000000)

  Local $idListView = GUICtrlCreateListView("Items List|SubItems1|SubItems2", 10, 10, 480, 280, -1, BitOR($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT))
  Local $hListView = GUICtrlGetHandle($idListView)
  Local $hHeader = GUICtrlSendMsg($idListView, $LVM_GETHEADER, 0, 0)
  _WinAPI_SetWindowTheme($hListView, "DarkMode_Explorer")              ; PROBLEM: cause of random GUI crashes
  _WinAPI_SetWindowTheme($hHeader, "DarkMode_ItemsView", "Header")      ; No problem with dark mode header theme
  GUICtrlSetBkColor($idListView, 0x121212)
  GUICtrlSetColor($idListView, 0xFFFFFF)

  Local $iCount = _GUICtrlListView_GetColumnCount($hListView), $aHeader[$iCount]
  For $i = 0 To $iCount - 1
    $aHeader[$i] = _GUICtrlListView_GetColumn($hListView, $i)[5]
  Next
  $mHeader[$idListView] = $aHeader

  Local $hSubClass = DllCallbackRegister(WM_NOTIFY, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
  _WinAPI_SetWindowSubclass($hListView, DllCallbackGetPtr($hSubClass), _WinAPI_MakeLong($hSubClass, $idListView), $hHeader)
  _WinAPI_SetWindowSubclass($hHeader, DllCallbackGetPtr($hSubClass), 1000, $hHeader)

  For $i = 1 To 20
    GUICtrlCreateListViewItem("item" & $i & "|item" & $i & "|item" & $i, $idListView)
  Next

  _GUICtrlListView_SetColumnWidth($idListView, 2, $LVSCW_AUTOSIZE_USEHEADER)

  _WinAPI_DwmSetWindowAttributeEx($hGUI, 20, 1)

  ; Apply Acrylic material on newer Windows 11 builds
  If @OSBuild >= 22621 Then
    _WinAPI_DwmSetWindowAttributeEx($hGUI, 38, 3)
    _WinAPI_DwmExtendFrameIntoClientArea($hGUI, _WinAPI_CreateMargins(-1, -1, -1, -1))
  EndIf

  GUISetState()

  Do
  Until GUIGetMsg() = $GUI_EVENT_CLOSE

  _WinAPI_RemoveWindowSubclass($hHeader, DllCallbackGetPtr($hSubClass), 1000)
  _WinAPI_RemoveWindowSubclass($hListView, DllCallbackGetPtr($hSubClass), _WinAPI_MakeLong($hSubClass, $idListView))
  DllCallbackFree($hSubClass)
EndFunc   ;==>Example

Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
  Local Static $bHover, $tPoint
  Local $hBrush
  Switch $iMsg
    Case $WM_NOTIFY
      Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
      Switch $tNMHDR.Code
        Case $NM_CUSTOMDRAW
          If _WinAPI_GetClassName($tNMHDR.hWndFrom) = "sysheader32" Then
            Local $tCustDraw = DllStructCreate($tagNMLVCUSTOMDRAW, $lParam)
            Switch $tCustDraw.dwDrawStage
              Case $CDDS_PREPAINT
                Return $CDRF_NOTIFYITEMDRAW
              Case $CDDS_ITEMPREPAINT
                Local $tRect = DllStructCreate($tagRECT, DllStructGetPtr($tCustDraw, "Left"))
                _WinAPI_SetBkMode($tCustDraw.hDC, 1)
                _WinAPI_SetTextColor($tCustDraw.hDC, 0xFFFFFF)

                If $bHover And _WinAPI_PtInRect($tRect, $tPoint) Then
                  $hBrush = _WinAPI_CreateSolidBrush(_IsPressed($VK_LBUTTON) ? 0x404040 : 0x303030)
                Else
                  $hBrush = _WinAPI_CreateSolidBrush(0x202020)
                EndIf
                _WinAPI_InflateRect($tRect, -1, 0)
                _WinAPI_FillRect($tCustDraw.hDC, $tRect, $hBrush)
                _WinAPI_DeleteObject($hBrush)

                _WinAPI_InflateRect($tRect, -5, -2)
                _WinAPI_DrawText($tCustDraw.hDC, ($mHeader[_WinAPI_HiWord($iID)])[$tCustDraw.dwItemSpec], $tRect, BitOR($DT_LEFT, $DT_NOCLIP))

                Return $CDRF_SKIPDEFAULT
            EndSwitch
          EndIf
        ;Case $HDN_BEGINTRACKW
        ;  $bHover = False
        ;  _WinAPI_RemoveWindowSubclass($pData, DllCallbackGetPtr(_WinAPI_LoWord($iID)), 1000)
        ;Case $HDN_ENDTRACKW
        ;  _WinAPI_SetWindowSubclass($pData, DllCallbackGetPtr(_WinAPI_LoWord($iID)), 1000, $pData)
      EndSwitch

    Case $WM_MOUSEMOVE
      If $pData = $hWnd Then
        $bHover = True
        $tPoint = _WinAPI_GetMousePos(True, $hWnd)
        _WinAPI_InvalidateRect($hWnd, 0, False)
        _WinAPI_TrackMouseEvent($hWnd, $TME_LEAVE)
      EndIf
    Case $WM_MOUSELEAVE
      If $pData = $hWnd Then
        $bHover = False
        _WinAPI_InvalidateRect($hWnd, 0, False)
      EndIf

  EndSwitch
  Return __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>WM_NOTIFY

Func _WinAPI_DwmSetWindowAttributeEx($hWnd, $iAttribute, $iData)
  Switch $iAttribute
    Case 2, 3, 4, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, $DWMWA_USE_IMMERSIVE_DARK_MODE, 33, 34, 35, 36, 37, 38, 39, 40

    Case Else
      Return SetError(1, 0, 0)
  EndSwitch

  Local $aCall = DllCall('dwmapi.dll', 'long', 'DwmSetWindowAttribute', 'hwnd', $hWnd, 'dword', $iAttribute, _
      'dword*', $iData, 'dword', 4)
  If @error Then Return SetError(@error + 10, @extended, 0)
  If $aCall[0] Then Return SetError(10, $aCall[0], 0)

  Return 1
EndFunc   ;==>_WinAPI_DwmSetWindowAttributeEx

Func __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
  Return DllCall('comctl32.dll', 'lresult', 'DefSubclassProc', 'hwnd', $hWnd, 'uint', $iMsg, 'wparam', $wParam, 'lparam', $lParam)[0]
EndFunc

You can have with this approach as many LV in a single GUI as you want, you just need to register each one in the global map...

Edited by Nine
Posted
7 hours ago, Nine said:

You can have with this approach as many LV in a single GUI as you want, you just need to register each one in the global map...

This is brilliant! Thank you. :)

I just wanted to confirm a few things. Since we were able to get rid of cases for $HDN_BEGINTRACKW and $HDN_ENDTRACKW after the fixes:

Does that mean that we can switch anything that is HiWord/LoWord related?

;_WinAPI_SetWindowSubclass($hListView, DllCallbackGetPtr($hSubClass), _WinAPI_MakeLong($hSubClass, $idListView), $hHeader)
  _WinAPI_SetWindowSubclass($hListView, DllCallbackGetPtr($hSubClass), $idListView, $hHeader)

 

;_WinAPI_RemoveWindowSubclass($hListView, DllCallbackGetPtr($hSubClass), _WinAPI_MakeLong($hSubClass, $idListView))
  _WinAPI_RemoveWindowSubclass($hListView, DllCallbackGetPtr($hSubClass), $idListView)

 

;_WinAPI_DrawText($tCustDraw.hDC, ($mHeader[_WinAPI_HiWord($iID)])[$tCustDraw.dwItemSpec], $tRect, BitOR($DT_LEFT, $DT_NOCLIP))
    _WinAPI_DrawText($tCustDraw.hDC, ($mHeader[$iID])[$tCustDraw.dwItemSpec], $tRect, BitOR($DT_LEFT, $DT_NOCLIP))

Are those changes (above) safe changes to make?

 

Posted (edited)
On 4/28/2026 at 10:43 PM, jugador said:

Keep _GUICtrlHeader_GetItemText($tCustDraw.hWndFrom, $tCustDraw.dwItemSpec) as it is, omit $hHeader Subclass lines, and use the DefSubclassProc fix. 
I think this will solve the crash.

infinite recursion loop caused the script in this post to crash. 

Here is how to solve it.

#include <GuiConstants.au3>
#include <GuiHeader.au3>
#include <WinAPI.au3>
#include <Misc.au3>
#include <WinAPITheme.au3>
#include <GuiListView.au3>

AutoItSetOption("MustDeclareVars", 1)

Global $hUser32Dll = DllOpen("user32.dll")

Example()

Func Example()
    Local $hGUI = GUICreate("Header Subclass", 500, 300)
    GUISetBkColor(0x000000)

    Local $idListView = GUICtrlCreateListView("Items List|SubItems1|SubItems2", 10, 10, 480, 280, $LVS_REPORT, BitOR($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT, $LVS_EX_HEADERDRAGDROP))
    Local $hListView = GUICtrlGetHandle($idListView)
    Local $hHeader = _GUICtrlListView_GetHeader($hListView)

    _WinAPI_SetWindowTheme($hListView, "DarkMode_Explorer")
    _WinAPI_SetWindowTheme($hHeader, "DarkMode_ItemsView", "Header")
    GUICtrlSetBkColor($idListView, 0x121212)
    GUICtrlSetColor($idListView, 0xFFFFFF)

    GUICtrlCreateListViewItem("item1|item1|item1", $idListview)
    GUICtrlCreateListViewItem("item2|item2|item2", $idListview)
    GUICtrlCreateListViewItem("item3|item3|item3", $idListview)

     _GUICtrlListView_SetColumnWidth($idListView, 2, $LVSCW_AUTOSIZE_USEHEADER)

    Local $hSubClass = DllCallbackRegister(_Header_Subclass, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    _WinAPI_SetWindowSubclass($hHeader, DllCallbackGetPtr($hSubClass), 1000, $hHeader)

    GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
    GUISetState()

    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    _WinAPI_RemoveWindowSubclass($hHeader, DllCallbackGetPtr($hSubClass), 1000)
    DllCallbackFree($hSubClass)
    DllClose($hUser32Dll)
EndFunc

Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
    Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    Local $hWndFrom = HWnd($tNMHDR.hWndFrom)
    Local $iCode = $tNMHDR.Code

    If _WinAPI_GetClassName($hWndFrom) = "sysheader32" Then
        Switch $iCode
            Case $NM_CUSTOMDRAW
                Local $tCustDraw = DllStructCreate($tagNMLVCUSTOMDRAW, $lParam)

                Switch $tCustDraw.dwDrawStage
                    Case $CDDS_PREPAINT
                        Return $CDRF_NOTIFYITEMDRAW

                    Case $CDDS_ITEMPREPAINT
                        Local $hDC = $tCustDraw.hDC
                        Local $tRect = DllStructCreate($tagRECT, DllStructGetPtr($tCustDraw, "Left"))
                        Local $tPoint = _WinAPI_GetMousePos(True, $hWndFrom)

                        Local $iColor = 0x121212
                        If _WinAPI_PtInRect($tRect, $tPoint) Then
                            $iColor = _IsPressed(0x01, $hUser32Dll) ? 0x252525 : 0x383838
                        EndIf

                        Local $hBrush = _WinAPI_CreateSolidBrush($iColor)
                        _WinAPI_FillRect($hDC, $tRect, $hBrush)
                        _WinAPI_DeleteObject($hBrush)

                        _WinAPI_SetBkMode($hDC, 1)
                        _WinAPI_SetTextColor($hDC, 0xFFFFFF)

                        _WinAPI_InflateRect($tRect, -5, -2)
                        _WinAPI_DrawText($tCustDraw.hDC, _GUICtrlHeader_GetItemText($tCustDraw.hWndFrom, $tCustDraw.dwItemSpec), $tRect, BitOR($DT_LEFT, $DT_NOCLIP))
                        Return $CDRF_SKIPDEFAULT
                EndSwitch

        EndSwitch
    EndIf

    Return $GUI_RUNDEFMSG
EndFunc

Func _Header_Subclass($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    #forceref $iID, $pData
    Switch $iMsg
        Case $WM_MOUSEMOVE
             _WinAPI_TrackMouseEvent($hWnd, $TME_LEAVE)
            _WinAPI_InvalidateRect($hWnd)

        Case $WM_MOUSELEAVE
            _WinAPI_InvalidateRect($hWnd)
    EndSwitch

    Return __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc

Func __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
    Return DllCall('comctl32.dll', 'lresult', 'DefSubclassProc', 'hwnd', $hWnd, 'uint', $iMsg, 'wparam', $wParam, _
            'lparam', $lParam)[0]
EndFunc   ;==>_WinAPI_DefSubclassProc

 

Edited by jugador
Posted
3 hours ago, jugador said:

infinite recursion loop cause of crash:

I don't completely understand what the root of the infinite recursion loop is.

But from this example, the $CDDS_ITEMPREPAINT case is never entered and no coloring is done on the header and therefore is never reaching the _WinAPI_DrawText function that contains the _GUICtrlHeader_GetItemText which was causing crashes. I can only see the initial $CDDS_PREPAINT case being used here.

  • 3 weeks later...
Posted
On 4/26/2026 at 5:14 AM, jugador said:
Func __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
    Return DllCall('comctl32.dll', 'lresult', 'DefSubclassProc', 'hwnd', $hWnd, 'uint', $iMsg, 'wparam', $wParam, _
            'lparam', $lParam)[0]
EndFunc   ;==>_WinAPI_DefSubclassProc

I just wanted to follow up on this fixed version of the _WinAPI_DefSubclassProc standard UDF function. Anyone can feel free to share their opinion. :)

This version of the function fixed a crash in the subclassing of the busy ListView/Header subclass in this topic.

So my question is: Should I use this same fixed function in all of my other subclassing procedures in GUIDarkTheme UDF?

Posted
1 hour ago, WildByDesign said:

Should I use this same fixed function in all of my other subclassing procedures in GUIDarkTheme UDF?

At least for testing yes.

Use it we will be able to check if it relates to other gui stuff.

Signature beginning:
Please remember: "AutoIt"..... *  Wondering who uses AutoIt and what it can be used for ? * Forum Rules *
ADO.au3 UDF * POP3.au3 UDF * XML.au3 UDF * IE on Windows 11 * How to ask ChatGPT for AutoIt Codefor other useful stuff click the following button:

Spoiler

Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind. 

My contribution (my own projects): * Debenu Quick PDF Library - UDF * Debenu PDF Viewer SDK - UDF * Acrobat Reader - ActiveX Viewer * UDF for PDFCreator v1.x.x * XZip - UDF * AppCompatFlags UDF * CrowdinAPI UDF * _WinMergeCompare2Files() * _JavaExceptionAdd() * _IsBeta() * Writing DPI Awareness App - workaround * _AutoIt_RequiredVersion() * Chilkatsoft.au3 UDF * TeamViewer.au3 UDF * JavaManagement UDF * VIES over SOAP * WinSCP UDF * GHAPI UDF - modest begining - comunication with GitHub REST APIErrorLog.au3 UDF - A logging Library * Include Dependency Tree (Tool for analyzing script relations) * Show_Macro_Values.au3 *

 

My contribution to others projects or UDF based on  others projects: * _sql.au3 UDF  * POP3.au3 UDF *  RTF Printer - UDF * XML.au3 UDF * ADO.au3 UDF SMTP Mailer UDF * Dual Monitor resolution detection * * 2GUI on Dual Monitor System * _SciLexer.au3 UDF * SciTE - Lexer for console pane

Useful links: * Forum Rules * Forum etiquette *  Forum Information and FAQs * How to post code on the forum * AutoIt Online Documentation * AutoIt Online Beta Documentation * SciTE4AutoIt3 getting started * Convert text blocks to AutoIt code * Games made in Autoit * Programming related sites * Polish AutoIt Tutorial * DllCall Code Generator * 

Wiki: Expand your knowledge - AutoIt Wiki * Collection of User Defined Functions * How to use HelpFile * Good coding practices in AutoIt * 

OpenOffice/LibreOffice/XLS Related: WriterDemo.au3 * XLS/MDB from scratch with ADOX

IE Related:  * How to use IE.au3  UDF with  AutoIt v3.3.14.x * Why isn't Autoit able to click a Javascript Dialog? * Clicking javascript button with no ID * IE document >> save as MHT file * IETab Switcher (by LarsJ ) * HTML Entities * _IEquerySelectorAll() (by uncommon) * IE in TaskSchedulerIE Embedded Control Versioning (use IE9+ and HTML5 in a GUI) * PDF Related:How to get reference to PDF object embeded in IE * IE on Windows 11

I encourage you to read: * Global Vars * Best Coding Practices * Please explain code used in Help file for several File functions * OOP-like approach in AutoIt * UDF-Spec Questions *  EXAMPLE: How To Catch ConsoleWrite() output to a file or to CMD *

I also encourage you to check awesome @trancexx code:  * Create COM objects from modules without any demand on user to register anything. * Another COM object registering stuffOnHungApp handlerAvoid "AutoIt Error" message box in unknown errors  * HTML editor

winhttp.au3 related : * https://www.autoitscript.com/forum/topic/206771-winhttpau3-download-problem-youre-speaking-plain-http-to-an-ssl-enabled-server-port/

"Homo sum; humani nil a me alienum puto" - Publius Terentius Afer
"Program are meant to be read by humans and only incidentally for computers and execute" - Donald Knuth, "The Art of Computer Programming"
:naughty:  :ranting:, be  :) and       \\//_.

Anticipating Errors :  "Any program that accepts data from a user must include code to validate that data before sending it to the data store. You cannot rely on the data store, ...., or even your programming language to notify you of problems. You must check every byte entered by your users, making sure that data is the correct type for its field and that required fields are not empty."

Signature last update: 2023-04-24

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
×
×
  • Create New...