Jump to content

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


Go to solution Solved by Nine,

Recommended Posts

Posted (edited)

I'm playing around with using _WinAPI_FillRect and _WinAPI_DrawText on a CUSTOMDRAW ListView header. The reason why is because _WinAPI_FillRect is the only way that I can apply Windows 11 materials to the ListView header.

I am having success with both _WinAPI_FillRect and _WinAPI_DrawText in my testing script.

Problem: I am having zero success with getting a different header background colour when the cursor hovers over a header item.

In the following case I have the code below to help determine header item state:

Case $CDDS_ITEMPOSTPAINT
;...
    If BitAND($tInfo.ItemState, $CDIS_DEFAULT) Then     ; this is not working
        $hBrush = _WinAPI_CreateSolidBrush(0xff00ff)
    ElseIf BitAND($tInfo.ItemState, $CDIS_FOCUS) Then   ; this is not working
        $hBrush = _WinAPI_CreateSolidBrush(0xff0000)
    ElseIf BitAND($tInfo.ItemState, $CDIS_HOT) Then     ; this is not working
        $hBrush = _WinAPI_CreateSolidBrush(0x0000ff)
    Else
        $hBrush = _WinAPI_CreateSolidBrush(0x000000)    ; this is being used
    EndIf
    _WinAPI_FillRect($hDC, $tRECT, $hBrush)
 ;...

I had used this with CUSTOMDRAW buttons previously with success. However, it is not working at all with header item state.

If anyone has any ideas on how to achieve different header item background colour for different states, please let me know. Thank you. :)

My current example:

#include <GUIConstantsEx.au3>
#include <WindowsStylesConstants.au3>
#include <WinAPIShellEx.au3>
#include <WinAPISysWin.au3>
#include <WinAPITheme.au3>
#include <WinAPIGdi.au3>
#include <GuiHeader.au3>

; initiate System DPI awareness
DllCall("user32.dll", "bool", "SetProcessDpiAwarenessContext", @AutoItX64 ? "int64" : "int", -2)

Global $hListView, $hHeader
Global $g_hSubclassProc = 0, $g_pSubclassProc = 0

Global Const $tagNMCUSTOMDRAWINFO = $tagNMHDR & ";dword DrawStage;handle hdc;" & $tagRECT & ";dword_ptr ItemSpec;uint ItemState;lparam lItemParam;"

Example()

Func Example()
    Local $hGUI = GUICreate("ListView", 300, 170)
    GUISetBkColor(0x202020)

    Local $idListview = GUICtrlCreateListView("col1|col2|col3", 10, 10, 280, 150)
    $hListView = GUICtrlGetHandle($idListview)
    GUICtrlSetBkColor(-1, 0x202020)
    GUICtrlSetColor(-1, 0xFFFFFF)
    GUICtrlCreateListViewItem("item1|item1|item1", $idListview)
    GUICtrlCreateListViewItem("item2|item2|item2", $idListview)
    GUICtrlCreateListViewItem("item3|item3|item3", $idListview)

    $g_hSubclassProc = DllCallbackRegister("_SubclassProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    $g_pSubclassProc = DllCallbackGetPtr($g_hSubclassProc)
    _WinAPI_SetWindowSubclass($hListView, $g_pSubclassProc, 0)

    $hHeader = _WinAPI_FindWindowEx($hListView, 0, "SysHeader32", "")
    _WinAPI_DwmSetWindowAttribute($hGUI, $DWMWA_USE_IMMERSIVE_DARK_MODE, True)
    _WinAPI_SetWindowTheme($hHeader, 'DarkMode_ItemsView')
    GUISetState(@SW_SHOW)

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

    _WinAPI_RemoveWindowSubclass($hListView, $g_pSubclassProc, 0)
EndFunc   ;==>Example

Func _WinAPI_FindWindowEx($hParent, $hAfter, $sClass, $sTitle = "")
    Local $ret = DllCall('user32.dll', "hwnd", "FindWindowExW", "hwnd", $hParent, "hwnd", $hAfter, "wstr", $sClass, "wstr", $sTitle)
    If @error Or Not IsArray($ret) Then Return 0
    Return $ret[0]
EndFunc   ;==>_WinAPI_FindWindowEx

Func _SubclassProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    #forceref $iID, $pData
    Local $hDC, $sClass, $iRet, $tRect
    Switch $iMsg
        Case $WM_NOTIFY
            Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
            Local $hFrom = $tNMHDR.hWndFrom
            Local $iCode = $tNMHDR.Code
            Local $hFont = __CreateFont("Segoe UI", 9)
            If $iCode = $NM_CUSTOMDRAW Then
                Local $tNMCD = DllStructCreate($tagNMCUSTOMDRAWINFO, $lParam)
                Local $dwStage = $tNMCD.DrawStage
                $hDC = $tNMCD.hdc
                Switch _WinAPI_GetClassName($hFrom)
                    Case "sysheader32"
                        Switch $dwStage
                            Case $CDDS_PREPAINT
                                Return $CDRF_NOTIFYITEMDRAW
                            Case $CDDS_ITEMPREPAINT
                                ;_WinAPI_SetTextColor($hDC, 0xFFFFFF)
                                Return BitOR($CDRF_NEWFONT, $CDRF_NOTIFYPOSTPAINT)
                            Case $CDDS_ITEMPOSTPAINT
                                ; Set header section size
                                Local $hBrush
                                Local $tInfo = DllStructCreate($tagNMCUSTOMDRAWINFO, $lParam)
                                Local $tNMCustomDraw = DllStructCreate($tagNMLVCUSTOMDRAW, $lParam)
                                Local $tRECT = DllStructCreate($tagRECT)
                                DllStructSetData($tRECT, 1, DllStructGetData($tNMCustomDraw, 6) + 1)
                                DllStructSetData($tRECT, 2, DllStructGetData($tNMCustomDraw, 7) + 1)
                                DllStructSetData($tRECT, 3, DllStructGetData($tNMCustomDraw, 8) - 2)
                                DllStructSetData($tRECT, 4, DllStructGetData($tNMCustomDraw, 9) - 2)
                                _WinAPI_SetBkMode($hDC, $TRANSPARENT)
                                _WinAPI_SelectObject($hDC, $hFont)
                                _WinAPI_SetTextColor($hDC, 0xFFFFFF)
                                Local $iCol = $tInfo.ItemSpec
                                Local $sText = _GUICtrlHeader_GetItemText($hHeader, $iCol)
                                If BitAND($tInfo.ItemState, $CDIS_DEFAULT) Then     ; this is not working
                                    $hBrush = _WinAPI_CreateSolidBrush(0xff00ff)
                                ElseIf BitAND($tInfo.ItemState, $CDIS_FOCUS) Then   ; this is not working
                                    $hBrush = _WinAPI_CreateSolidBrush(0xff0000)
                                ElseIf BitAND($tInfo.ItemState, $CDIS_HOT) Then     ; this is not working
                                    $hBrush = _WinAPI_CreateSolidBrush(0x0000ff)
                                Else
                                    $hBrush = _WinAPI_CreateSolidBrush(0x000000)    ; this is being used
                                EndIf
                                _WinAPI_FillRect($hDC, $tRECT, $hBrush)
                                _WinAPI_DrawText($hDC, $sText, $tRECT, BitOR($DT_CENTER, $DT_VCENTER, $DT_NOCLIP))
                                _WinAPI_DeleteObject($hBrush)
                                _WinAPI_DeleteObject($hFont)
                                Return $CDRF_NEWFONT
                        EndSwitch
                EndSwitch
            EndIf
    EndSwitch
    Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>_SubclassProc

Func __CreateFont($sFontName, $nHeight = 9, $nWidth = 400)
    Local $stFontName = DllStructCreate("char[260]")
    DllStructSetData($stFontName, 1, $sFontName)
    Local $hDC = _WinAPI_GetDC(0)        ; Get the Desktops DC
    Local $nPixel = _WinAPI_GetDeviceCaps($hDC, 90)
    $nHeight = 0 - _WinAPI_MulDiv($nHeight, $nPixel, 72)
    _WinAPI_ReleaseDC(0, $hDC)
    Local $hFont = _WinAPI_CreateFont($nHeight, 0, 0, 0, $nWidth, False, False, False, _
            $DEFAULT_CHARSET, $OUT_DEFAULT_PRECIS, $CLIP_DEFAULT_PRECIS, $PROOF_QUALITY, $DEFAULT_PITCH, $sFontName)
    Return $hFont
EndFunc   ;==>__CreateFont

 

Edited by WildByDesign
Posted
10 hours ago, WildByDesign said:

_WinAPI_FillRect is the only way that I can apply Windows 11 materials to the ListView header.

Do you mind expanding a bit on that, or popping up a snippet? I'm just trying to understand where you're coming from. :)

Otherwise we can simplify things quite a bit...

#include <GUIConstants.au3>
#include <WindowsConstants.au3>
#include <WinAPI.au3>
#include <WinAPITheme.au3>

Global Const $tagNMCUSTOMDRAWINFO = "struct;" & $tagNMHDR & ";dword DrawStage;handle hdc;" & _
        $tagRECT & ";dword_ptr ItemSpec;uint ItemState;lparam lItemParam;endstruct"

Example()

Func Example()
    DllCall("user32.dll", "bool", "SetProcessDpiAwarenessContext", @AutoItX64 ? "int64" : "int", -2)

    Local $hGUI = GUICreate("ListView", 300, 170)
    GUISetBkColor(0x202020)

    Local $idListview = GUICtrlCreateListView("col1|col2|col3", 10, 10, 280, 150)
    Local $hListView = GUICtrlGetHandle($idListview)
    GUICtrlSetBkColor(-1, 0x404040)
    GUICtrlSetColor(-1, 0xFFFFFF)

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

    Local $hSubclassProc = DllCallbackRegister("_SubclassProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    Local $pSubclassProc = DllCallbackGetPtr($hSubclassProc)
    _WinAPI_SetWindowSubclass($hListView, $pSubclassProc, 0)

    Local $hHeader = GUICtrlSendMsg($idListview, $LVM_GETHEADER, 0, 0)
    _WinAPI_DwmSetWindowAttribute($hGUI, $DWMWA_USE_IMMERSIVE_DARK_MODE, True)
    _WinAPI_SetWindowTheme($hHeader, 'DarkMode_ItemsView')
    GUISetState(@SW_SHOW)

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

    _WinAPI_RemoveWindowSubclass($hListView, $pSubclassProc, 0)
    GUIDelete($hGUI)
    DllCallbackFree($hSubclassProc)

EndFunc   ;==>Example

Func _SubclassProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Switch $iMsg
        Case $WM_NOTIFY
            Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
            If $tNMHDR.Code = $NM_CUSTOMDRAW Then
                Local $tNMCD = DllStructCreate($tagNMCUSTOMDRAWINFO, $lParam)

                Switch $tNMCD.DrawStage
                    Case $CDDS_PREPAINT
                        Return $CDRF_NOTIFYITEMDRAW

                    Case $CDDS_ITEMPREPAINT
                        _WinAPI_SetTextColor($tNMCD.hdc, 0xFFFFFF)
                        Return $CDRF_DODEFAULT

                EndSwitch
            EndIf
    EndSwitch
    Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>_SubclassProc
Posted
1 hour ago, MattyD said:

Do you mind expanding a bit on that, or popping up a snippet? I'm just trying to understand where you're coming from. :)

Absolutely. I've updated my testing script and yours to include applying Acrylic to the entire GUI so that you can see what I mean. It would require Windows 11 (Build 22621+) and only works if Transparency Effects are enabled for the system. Settings app > Personalization > Colors > Transparency Effects

I have seen many header subclassing examples that use _WinAPI_SetBkColor but it never works to change the header background color. I'm guessing it must have worked at some point in time with older Windows OS versions, but does not work now. Also, I'm pretty certain that _WinAPI_SetBkColor wont work to accept Windows 11 materials. It generally must be done with FillRect which I had done before with statusbar and so on.

It basically works on the alpha channel of the color, so anything close to black works best. For example, anything between 0x000000 and 0x202020 shows the materials the best.

My example with FillRect on the header items:

#include <GUIConstantsEx.au3>
#include <WindowsStylesConstants.au3>
#include <WinAPIShellEx.au3>
#include <WinAPISysWin.au3>
#include <WinAPITheme.au3>
#include <WinAPIGdi.au3>
#include <GuiHeader.au3>

; initiate System DPI awareness
DllCall("user32.dll", "bool", "SetProcessDpiAwarenessContext", @AutoItX64 ? "int64" : "int", -2)

Global $hListView, $hHeader
Global $g_hSubclassProc = 0, $g_pSubclassProc = 0

Global Const $DWMSBT_AUTO = 0               ; Default (Auto)
Global Const $DWMSBT_NONE = 1               ; None
Global Const $DWMSBT_MAINWINDOW = 2         ; Mica
Global Const $DWMSBT_TRANSIENTWINDOW = 3    ; Acrylic
Global Const $DWMSBT_TABBEDWINDOW = 4       ; Mica Alt (Tabbed)

Enum $APPMODE_DEFAULT = 0, $APPMODE_ALLOWDARK, $APPMODE_FORCEDARK, $APPMODE_FORCELIGHT, $APPMODE_MAX

Global Const $tagNMCUSTOMDRAWINFO = $tagNMHDR & ";dword DrawStage;handle hdc;" & $tagRECT & ";dword_ptr ItemSpec;uint ItemState;lparam lItemParam;"

Example()

Func Example()
    Local $hGUI = GUICreate("ListView", 300, 170)
    GUISetBkColor(0x000000)

    _WinAPI_SetPreferredAppMode($APPMODE_FORCEDARK)

    Local $idListview = GUICtrlCreateListView("col1|col2|col3", 10, 10, 280, 150)
    $hListView = GUICtrlGetHandle($idListview)
    GUICtrlSetBkColor(-1, 0x202020)
    GUICtrlSetColor(-1, 0xFFFFFF)
    GUICtrlCreateListViewItem("item1|item1|item1", $idListview)
    GUICtrlCreateListViewItem("item2|item2|item2", $idListview)
    GUICtrlCreateListViewItem("item3|item3|item3", $idListview)

    $g_hSubclassProc = DllCallbackRegister("_SubclassProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    $g_pSubclassProc = DllCallbackGetPtr($g_hSubclassProc)
    _WinAPI_SetWindowSubclass($hListView, $g_pSubclassProc, 0)

    $hHeader = _WinAPI_FindWindowEx($hListView, 0, "SysHeader32", "")
    _WinAPI_SetWindowTheme($hHeader, 'DarkMode_ItemsView')

    ; Apply Acrylic material (yuck, but for testing purposes!)
    _WinAPI_DwmSetWindowAttributeEx($hGUI, $DWMWA_USE_IMMERSIVE_DARK_MODE, True)
    _WinAPI_DwmSetWindowAttributeEx($hGUI, $DWMWA_SYSTEMBACKDROP_TYPE, $DWMSBT_TRANSIENTWINDOW)
    _WinAPI_DwmExtendFrameIntoClientArea($hGUI, _WinAPI_CreateMargins(-1, -1, -1, -1))

    GUISetState(@SW_SHOW)

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

    _WinAPI_RemoveWindowSubclass($hListView, $g_pSubclassProc, 0)
EndFunc   ;==>Example

Func _WinAPI_FindWindowEx($hParent, $hAfter, $sClass, $sTitle = "")
    Local $ret = DllCall('user32.dll', "hwnd", "FindWindowExW", "hwnd", $hParent, "hwnd", $hAfter, "wstr", $sClass, "wstr", $sTitle)
    If @error Or Not IsArray($ret) Then Return 0
    Return $ret[0]
EndFunc   ;==>_WinAPI_FindWindowEx

Func _SubclassProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    #forceref $iID, $pData
    Local $hDC, $sClass, $iRet, $tRect
    Switch $iMsg
        Case $WM_NOTIFY
            Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
            Local $hFrom = $tNMHDR.hWndFrom
            Local $iCode = $tNMHDR.Code
            Local $hFont = __CreateFont("Segoe UI", 9)
            If $iCode = $NM_CUSTOMDRAW Then
                Local $tNMCD = DllStructCreate($tagNMCUSTOMDRAWINFO, $lParam)
                Local $dwStage = $tNMCD.DrawStage
                $hDC = $tNMCD.hdc
                Switch _WinAPI_GetClassName($hFrom)
                    Case "sysheader32"
                        Switch $dwStage
                            Case $CDDS_PREPAINT
                                Return $CDRF_NOTIFYITEMDRAW
                            Case $CDDS_ITEMPREPAINT
                                ;_WinAPI_SetTextColor($hDC, 0xFFFFFF)
                                Return BitOR($CDRF_NEWFONT, $CDRF_NOTIFYPOSTPAINT)
                            Case $CDDS_ITEMPOSTPAINT
                                ; Set header section size
                                Local $hBrush
                                Local $tInfo = DllStructCreate($tagNMCUSTOMDRAWINFO, $lParam)
                                Local $tNMCustomDraw = DllStructCreate($tagNMLVCUSTOMDRAW, $lParam)
                                Local $tRECT = DllStructCreate($tagRECT)
                                DllStructSetData($tRECT, 1, DllStructGetData($tNMCustomDraw, 6))
                                DllStructSetData($tRECT, 2, DllStructGetData($tNMCustomDraw, 7))
                                DllStructSetData($tRECT, 3, DllStructGetData($tNMCustomDraw, 8) - 1)
                                DllStructSetData($tRECT, 4, DllStructGetData($tNMCustomDraw, 9))
                                _WinAPI_SetBkMode($hDC, $TRANSPARENT)
                                _WinAPI_SelectObject($hDC, $hFont)
                                _WinAPI_SetTextColor($hDC, 0xFFFFFF)
                                Local $iCol = $tInfo.ItemSpec
                                Local $sText = _GUICtrlHeader_GetItemText($hHeader, $iCol)
                                If BitAND($tInfo.ItemState, $CDIS_DEFAULT) Then     ; this is not working
                                    $hBrush = _WinAPI_CreateSolidBrush(0xff00ff)
                                ElseIf BitAND($tInfo.ItemState, $CDIS_FOCUS) Then   ; this is not working
                                    $hBrush = _WinAPI_CreateSolidBrush(0xff0000)
                                ElseIf BitAND($tInfo.ItemState, $CDIS_HOT) Then     ; this is not working
                                    $hBrush = _WinAPI_CreateSolidBrush(0x0000ff)
                                Else
                                    $hBrush = _WinAPI_CreateSolidBrush(0x000000)    ; this is being used
                                EndIf
                                _WinAPI_FillRect($hDC, $tRECT, $hBrush)
                                Local $tRECT = DllStructCreate($tagRECT)
                                DllStructSetData($tRECT, 1, DllStructGetData($tNMCustomDraw, 6))
                                DllStructSetData($tRECT, 2, DllStructGetData($tNMCustomDraw, 7) + 2)
                                DllStructSetData($tRECT, 3, DllStructGetData($tNMCustomDraw, 8) - 1)
                                DllStructSetData($tRECT, 4, DllStructGetData($tNMCustomDraw, 9))
                                _WinAPI_DrawText($hDC, $sText, $tRECT, BitOR($DT_CENTER, $DT_VCENTER, $DT_NOCLIP))
                                _WinAPI_DeleteObject($hBrush)
                                _WinAPI_DeleteObject($hFont)
                                Return $CDRF_NEWFONT
                        EndSwitch
                EndSwitch
            EndIf
    EndSwitch
    Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>_SubclassProc

Func __CreateFont($sFontName, $nHeight = 9, $nWidth = 400)
    Local $stFontName = DllStructCreate("char[260]")
    DllStructSetData($stFontName, 1, $sFontName)
    Local $hDC = _WinAPI_GetDC(0)        ; Get the Desktops DC
    Local $nPixel = _WinAPI_GetDeviceCaps($hDC, 90)
    $nHeight = 0 - _WinAPI_MulDiv($nHeight, $nPixel, 72)
    _WinAPI_ReleaseDC(0, $hDC)
    Local $hFont = _WinAPI_CreateFont($nHeight, 0, 0, 0, $nWidth, False, False, False, _
            $DEFAULT_CHARSET, $OUT_DEFAULT_PRECIS, $CLIP_DEFAULT_PRECIS, $PROOF_QUALITY, $DEFAULT_PITCH, $sFontName)
    Return $hFont
EndFunc   ;==>__CreateFont

Func _WinAPI_SetPreferredAppMode($iPreferredAppMode) ;Windows 10 Build 18362+
    Local $aResult = DllCall("UxTheme.dll", "long", 135, "long", $iPreferredAppMode)
    If @error Then Return SetError(1, 0, False)
    Return $aResult[0]
EndFunc   ;==>_WinAPI_SetPreferredAppMode

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

Your example without FillRect on header items:

#include <GUIConstants.au3>
#include <WindowsConstants.au3>
#include <WinAPI.au3>
#include <WinAPITheme.au3>

Global Const $DWMSBT_AUTO = 0               ; Default (Auto)
Global Const $DWMSBT_NONE = 1               ; None
Global Const $DWMSBT_MAINWINDOW = 2         ; Mica
Global Const $DWMSBT_TRANSIENTWINDOW = 3    ; Acrylic
Global Const $DWMSBT_TABBEDWINDOW = 4       ; Mica Alt (Tabbed)

Global Const $tagNMCUSTOMDRAWINFO = "struct;" & $tagNMHDR & ";dword DrawStage;handle hdc;" & _
        $tagRECT & ";dword_ptr ItemSpec;uint ItemState;lparam lItemParam;endstruct"

Example()

Func Example()
    DllCall("user32.dll", "bool", "SetProcessDpiAwarenessContext", @AutoItX64 ? "int64" : "int", -2)

    Local $hGUI = GUICreate("ListView", 300, 170)
    GUISetBkColor(0x202020)

    Local $idListview = GUICtrlCreateListView("col1|col2|col3", 10, 10, 280, 150)
    Local $hListView = GUICtrlGetHandle($idListview)
    GUICtrlSetBkColor(-1, 0x404040)
    GUICtrlSetColor(-1, 0xFFFFFF)

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

    Local $hSubclassProc = DllCallbackRegister("_SubclassProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    Local $pSubclassProc = DllCallbackGetPtr($hSubclassProc)
    _WinAPI_SetWindowSubclass($hListView, $pSubclassProc, 0)

    Local $hHeader = GUICtrlSendMsg($idListview, $LVM_GETHEADER, 0, 0)
    _WinAPI_SetWindowTheme($hHeader, 'DarkMode_ItemsView')

    ; Apply Acrylic material (yuck, but for testing purposes!)
    _WinAPI_DwmSetWindowAttributeEx($hGUI, $DWMWA_USE_IMMERSIVE_DARK_MODE, True)
    _WinAPI_DwmSetWindowAttributeEx($hGUI, $DWMWA_SYSTEMBACKDROP_TYPE, $DWMSBT_TRANSIENTWINDOW)
    _WinAPI_DwmExtendFrameIntoClientArea($hGUI, _WinAPI_CreateMargins(-1, -1, -1, -1))

    GUISetState(@SW_SHOW)

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

    _WinAPI_RemoveWindowSubclass($hListView, $pSubclassProc, 0)
    GUIDelete($hGUI)
    DllCallbackFree($hSubclassProc)

EndFunc   ;==>Example

Func _SubclassProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Switch $iMsg
        Case $WM_NOTIFY
            Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
            If $tNMHDR.Code = $NM_CUSTOMDRAW Then
                Local $tNMCD = DllStructCreate($tagNMCUSTOMDRAWINFO, $lParam)

                Switch $tNMCD.DrawStage
                    Case $CDDS_PREPAINT
                        Return $CDRF_NOTIFYITEMDRAW

                    Case $CDDS_ITEMPREPAINT
                        _WinAPI_SetTextColor($tNMCD.hdc, 0xFFFFFF)
                        Return $CDRF_DODEFAULT

                EndSwitch
            EndIf
    EndSwitch
    Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>_SubclassProc

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

 

Posted

By the way, I also tried sending a message ($HDM_GETFOCUSEDITEM) to the header in hopes to get whatever header item is under the cursor, but no luck there. I was hoping to try that to determine which brush colour to use. I am not incredibly familiar with _SendMessage though, so I may have completely botched it anyway.

Posted

I just realized that I had asked this question less than 6 months ago. 🤣

It was in the Is a ListView Header subclass with FillRect, DrawText and SetBkMode possible? thread and had some success. The end result was achieving a different brush colour on click, but not on hover unfortunately.

The main difference there was doing the RectFill in $CDDS_ITEMPREPAINT, while in this current thread I'm using $CDDS_ITEMPOSTPAINT. Regardless, I am still open to any kind of improvements. Particularly if we can ever find a way to achieve a different brush colour on hover.

Here is the end result from the other thread to give us another perspective:

#include <APIConstants.au3>
#include <ListViewConstants.au3>
#include <WinAPIConstants.au3>
#include <WinAPIGdi.au3>
#include <WinAPIShellEx.au3>
#include <WinAPISys.au3>
#include <WinAPITheme.au3>
#include <GuiHeader.au3>
#include <GuiListView.au3>

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

Enum $APPMODE_FORCEDARK

; Dark Mode Colors (RGB)
Global Const $COLOR_BG_DARK = 0x000000
Global Const $COLOR_TEXT_LIGHT = 0xFFFFFF
Global Const $COLOR_EDIT_BG = 0x1E1E1E

; Global variables for subclassing (MUST be declared before _Example()!)
Global $g_hGUI = 0, $g_ListView
Global $g_aControls[50][3] = [[0, 0, 0]] ; [ControlID, hWnd, OldWndProc]
Global $g_iControlCount = 0
Global $g_pSubclassProc = 0

; Structure for NM_CUSTOMDRAW notification
Global Const $tagNMCUSTOMDRAW = _
            $tagNMHDR & ";" & _                                ; Contains NM_CUSTOMDRAW / NMHDR header among other things
            "dword dwDrawStage;" & _                           ; Current drawing stage (CDDS_*)
            "handle hdc;" & _                                  ; Device Context Handle
            "long left;long top;long right;long bottom;" & _   ; Drawing rectangle
            "dword_ptr dwItemSpec;" & _                        ; Item index or other info (depending on the control)
            "uint uItemState;" & _                             ; State Flags (CDIS_SELECTED, CDIS_FOCUS etc.)
            "lparam lItemlParam"                               ; lParam set by the item (e.g., via LVITEM.lParam)

_Example()

Func _Example()

    #Region GUI
    $g_hGUI = GUICreate("ListView with Materials", 400, 400)
    GUISetBkColor($COLOR_BG_DARK, $g_hGUI)
    #EndRegion GUI

    #Region LIST VIEW
    Local $idListView = GUICtrlCreateListView("Sample|ListView", 10, 10, 380, 380, -1, BitOR($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT, $WS_EX_CLIENTEDGE))
    _AddControlForSubclass($idListView)
    GUICtrlSetBkColor($idListView, $COLOR_EDIT_BG)
    GUICtrlSetColor($idListView, $COLOR_TEXT_LIGHT)
    GUICtrlSetTip(-1, '#Region LIST VIEW')
    GUICtrlCreateListViewItem("A|One", $idListView)
    GUICtrlCreateListViewItem("B|Two", $idListView)
    GUICtrlCreateListViewItem("C|Three", $idListView)
    $g_ListView = GUICtrlGetHandle($idListView)
    _GUICtrlListView_SetColumnWidth($idListView, 1, $LVSCW_AUTOSIZE_USEHEADER)
    #EndRegion LIST VIEW


    ; Apply Dark Mode
    _ApplyDarkModeToAllControls()

    If @OSBuild >= 22621 Then
        _WinAPI_DwmSetWindowAttribute_unr($g_hGUI, 38, 3)
        _WinAPI_DwmExtendFrameIntoClientArea($g_hGUI, _WinAPI_CreateMargins(-1, -1, -1, -1))
    EndIf

    GUISetState(@SW_SHOW)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                _CleanupSubclassing()
                ExitLoop
        EndSwitch
    WEnd

    GUIDelete()
EndFunc   ;==>_Example

Func _ColorToCOLORREF($iColor) ;RGB to BGR
    Local $iR = BitAND(BitShift($iColor, 16), 0xFF)
    Local $iG = BitAND(BitShift($iColor, 8), 0xFF)
    Local $iB = BitAND($iColor, 0xFF)
    Return BitOR(BitShift($iB, -16), BitShift($iG, -8), $iR)
EndFunc   ;==>_ColorToCOLORREF

Func _AddControlForSubclass($iCtrlID)
    Local $hCtrl = GUICtrlGetHandle($iCtrlID)
    If $hCtrl Then
        $g_aControls[$g_iControlCount][0] = $iCtrlID
        $g_aControls[$g_iControlCount][1] = $hCtrl
        $g_aControls[$g_iControlCount][2] = 0 ; Placeholder for OldWndProc
        $g_iControlCount += 1
    EndIf
EndFunc   ;==>_AddControlForSubclass

Func _ApplyDarkModeToAllControls()
    ; DWM Dark Mode for the main window
    _WinAPI_SetPreferredAppMode($APPMODE_FORCEDARK)

    ; Create subclass callback
    If Not $g_pSubclassProc Then $g_pSubclassProc = DllCallbackRegister("_SubclassProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")

    ; Subclass all controls
    Local $hCtrl, $sClass, $hEdit, $hComboLBox, $hHeader, $hUpDown
    For $i = 0 To $g_iControlCount - 1
        $hCtrl = $g_aControls[$i][1]
        If $hCtrl Then
            $sClass = _WinAPI_GetClassName($hCtrl)
            ; Use SetWindowSubclass
            _WinAPI_SetWindowSubclass($hCtrl, DllCallbackGetPtr($g_pSubclassProc), $i, 0)

            ; Special themes for different control types
            Switch StringLower($sClass)
                Case "syslistview32"
                    _WinAPI_SetWindowTheme($hCtrl, "DarkMode_Explorer", 0)
                    ; Also make the ListView header dark
                    $hHeader = _SendMessage($hCtrl, $LVM_GETHEADER, 0, 0)
                    If $hHeader Then
                        _WinAPI_SetWindowTheme($hHeader, "DarkMode_ItemsView", 0)
                    EndIf
                Case Else
                    _WinAPI_SetWindowTheme($hCtrl, "DarkMode_Explorer", 0)
            EndSwitch

            _WinAPI_AllowDarkModeForWindow($hCtrl, True)
        EndIf
    Next

    ; Update theme system
    _WinAPI_RefreshImmersiveColorPolicyState()
    _WinAPI_FlushMenuThemes()
    _WinAPI_DwmSetWindowAttribute($g_hGUI, $DWMWA_USE_IMMERSIVE_DARK_MODE, True)

    ; Redraw GUI
    _WinAPI_RedrawWindow($g_hGUI, 0, 0, $RDW_UPDATENOW)
EndFunc   ;==>_ApplyDarkModeToAllControls

Func _CleanupSubclassing()
    ; Remove all subclasses
    If $g_pSubclassProc Then
        Local $hCtrl
        For $i = 0 To $g_iControlCount - 1
            $hCtrl = $g_aControls[$i][1]
            If $hCtrl Then
                _WinAPI_RemoveWindowSubclass($hCtrl, DllCallbackGetPtr($g_pSubclassProc), $i)
            EndIf
        Next
        DllCallbackFree($g_pSubclassProc)
        $g_pSubclassProc = 0
    EndIf
EndFunc   ;==>_CleanupSubclassing

Func _SubclassProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Switch $iMsg
        Case $WM_NOTIFY
            Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
            Local $hFrom = $tNMHDR.hWndFrom
            Local $iCode = $tNMHDR.Code

            ; --- Adjust ListView Header text color ---
            If $iCode = $NM_CUSTOMDRAW Then
                Local $tNMCUSTOMDRAW = DllStructCreate($tagNMCUSTOMDRAW, $lParam)
                Local $dwDrawStage = $tNMCUSTOMDRAW.dwDrawStage
                Local $hDC = $tNMCUSTOMDRAW.hdc

                Switch $dwDrawStage
                    Case $CDDS_PREPAINT
                        Return $CDRF_NOTIFYITEMDRAW
                    Case $CDDS_ITEMPREPAINT
                        $hDC = $tNMCUSTOMDRAW.hDC
                        $iItem = $tNMCUSTOMDRAW.dwItemSpec
                        $tRect = DllStructCreate($tagRECT, DllStructGetPtr($tNMCUSTOMDRAW, "Left"))
                        _WinAPI_SetBkMode($hDC, 1)
                        _WinAPI_SetTextColor($hDC, 0xFFFFFF)

                        If BitAND($tNMCUSTOMDRAW.uItemState, $CDIS_SELECTED) Then
                            $hBrush = _WinAPI_CreateSolidBrush(0x202020)
                        ElseIf BitAND($tNMCUSTOMDRAW.uItemState, $CDIS_HOT) Then
                            $hBrush = _WinAPI_CreateSolidBrush(0xff0000)
                        ElseIf BitAND($tNMCUSTOMDRAW.uItemState, $CDIS_FOCUS) Then
                            $hBrush = _WinAPI_CreateSolidBrush(0x0000ff)
                        Else
                            $hBrush = _WinAPI_CreateSolidBrush(0x000000)
                        EndIf
                        _WinAPI_FillRect($hDC, $tRect, $hBrush)
                        _WinAPI_DeleteObject($hBrush)
                        _WinAPI_InflateRect($tRect, -5, -2)

                        _WinAPI_DrawText($hDC, _GUICtrlHeader_GetItemText($tNMCUSTOMDRAW.hWndFrom, $iItem), $tRect, $DT_LEFT)
                        Return $CDRF_SKIPDEFAULT

                EndSwitch
            EndIf

    EndSwitch

    ; Forward standard message to DefSubclassProc
    Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>_SubclassProc

Func _WinAPI_FindWindowEx($hParent, $hAfter, $sClass, $sTitle = "")
    Local $ret = DllCall("user32.dll", "hwnd", "FindWindowExW", "hwnd", $hParent, "hwnd", $hAfter, "wstr", $sClass, "wstr", $sTitle)
    If @error Or Not IsArray($ret) Then Return 0
    Return $ret[0]
EndFunc   ;==>_WinAPI_FindWindowEx

Func _WinAPI_AllowDarkModeForWindow($hWND, $bAllow = True)
    Local $aResult = DllCall("UxTheme.dll", "bool", 133, "hwnd", $hWND, "bool", $bAllow)
    If @error Then Return SetError(1, 0, False)
    Return ($aResult[0] <> 0)
EndFunc   ;==>_WinAPI_AllowDarkModeForWindow

Func _WinAPI_FlushMenuThemes()
    Local $aResult = DllCall("UxTheme.dll", "none", 136)
    If @error Then Return SetError(1, 0, False)
    Return True
EndFunc   ;==>_WinAPI_FlushMenuThemes

Func _WinAPI_RefreshImmersiveColorPolicyState()
    Local $aResult = DllCall("UxTheme.dll", "none", 104)
    If @error Then Return SetError(1, 0, False)
    Return True
EndFunc   ;==>_WinAPI_RefreshImmersiveColorPolicyState

Func _WinAPI_SetPreferredAppMode($iPreferredAppMode) ;Windows 10 Build 18362+
    Local $aResult = DllCall("UxTheme.dll", "long", 135, "long", $iPreferredAppMode)
    If @error Then Return SetError(1, 0, False)
    Return $aResult[0]
EndFunc   ;==>_WinAPI_SetPreferredAppMode
#EndRegion DarkMode API

; #FUNCTION# ====================================================================================================================
; Name ..........: _WinAPI_DwmSetWindowAttribute_unr
; Description ...: Dose the same as _WinAPI_DwmSetWindowAttribute; But has no Restrictions
; Syntax ........: _WinAPI_DwmSetWindowAttribute_unr($hWnd, $iAttribute, $iData)
; Parameters ....: $hWnd                - a handle value.
;                  $iAttribute          - an integer value.
;                  $iData               - an integer value.
; Return values .: Success: 1 Failure: @error, @extended & False
; Author ........: argumentum
; Modified ......:
; Remarks .......:
; Related .......:
; Link ..........: https://www.autoitscript.com/forum/topic/211475-winapithemeex-darkmode-for-autoits-win32guis/?do=findComment&comment=1530103
; Example .......: No
; ===============================================================================================================================
Func _WinAPI_DwmSetWindowAttribute_unr($hWnd, $iAttribute, $iData) ; #include <WinAPIGdi.au3> ; unthoughtful unrestricting mod.
    Local $aCall = DllCall('dwmapi.dll', 'long', 'DwmSetWindowAttribute', 'hwnd', $hWnd, 'dword', $iAttribute, _
            'dword*', $iData, 'dword', 4)
    If @error Then Return SetError(@error, @extended, 0)
    If $aCall[0] Then Return SetError(10, $aCall[0], 0)
    Return 1
EndFunc   ;==>_WinAPI_DwmSetWindowAttribute_unr

 

Posted

That seems to be working fine :

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

Example()

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

  Local $idListView = GUICtrlCreateListView("Items List|SubItems1|SubItems2", 10, 10, 480, 280)
  Local $hListView = GUICtrlGetHandle($idListView)
  Local $hHeader = GUICtrlSendMsg($idListView, $LVM_GETHEADER, 0, 0)

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

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

  GUISetState()

  Do
  Until GUIGetMsg() = $GUI_EVENT_CLOSE

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

Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
  Local Static $bHover, $tPoint
  Switch $iMsg
    Case $WM_NOTIFY
      Local $tCustDraw = DllStructCreate($tagNMLVCUSTOMDRAW, $lParam)
      If $tCustDraw.hWndFrom = $pData And $tCustDraw.Code = $NM_CUSTOMDRAW 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)

            Local $hBrush = _WinAPI_CreateSolidBrush($bHover And _WinAPI_PtInRect($tRect, $tPoint) ? (_IsPressed($VK_LBUTTON) ? 0xA0A0A0 : 0x505050) : 0x202020)
            _WinAPI_FillRect($tCustDraw.hDC, $tRect, $hBrush)
            _WinAPI_DeleteObject($hBrush)

            _WinAPI_InflateRect($tRect, -5, -2)
            _WinAPI_DrawText($tCustDraw.hDC, _GUICtrlHeader_GetItemText($tCustDraw.hWndFrom, $tCustDraw.dwItemSpec), $tRect, $DT_LEFT)

            Return $CDRF_SKIPDEFAULT
        EndSwitch
      EndIf
    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
        $tPoint = 0
        _WinAPI_InvalidateRect($hWnd, 0, False)
      EndIf
  EndSwitch
  Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>WM_NOTIFY

 

Posted (edited)

Hmm, apologies for the sidetrack, but I don't think we're quite there with the materials generally. 

The materials are presumably meant to be a background in and of themselves.  ATM we're blending them with the backcolor of our win, so we lose the semi-transparent effect. Also, extending out that frame into the client area is affecting our controls. Ideally those should be painted on top of any window background...

I've setup the left widow so you can remove the background and you can see what I mean.   Obviously this approach is a non starter... if the window manager thinks the background is transparent, your mouse will click straight through it!

#include <GUIConstants.au3>
#include <WindowsConstants.au3>
#include <WinAPI.au3>
#include <WinAPITheme.au3>

Global Const $DWMSBT_AUTO = 0               ; Default (Auto)
Global Const $DWMSBT_NONE = 1               ; None
Global Const $DWMSBT_MAINWINDOW = 2         ; Mica
Global Const $DWMSBT_TRANSIENTWINDOW = 3    ; Acrylic
Global Const $DWMSBT_TABBEDWINDOW = 4       ; Mica Alt (Tabbed)

Example()

Func Example()
    Local $hGUI = GUICreate("Win1", 300, 170, (@DesktopWidth-610)/2, -1, -1, BitOR($WS_EX_COMPOSITED, $WS_EX_LAYERED))
    _WinAPI_SetLayeredWindowAttributes($hGUI, 0x123456, 255)
    Local $idRmvBkgnd = GUICtrlCreateCheckbox("Remove Bkgnd", 10, 10, 90, 25, $BS_PUSHLIKE)
    GUISetState()


    Local $hGUI2 = GUICreate("Win2", 300, 170, (@DesktopWidth+10)/2, -1, -1)
    Local $idExtendFrame = GUICtrlCreateCheckbox("Extend Frame", 10, 10, 90, 25, $BS_PUSHLIKE)
    Local $idHalfExtendFrame = GUICtrlCreateCheckbox("Patial Frame", 120, 10, 90, 25, $BS_PUSHLIKE)
    GUISetState()


    _WinAPI_DwmSetWindowAttributeEx($hGUI, $DWMWA_SYSTEMBACKDROP_TYPE, $DWMSBT_TRANSIENTWINDOW)
    _WinAPI_DwmSetWindowAttributeEx($hGUI2, $DWMWA_SYSTEMBACKDROP_TYPE, $DWMSBT_TRANSIENTWINDOW)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

            Case $idRmvBkgnd
                If GUICtrlRead($idRmvBkgnd) = $GUI_CHECKED Then
                    _WinAPI_SetLayeredWindowAttributes($hGUI, _WinAPI_GetSysColor($COLOR_3DFACE))
                Else
                    _WinAPI_SetLayeredWindowAttributes($hGUI, 0x123456)
                EndIf

            Case $idExtendFrame
                If GUICtrlRead($idExtendFrame) = $GUI_CHECKED Then
                    GUICtrlSetState($idHalfExtendFrame, $GUI_UNCHECKED)
                    _WinAPI_DwmExtendFrameIntoClientArea($hGUI2, _WinAPI_CreateMargins(150, 85, 150, 85))
                Else
                    _WinAPI_DwmExtendFrameIntoClientArea($hGUI2, _WinAPI_CreateMargins(0, 0, 0, 0))
                EndIf

            Case $idHalfExtendFrame
                If GUICtrlRead($idHalfExtendFrame) = $GUI_CHECKED Then
                    GUICtrlSetState($idExtendFrame, $GUI_UNCHECKED)
                    _WinAPI_DwmExtendFrameIntoClientArea($hGUI2, _WinAPI_CreateMargins(25, 25, 25, 25))
                Else
                    _WinAPI_DwmExtendFrameIntoClientArea($hGUI2, _WinAPI_CreateMargins(0, 0, 0, 0))
                EndIf
        EndSwitch
    WEnd

    GUIDelete($hGUI)
    GUIDelete($hGUI2)

EndFunc   ;==>Example

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

 

Edited by MattyD
english!
Posted (edited)
42 minutes ago, argumentum said:

if you resize the columns it all goes crazy

Yes you are right.  Need to check this.

ps. subclassing the header by itself is causing Windows to react erroneously.  Even with a very minimalistic subclass proc, resizing the header causes the GUI to freeze after awhile.  

Edited by Nine
Posted (edited)
54 minutes ago, MattyD said:

The materials are presumably meant to be a background in and of themselves.  ATM we're blending them with the backcolor of our win, so we lose the semi-transparent effect

You are right. This “extending to client area” technique is what programs such as Mica For Everyone uses. In their case, they are doing it system-wide to work with any Win32 app. They don’t have a direct hook with individual app subclassing though, so they are limited in that sense.

In our case, we have subclassing and full control since it is our GUI. So technically we could do much better with regard to materials.

This extending method can be bad for the look of fonts as well since they would need alpha blending.

Anyway, the proper method for applying materials to GUI and individual control windows is all done via WinRT functions. If we had that in AutoIt that would be phenomenal with regard to modern GUI functionality. But I have never been able to understand how to use the WinRT functionality in AutoIt to apply materials.

If we had that, we would have much more control over material opacity, strength of effects and other fine tuning.

Edited by WildByDesign
  • Solution
Posted (edited)

I think the issue with the header resize is solved.  

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

Example()

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

  Local $idListView = GUICtrlCreateListView("Items List|SubItems1|SubItems2", 10, 10, 480, 280, -1, $LVS_EX_DOUBLEBUFFER)
  Local $hListView = GUICtrlGetHandle($idListView)
  Local $hHeader = GUICtrlSendMsg($idListView, $LVM_GETHEADER, 0, 0)

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

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

  GUISetState()

  Do
  Until GUIGetMsg() = $GUI_EVENT_CLOSE

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

Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
  Local Static $bHover, $tPoint
  Switch $iMsg
    Case $WM_NOTIFY
      Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
      Switch $tNMHDR.Code
        Case $NM_CUSTOMDRAW
          If $tNMHDR.hWndFrom = $pData 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)

                Local $hBrush = _WinAPI_CreateSolidBrush($bHover And _WinAPI_PtInRect($tRect, $tPoint) ? (_IsPressed($VK_LBUTTON) ? 0xA0A0A0 : 0x505050) : 0x202020)
                _WinAPI_FillRect($tCustDraw.hDC, $tRect, $hBrush)
                _WinAPI_DeleteObject($hBrush)

                _WinAPI_InflateRect($tRect, -5, -2)
                _WinAPI_DrawText($tCustDraw.hDC, _GUICtrlHeader_GetItemText($tCustDraw.hWndFrom, $tCustDraw.dwItemSpec), $tRect, $DT_LEFT)

                Return $CDRF_SKIPDEFAULT
            EndSwitch
          EndIf
        Case $HDN_BEGINTRACKW
          $bHover = False
          _WinAPI_RemoveWindowSubclass($pData, DllCallbackGetPtr($iID), 1000)
        Case $HDN_ENDTRACKW
          _WinAPI_SetWindowSubclass($pData, DllCallbackGetPtr($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

Added double buffer to avoid flickers.

Edited by Nine
better code
Posted
52 minutes ago, Nine said:

I think the issue with the header resize is solved. 

You did an absolutely incredible job with this. I feel like this was a pretty unique challenge and you nailed it. It works very well and performs great since you added the double buffering to the ListView. I am really impressed with your techniques.

Thank you for always being open to interesting and unique challenges. :)

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...