Jump to content

Need help synchronizing MDI (WS_EX_MDICHILD) window with Child Window (WS_CHILD)


Go to solution Solved by pixelsearch,

Recommended Posts

Posted (edited)

@pixelsearch I have a question regarding the following code:

Case $LVN_ENDSCROLL
    Local Static $tagNMLVSCROLL = $tagNMHDR & ";int dx;int dy"
    Local $tNMLVSCROLL = DllStructCreate($tagNMLVSCROLL, $lParam)
    If $tNMLVSCROLL.dy = 0 Then ; ListView horizontal scrolling
        _resizeHeaderItems()
    EndIf

 

The horizontal scrolling is very smooth. However, I notice occasionally that vertical scrolling can cause some flicker. Now, there is no need to resize headers when vertical scrolling, of course. However, if we can run _changeExStyles() when vertical scrolling is detected, I am pretty confident that it will get rid of any vertical scrolling flicker.

Is there a way to run that function when vertical scrolling is detected?

Thank you. :)

 

EDIT:  I ended up figuring it out thanks to you already having the structure in place. Thanks again. :)

Case $LVN_ENDSCROLL
    Local Static $tagNMLVSCROLL = $tagNMHDR & ";int dx;int dy"
    Local $tNMLVSCROLL = DllStructCreate($tagNMLVSCROLL, $lParam)
    If $tNMLVSCROLL.dy = 0 Then ; ListView horizontal scrolling
        _resizeHeaderItems()
    EndIf
    If $tNMLVSCROLL.dx = 0 Then ; ListView vertical scrolling
        ConsoleWrite("vertical scrolling detected" & @CRLF)
    EndIf

 

Edited by WildByDesign
Posted (edited)
1 hour ago, WildByDesign said:

I ended up figuring it out thanks to you already having the structure in place. Thanks again. :)

Yes but please be careful, $LVN_ENDSCROLL shouldn't be the right place to run _changeExStyles() , I would place it when $LVN_BEGINSCROLL is detected (for vertical scrolling only)

Here is a WM_NOTIFY function (just for test purpose) . It indicates how many times each notification occurs. From all notifications found in the function, only 4 are triggered a lot of times (when others are triggered only once. These 4 frequent notifications are : $HDN_TRACK, $HDN_TRACKW, $LVN_BEGINSCROLL, $LVN_ENDSCROLL

As you suggested, I added _changeExStyles() in the $LVN_BEGINSCROLL part (when vertical scroll begins). As $LVN_BEGINSCROLL / $LVN_ENDSCROLL are constantly triggered when the scrollbars are moved, you'll have to check on your computer if calling _changeExStyles() so frequently creates an issue... or not.
Good luck :)

Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam

    Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    Local $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    Local $iCode = DllStructGetData($tNMHDR, "Code")
    Switch $hWndFrom
        Case $g_hHeader
            Switch $iCode
                Case $HDN_TRACK, $HDN_TRACKW
                    Local Static $iHDN_TRACK = 0
                    $iHDN_TRACK += 1
                    ConsoleWrite("$iHDN_TRACK #" & $iHDN_TRACK & @crlf)
                    Local $tNMHEADER = DllStructCreate($tagNMHEADER, $lParam)
                    Local $iHeaderItem = $tNMHEADER.Item
                    Local $tHDITEM = DllStructCreate($tagHDITEM, $tNMHEADER.pItem)
                    Local $iHeaderItemWidth = $tHDITEM.XY
                    _GUICtrlHeader_SetItemWidth($g_hHeader, $iHeaderItem, $iHeaderItemWidth)
                    _resizeLVCols() ; <========================
                    Return False ; to continue tracking the divider

                Case $HDN_BEGINTRACK, $HDN_BEGINTRACKW
                    Local Static $iHDN_BEGINTRACK = 0
                    $iHDN_BEGINTRACK += 1
                    ConsoleWrite("$iHDN_BEGINTRACK #" & $iHDN_BEGINTRACK & @crlf)
                    Return False ; To allow tracking of the divider

                Case $HDN_ENDTRACK, $HDN_ENDTRACKW
                    Local Static $iHDN_ENDTRACK = 0
                    $iHDN_ENDTRACK += 1
                    ConsoleWrite("$iHDN_ENDTRACK #" & $iHDN_ENDTRACK & @crlf)

                Case $HDN_BEGINDRAG
                    Local Static $iHDN_BEGINDRAG = 0
                    $iHDN_BEGINDRAG += 1
                    ConsoleWrite("$iHDN_BEGINDRAG #" & $iHDN_BEGINDRAG &@crlf)
                    Return False ; To allow the header control to automatically manage drag-and-drop operations

                Case $HDN_ENDDRAG
                    Local Static $iHDN_ENDDRAG = 0
                    $iHDN_ENDDRAG += 1
                    ConsoleWrite("$iHDN_ENDDRAG #" & $iHDN_ENDDRAG & @crlf)
                    $g_bHeaderEndDrag = True ; <========================
                    Return False ; to allow the control to automatically place and reorder the item

                Case $HDN_ITEMCLICK, $HDN_ITEMCLICKW
                    Local $tNMHEADER = DllStructCreate($tagNMHEADER, $lParam)
                    Local $iHeaderItem = $tNMHEADER.Item
                    ConsoleWrite("Item " & $iHeaderItem & " clicked"  & @crlf)

                Case $HDN_DIVIDERDBLCLICK, $HDN_DIVIDERDBLCLICKW
                    Local $tNMHEADER = DllStructCreate($tagNMHEADER, $lParam)
                    Local $iHeaderItem = $tNMHEADER.Item
                    ConsoleWrite("Divider " & $iHeaderItem & " double clicked"  & @crlf)
            EndSwitch

        Case $g_hListView
            Switch $iCode
                Case $LVN_BEGINSCROLL, $LVN_ENDSCROLL
                    Local Static $iLVN_BEGINSCROLL_H = 0, $iLVN_ENDSCROLL_H = 0, $iLVN_BEGINSCROLL_V = 0, $iLVN_ENDSCROLL_V = 0
                    Local Static $tagNMLVSCROLL = $tagNMHDR & ";int dx;int dy"
                    Local $tNMLVSCROLL = DllStructCreate($tagNMLVSCROLL, $lParam)
                    If $tNMLVSCROLL.dy = 0 Then ; ListView horizontal scrolling
                        Switch $iCode
                            Case $LVN_BEGINSCROLL
                                $iLVN_BEGINSCROLL_H += 1
                                ConsoleWrite("$iLVN_BEGINSCROLL_H #" & $iLVN_BEGINSCROLL_H & @crlf)
                            Case Else ; $LVN_ENDSCROLL
                                $iLVN_ENDSCROLL_H += 1
                                ConsoleWrite("$iLVN_ENDSCROLL_H #" & $iLVN_ENDSCROLL_H & @crlf)
                                _resizeHeaderItems() ; <========================
                        EndSwitch
                    Else ; ListView vertical scrolling
                        Switch $iCode
                            Case $LVN_BEGINSCROLL
                                $iLVN_BEGINSCROLL_V += 1
                                ConsoleWrite("$iLVN_BEGINSCROLL_V #" & $iLVN_BEGINSCROLL_V & @crlf)
                                _changeExStyles() ; <========================
                            Case Else ; $LVN_ENDSCROLL
                                $iLVN_ENDSCROLL_V += 1
                                ConsoleWrite("$iLVN_ENDSCROLL_V #" & $iLVN_ENDSCROLL_V & @crlf)
                        EndSwitch
                    EndIf
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY

 

Edited by pixelsearch
typo

"I think you are searching a bug where there is no bug... don't listen to bad advice."

Posted (edited)
22 minutes ago, pixelsearch said:

you'll have to check on your computer if calling _changeExStyles() so frequently creates an issue... or not

Realistically, I should figure out a way to limit how often it gets called. I have to find a way to track that and keep a variable of the time difference.

Since the Adlib stays active for 3 seconds before reverting styles, the _changeExStyles() technically only needs to be called every 2 seconds maybe.

I actually tried to track this time diff earlier but failed miserably. I’ll have to try again though because it would be important for performance and efficiency.

Edited by WildByDesign
Posted

For the record, calling  _changeExStyles() from $LVN_BEGINSCROLL behaves badly on my computer (vertical scrolling down and up, which calls _changeExStyles() dozen of times in a few ms, when the function adds/removes styles to the GUI/LV with a timer each 3s . This is the wrong display I got now :

issuecalling_changeExStylestoooften.png.83546a48259608a3260398165b74d6e8.png

 

11 minutes ago, WildByDesign said:

I’ll have to try again though because it would be important for performance and efficiency.

Hope you'll find a way. Good luck :)

"I think you are searching a bug where there is no bug... don't listen to bad advice."

Posted
19 hours ago, pixelsearch said:

For the record, calling  _changeExStyles() from $LVN_BEGINSCROLL behaves badly on my computer (vertical scrolling down and up, which calls _changeExStyles() dozen of times in a few ms, when the function adds/removes styles to the GUI/LV with a timer each 3s . This is the wrong display I got now :

I just wanted to confirm that I get those glitches with $LVN_BEGINSCROLL as well. It seems to be guaranteed to be problematic. For whatever reason, I don't know, but having it in $LVN_ENDSCROLL seems to work 100% for me. No issues there.

 

19 hours ago, pixelsearch said:

Hope you'll find a way. Good luck :)

I had been trying many overly complicated methods which failed. I ended up going with a high precision timer which was more simplistic but works very well. Initially I had the timer for 3 seconds to match the adlib but it would cause an occasional flicker in between. So I switched the timer to 2 seconds while the adlib is at 3 seconds to allow some overlap.

In this case, the function does a Return immediately if it was already called less than 2 seconds prior. If it's between 2-3 seconds, it will call it again and also reset the 3 second adlib timer.

So while that all works well and efficiently, I'm trying some other ideas at the moment.

Posted
On 12/5/2025 at 9:55 PM, ioa747 said:

and,  after a little observation, I noticed that after each change of _GUIFrame, it updates the GUI with Msg 4, so

By the way, how did you find out the possible Msg numbers?

Do you know if there is a Msg related to starting the movement of the separator? As opposed to after it has moved?

Posted
2 hours ago, WildByDesign said:

By the way, how did you find out the possible Msg numbers?

as I said, after a little observation  :)
 

after reading the index

; #CURRENT# ==========================================================================================================
; _GUIFrame_Create:       Splits a GUI into 2 frames
; _GUIFrame_SetMin:       Sets minimum sizes for each frame
; _GUIFrame_ResizeSet:    Sets resizing flag for all or specified frame sets
; _GUIFrame_GetHandle:    Returns the handle of a frame element (required for further splitting)
; _GUIFrame_Switch:       Sets a frame element as current GUI
; _GUIFrame_GetSepPos:    Returns the current position of the separator
; _GUIFrame_SetSepPos:    Moves the separator bar to adjust frame sizes
; _GUIFrame_ResizeState:  Returns the resize state of the frames
; _GUIFrame_ResizeReg:    Registers WM_SIZE message for resizing
; _GUIFrame_SIZE_Handler: WM_SIZE message handler to resize frames using _GUIFrame_Move
; ====================================================================================================================

; #INTERNAL_USE_ONLY#=================================================================================================
; _GUIFrame_SepSubClass:   Sets new WndProc for frame separator bar
; _GUIFrame_SepWndProc:    New WndProc for frame separator bar
; _GUIFrame_SepPassMsg:    Passes Msg to original frame WndProc for action
; _GUIFrame_Move:          Moves and resizes frame elements and separator bars
; _GUIFrame_Exit:          Deletes all subclassed separator bars to free UDF WndProc and frees registered callback handle
; ====================================================================================================================

I noticed the _GUIFrame_SepPassMsg:    Passes Msg to original frame WndProc for action
and then I put it

While True
    Local $iMsg = GUIGetMsg()
    Switch $iMsg
        ...
        
        Case Else
            If $iMsg <> 0 Then ConsoleWrite("$iMsg=" & $iMsg & @CRLF)
            
    EndSwitch
WEnd

to see

 

I know that I know nothing

Posted

Is there a way to trigger a GUI Msg that can be picked up in the GUIGetMsg() While loop?

For example, in the GUIFrame UDF file in function _GUIFrame_SepWndProc(), there is a section of code that gets triggered anytime you move the separator.

Here:

; Move the GUIs to the correct places
WinMove($hFirstFrame, "", 0, 0, $iFirstWidth, $iHeight)
WinMove($hSecondFrame, "", $iFirstWidth + $iSeparatorSize, 0, $iWidth - ($iFirstWidth + $iSeparatorSize), $iHeight)
WinMove($hSeparator, "", $iFirstWidth, 0, $iSeparatorSize, $iHeight)

And also in the ElseIf below it:

WinMove($hFirstFrame, "", 0, 0, $iWidth, $iFirstHeight)
WinMove($hSecondFrame, "", 0, $iFirstHeight + $iSeparatorSize, $iWidth, $iHeight - ($iFirstHeight + $iSeparatorSize))
WinMove($hSeparator, "", 0, $iFirstHeight, $iWidth, $iSeparatorSize)

If there is a way to put something there that would trigger a specific GUI Msg, then I can do some interesting things with it. That is the only area that seems to trigger while the separator is actually moving instead of after it stops moving.

Posted

@pixelsearch After a few days, I ended up finding a far more efficient way to do it for the GUIFrame stuff. I made it so that the extended styles are only modified (briefly) when the GUIFrame separator is moving. I realized that that is technically the only time in which we really need WS_EX_COMPOSITED. This way it's not getting constantly hammered with all of the ListView and header activity. Plus, I still have it limited to every 750ms.

Anyway, as a result of everyone's help here, I have finally opened up the project collab thread for Files Au3 - AutoIt File Manager

@ioa747 I've got the header resizing in real-time now while the separator is moving which is nice. I had to modify the UDF to do it for now, but if you have a proper way to do it please let me know. :)

 

Posted (edited)

Here is  an improved version concerning the horizontal scrolling / header resizing / cols reordering, when a "detached" header control needs to be "linked" to a listview that has no headers.

Also a button "Infos" has been added, it allows to display anytime in the console informations concerning the actual position &  rectangle size of all headers & columns.

Some code should probably be improved, but that's all I got for now. Hope it helps

#include <GUIConstantsEx.au3>
#include <GuiListView.au3>
#include <StructureConstants.au3>
#include <WinAPISysWin.au3>
#include <WindowsConstants.au3>

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

Opt("MustDeclareVars", 1) ;0=no, 1=require pre-declaration

Global $g_hGUI, $g_hChild, $g_hHeader, $g_hListview, $g_bHeaderEndDrag

Example()

Func Example()
    $g_hGUI = GUICreate("Example", 400, 400, -1, -1, $WS_OVERLAPPEDWINDOW, $WS_EX_TOPMOST)
    GUISetBkColor(0x202020)

    Local $idButton = GUICtrlCreateButton("Infos", 160, 370, 80, 20)

    GUISetState(@SW_SHOW, $g_hGUI)

    $g_hChild = GUICreate("ChildWindow", 320, 320, 40, 40, $WS_CHILD, -1, $g_hGUI)
    GUISetBkColor(0x606060)

    $g_hHeader = _GUICtrlHeader_Create($g_hChild)
    Local $iHeaderHeight = _WinAPI_GetWindowHeight($g_hHeader)
    For $i = 0 To 3
        _GUICtrlHeader_AddItem($g_hHeader, "Column" & $i, 120)
    Next

    Local $idListview = GUICtrlCreateListView("col0|col1|col2|col3", 0, $iHeaderHeight, 320, 320 - $iHeaderHeight, _
        BitOR($GUI_SS_DEFAULT_LISTVIEW, $LVS_NOCOLUMNHEADER), $LVS_EX_FULLROWSELECT)
    $g_hListview = GUICtrlGetHandle($idListview)

    _GUICtrlListView_BeginUpdate($idListview)
    For $i = 1 To 30
        GUICtrlCreateListViewItem("item" & $i & "-0|item" & $i  & "-1|item" & $i & "-2|item" & $i & "-3", $idListview)
    Next
    _GUICtrlListView_EndUpdate($idListview)

    ; resize listview columns to match header widths
    _resizeLVCols()

    GUISetState(@SW_SHOW, $g_hChild)

    ; get rid of dotted rectangle in listview, when an item got the focus
    GUICtrlSendMsg($idListview, $WM_CHANGEUISTATE, 65537, 0)

    GUIRegisterMsg($WM_NOTIFY, WM_NOTIFY)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop
            Case $idButton
                GUICtrlSetState($idButton, $GUI_DISABLE)
                _Infos()
                GUICtrlSetState($idButton, $GUI_ENABLE)
        EndSwitch

        If $g_bHeaderEndDrag Then
            $g_bHeaderEndDrag = False
            _reorderLVCols()
        EndIf
    WEnd

    _GUICtrlHeader_Destroy($g_hHeader)
    GUIDelete($g_hChild)
    GUIDelete($g_hGUI)
EndFunc   ;==>Example

;==============================================
Func _Infos()
    Local $iCol, $iHDColWidth, $iLVColWidth, $aRectHD, $aRectLV
    Local $aOrder = _GUICtrlHeader_GetOrderArray($g_hHeader)
    Local Static $iTest = 0

    $iTest +=1
    ConsoleWrite("#" & $iTest & @TAB & "Column" & @TAB & "Left" & @TAB & "Right" & @TAB & "Width" & @crlf)

    For $iIndex = 1 To $aOrder[0]
        $iCol = $aOrder[$iIndex]
        $aRectHD = _GUICtrlHeader_GetItemRect($g_hHeader, $iCol) ; remember $aRectHD[0] & $aRectHD[2] always >= 0 in a Header rectangle
        If $iCol > 0 Then ; LV subitem
            $aRectLV = _GUICtrlListView_GetSubItemRect($g_hListView, 0, $iCol) ; $aRect[0] & $aRect[2] may be < 0 in a ListView rectangle
        Else ; LV item (col 0) and its annoying rectangle size
            $aRectLV = _GUICtrlListView_GetItemRect($g_hListView, 0, 2) ; 2 = bounding rectangle of the item text
            $aRectLV[0] -= 4 ; adjust LV col 0 left coord (+++)
        EndIf
        $iHDColWidth = _GUICtrlHeader_GetItemWidth($g_hHeader, $iCol)
        $iLVColWidth = _GUICtrlListView_GetColumnWidth($g_hListView, $iCol)
        ConsoleWrite("+Header" & @TAB & $iCol & @TAB & $aRectHD[0] & @TAB & $aRectHD[2] & @TAB & $iHDColWidth & @crlf)
        ConsoleWrite("-ListV"  & @TAB & $iCol & @TAB & $aRectLV[0] & @TAB & $aRectLV[2] & @TAB & $iLVColWidth & @crlf)
    Next

    ConsoleWrite(@crlf)
EndFunc   ;==>Infos

;==============================================
Func _resizeHeaderItems() ; after each horizontal scroll notification (function seems to work fine)
    Local $aRectLV
    For $iCol = 0 To _GUICtrlListView_GetColumnCount($g_hListView) - 1
        If $iCol Then
            $aRectLV = _GUICtrlListView_GetSubItemRect($g_hListView, 0, $iCol) ; $aRect[0] & $aRect[2] may be < 0 in a ListView rectangle
        Else ; column 0 needs _GUICtrlListView_GetItemRect() in case it has been dragged elsewhere
            $aRectLV = _GUICtrlListView_GetItemRect($g_hListView, $iCol, 2) ; 2 = bounding rectangle of the item text
            $aRectLV[0] -= 4 ; adjust LV col 0 left coord (+++)
        EndIf
        _GUICtrlHeader_SetItemWidth( $g_hHeader, $iCol, $aRectLV[2] - (($aRectLV[0] > 0) ? $aRectLV[0] : 0) )
    Next
EndFunc   ;==>_resizeHeaderItems

;==============================================
Func _resizeLVCols() ; resize listview columns to match header widths (1st display only, before any horizontal scrolling)
    For $i = 0 To _GUICtrlHeader_GetItemCount($g_hHeader) - 1
        _GUICtrlListView_SetColumnWidth($g_hListview, $i, _GUICtrlHeader_GetItemWidth($g_hHeader, $i))
    Next
EndFunc   ;==>_resizeLVCols

;==============================================
Func _resizeLVCols2() ; called while a header item is tracked (to be improved) . Also called from _reorderLVCols()
    Local $aRectHD, $aRectLV
    For $iCol = 0 To _GUICtrlListView_GetColumnCount($g_hListView) - 1
        $aRectHD = _GUICtrlHeader_GetItemRect($g_hHeader, $iCol) ; remember $aRectHD[0] & $aRectHD[2] always >= 0 in a Header rectangle
        If $iCol Then
            $aRectLV = _GUICtrlListView_GetSubItemRect($g_hListView, 0, $iCol) ; $aRect[0] & $aRect[2] may be < 0 in a ListView rectangle
        Else ; column 0 needs _GUICtrlListView_GetItemRect() in case it has been dragged elsewhere
            $aRectLV = _GUICtrlListView_GetItemRect($g_hListView, 0, 2) ; 2 = bounding rectangle of the item text
            $aRectLV[0] -= 4 ; adjust LV col 0 left coord (+++)
        EndIf
        If $aRectLV[0] < 0 And $aRectHD[2] > 0 Then $aRectHD[0] = $aRectLV[0]
        _GUICtrlListView_SetColumnWidth($g_hListview, $iCol, $aRectHD[2] - $aRectHD[0])
     Next
EndFunc   ;==>_resizeLVCols2

;==============================================
Func _reorderLVCols()
    ; remove LVS_NOCOLUMNHEADER from listview
    __WinAPI_Set_Window_Style($g_hListView, $LVS_NOCOLUMNHEADER, False, False)
    ; reorder listview columns order to match header items order
    Local $aOrder = _GUICtrlHeader_GetOrderArray($g_hHeader)
    _GUICtrlListView_SetColumnOrderArray($g_hListview, $aOrder)
    ; add LVS_NOCOLUMNHEADER back to listview
    __WinAPI_Set_Window_Style($g_hListView, $LVS_NOCOLUMNHEADER, True, False)
    _resizeLVCols2()
EndFunc   ;==>_reorderLVCols

;==============================================
Func __WinAPI_Set_Window_Style($hWnd, $i_Style, $b_Add, $b_exStyle = False) ; compacted code (from Kafu's original)
    If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd)
    Local $iIndex = $b_exStyle ? $GWL_EXSTYLE : $GWL_STYLE ; $iIndex as named by msdn & help file
    Local $i_Style_Old = _WinAPI_GetWindowLong($hWnd, $iIndex)

    If $b_Add Then
        If BitAND($i_Style_Old, $i_Style) Then Return ; style already applied
        _WinAPI_SetWindowLong($hWnd, $iIndex, BitOR($i_Style_Old, $i_Style))
    Else ; remove
        If Not BitAND($i_Style_Old, $i_Style) Then Return ; style not set
        _WinAPI_SetWindowLong($hWnd, $iIndex, BitXOR($i_Style_Old, $i_Style))
    EndIf

    Local $iRet = _WinAPI_SetWindowPos($hWnd, $HWND_TOP, 0, 0, 0, 0, BitOR($SWP_FRAMECHANGED, $SWP_NOACTIVATE, $SWP_NOMOVE, $SWP_NOSIZE, $SWP_NOZORDER)) ; +++
    If Not $iRet Then MsgBox($MB_TOPMOST, "_WinAPI_SetWindowPos", "Error = " & _WinAPI_GetLastError() & "   Message = " & _WinAPI_GetLastErrorMessage())
EndFunc   ;==>__WinAPI_Set_Window_Style

;==============================================
Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam

    Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    Local $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    Local $iCode = DllStructGetData($tNMHDR, "Code")
    Switch $hWndFrom
        Case $g_hHeader
            Switch $iCode
                Case $HDN_TRACK, $HDN_TRACKW
                    Local $tNMHEADER = DllStructCreate($tagNMHEADER, $lParam)
                    Local $iHeaderItem = $tNMHEADER.Item
                    Local $tHDITEM = DllStructCreate($tagHDITEM, $tNMHEADER.pItem)
                    Local $iHeaderItemWidth = $tHDITEM.XY
                    _GUICtrlHeader_SetItemWidth($g_hHeader, $iHeaderItem, $iHeaderItemWidth)
                    _resizeLVCols2()
                    Return False ; to continue tracking the divider

                Case $HDN_ENDDRAG
                    $g_bHeaderEndDrag = True
                    Return False ; to allow the control to automatically place and reorder the item
            EndSwitch

        Case $g_hListView
            Switch $iCode
                Case $LVN_ENDSCROLL
                    Local Static $tagNMLVSCROLL = $tagNMHDR & ";int dx;int dy"
                    Local $tNMLVSCROLL = DllStructCreate($tagNMLVSCROLL, $lParam)
                    If $tNMLVSCROLL.dy = 0 Then ; ListView horizontal scrolling
                        _resizeHeaderItems()
                    EndIf
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY

 

Edited by pixelsearch
typo

"I think you are searching a bug where there is no bug... don't listen to bad advice."

Posted (edited)
25 minutes ago, pixelsearch said:

Here is  an improved version concerning the horizontal scrolling / header resizing / cols reordering, when a "detached" header control needs to be "linked" to a listview that has no headers.

I really appreciate your help with this, thank you. In your examples, your original _resizeLVCols() and newer _resizeLVCols2() both work perfectly, 100% of the time.

For some reason, both of them seem to always be off by about 20 pixels in Files Au3. And it's always consistently around 20 pixels.

25 minutes ago, pixelsearch said:

Also a button "Infos" has been added, it allows to display anytime in the console informations concerning the actual position &  rectangle size of all headers & columns

This is really quite helpful. I decided to bring your _Infos() into Files Au3 so that I could call it anytime with a hotkey.

Here are some examples of what I am getting in the output with dragging of header:

+Header 2   0   150 150
-ListV  2   0   150 150
+Header 0   150 566 416
-ListV  0   170 590 440
+Header 1   566 666 100
-ListV  1   590 690 100

Here is an example of before horizontal scroll:

+Header 0   0   506 506
-ListV  0   20  506 506
+Header 1   506 606 100
-ListV  1   506 606 100
+Header 2   606 756 150
-ListV  2   606 756 150

And here is after horizontal scroll (bringing scrollbar to the right and back to the left)

+Header 0   0   482 482
-ListV  0   20  506 506
+Header 1   482 582 100
-ListV  1   506 606 100
+Header 2   582 732 150
-ListV  2   606 756 150

 

I am just not 100% sure what it is about Files Au3 that makes the _resizeLVCols() and _resizeLVCols2() functions end up having some of the columns off by approx. 20 or so pixels. I just don't quite understand it.

Edited by WildByDesign
Posted (edited)
2 hours ago, WildByDesign said:

In your examples, your original _resizeLVCols() and newer _resizeLVCols2() both work perfectly, 100% of the time.

Delighted to hear that, as would say our dear friend @Melba23 :D

As you got icons in your ListView (left column), then you'll probably have to work on this :
Run your amended script 4 times, changing each time a value in the function Infos() , inside this line :

GUICtrlListView_GetItemRect(... , ... , $iPart)

Retrieve immediate Infos() by pressing your hotkey : don't press or scroll anything else before !

$iPart = $LVIR_BOUNDS (0)
#1      Column  Left    Right   Width
+Header 0       0       300     300
-ListV  0       -4      550     300

$iPart = $LVIR_ICON (1)
#1      Column  Left    Right   Width
+Header 0       0       300     300
-ListV  0       0       20      300   <==== here appears your "20", it is the icon width

$iPart = $LVIR_LABEL (2)
#1      Column  Left    Right   Width
+Header 0       0       300     300
-ListV  0       16      300     300   <==== Right - Left gives the most approaching LV col width 300... (*)

$iPart = LVIR_SELECTBOUNDS (3)
#1      Column  Left    Right   Width
+Header 0       0       300     300
-ListV  0       -4      550     300

(*) ...having in mind the icon width (20) and the line $aRectLV[0] -= 4

Good luck

Edit: please don't forget this line is found 3 times in the script, in case you intend to test different values in it :

$aRectLV = _GUICtrlListView_GetItemRect($g_hListView, 0, 2) ; 2 = bounding rectangle of the item text

 

Edited by pixelsearch
added the edit comment

"I think you are searching a bug where there is no bug... don't listen to bad advice."

Posted
4 hours ago, pixelsearch said:

As you got icons in your ListView (left column), then you'll probably have to work on this

I can’t believe I didn’t think about the icons. The funny thing is that I know that the standard icon size is 20px on my 125% scaled monitor.

The good thing is that I already have a variable in the Files Au3 script that stores the icon size which already takes into account the DPI scaling.

Posted

I just tried this revised code, in case an icon is found in column 0 . Let's hope it will solve your alignment issue.

#include <GUIConstantsEx.au3>
#include <GuiImageList.au3>
#include <GuiListView.au3>
#include <StructureConstants.au3>
#include <WinAPISysWin.au3>
#include <WindowsConstants.au3>

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

Opt("MustDeclareVars", 1) ;0=no, 1=require pre-declaration

Global $g_hGUI, $g_hChild, $g_hHeader, $g_hListview, $g_iIconWidth, $g_bHeaderEndDrag

Example()

Func Example()
    $g_hGUI = GUICreate("Example", 400, 400, -1, -1, $WS_OVERLAPPEDWINDOW, $WS_EX_TOPMOST)
    GUISetBkColor(0x202020)

    Local $idButton = GUICtrlCreateButton("Infos", 160, 370, 80, 20)

    $g_hChild = GUICreate("ChildWindow", 320, 320, 40, 40, $WS_CHILD, -1, $g_hGUI)
    GUISetBkColor(0x606060)

    $g_hHeader = _GUICtrlHeader_Create($g_hChild)
    Local $iHeaderHeight = _WinAPI_GetWindowHeight($g_hHeader)
    For $i = 0 To 3
        _GUICtrlHeader_AddItem($g_hHeader, "Column" & $i, 120)
    Next

    Local $idListview = GUICtrlCreateListView("col0|col1|col2|col3", 0, $iHeaderHeight, 320, 320 - $iHeaderHeight, _
        BitOR($GUI_SS_DEFAULT_LISTVIEW, $LVS_NOCOLUMNHEADER), $LVS_EX_FULLROWSELECT)
    $g_hListview = GUICtrlGetHandle($idListview)

    Local $hImageList = _GUIImageList_Create(20, 20, 5) ; 20,20 = width/height (pixels) of each image/icon . 3rd param. 5 for 32 bits.
    For $i = 0 To 29
        _GUIImageList_AddIcon($hImageList, @SystemDir & "\shell32.dll", $i + 204)
    Next
    _GUICtrlListView_SetImageList($idListview, $hImageList, 1) ; 1 = image list with small icons

    _GUICtrlListView_BeginUpdate($idListview)
    For $i = 0 To 29
        _GUICtrlListView_AddItem($idListview, "item" & $i & "-0", $i)
        For $j = 1 To 3
            _GUICtrlListView_AddSubItem($idListview, $i, "item" & $i & "-" & $j, $j)
        Next
    Next
    _GUICtrlListView_EndUpdate($idListview)

    ; resize listview columns to match header widths + update global variable $g_iIconWidth
    _resizeLVCols()

    GUISetState(@SW_SHOW, $g_hGUI)
    GUISetState(@SW_SHOW, $g_hChild)

    ; get rid of dotted rectangle in listview, when an item got the focus
    GUICtrlSendMsg($idListview, $WM_CHANGEUISTATE, 65537, 0)

    GUIRegisterMsg($WM_NOTIFY, WM_NOTIFY)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop
            Case $idButton
                GUICtrlSetState($idButton, $GUI_DISABLE)
                _Infos()
                GUICtrlSetState($idButton, $GUI_ENABLE)
        EndSwitch

        If $g_bHeaderEndDrag Then
            $g_bHeaderEndDrag = False
            _reorderLVCols()
        EndIf
    WEnd

    _GUIImageList_Destroy($hImageList) ; needed ?
    _GUICtrlHeader_Destroy($g_hHeader)
    GUIDelete($g_hChild)
    GUIDelete($g_hGUI)
EndFunc   ;==>Example

;==============================================
Func _Infos()
    Local $iCol, $iHDColWidth, $iLVColWidth, $aRectHD, $aRectLV
    Local $aOrder = _GUICtrlHeader_GetOrderArray($g_hHeader)
    Local Static $iTest = 0

    $iTest +=1
    ConsoleWrite("#" & $iTest & @TAB & "Column" & @TAB & "Left" & @TAB & "Right" & @TAB & "Width" & @crlf)

    For $iIndex = 1 To $aOrder[0]
        $iCol = $aOrder[$iIndex]
        $aRectHD = _GUICtrlHeader_GetItemRect($g_hHeader, $iCol) ; remember $aRectHD[0] & $aRectHD[2] always >= 0 in a Header rectangle
        If $iCol > 0 Then ; LV subitem
            $aRectLV = _GUICtrlListView_GetSubItemRect($g_hListView, 0, $iCol) ; $aRectLV[0] & $aRectLV[2] may be < 0 in a ListView rectangle
        Else ; column 0 needs _GUICtrlListView_GetItemRect()
            $aRectLV = _GUICtrlListView_GetItemRect($g_hListView, 0, $LVIR_LABEL) ; bounding rectangle of the item text
            $aRectLV[0] -= (4 + $g_iIconWidth) ; adjust LV col 0 left coord (+++)
        EndIf
        $iHDColWidth = _GUICtrlHeader_GetItemWidth($g_hHeader, $iCol)
        $iLVColWidth = _GUICtrlListView_GetColumnWidth($g_hListView, $iCol)
        ConsoleWrite("+Header" & @TAB & $iCol & @TAB & $aRectHD[0] & @TAB & $aRectHD[2] & @TAB & $iHDColWidth & @crlf)
        ConsoleWrite("-ListV"  & @TAB & $iCol & @TAB & $aRectLV[0] & @TAB & $aRectLV[2] & @TAB & $iLVColWidth & @crlf)
    Next

    ConsoleWrite(@crlf)
EndFunc   ;==>Infos

;==============================================
Func _resizeHeaderItems() ; after each horizontal scroll notification (function seems to work fine)
    Local $aRectLV
    For $iCol = 0 To _GUICtrlListView_GetColumnCount($g_hListView) - 1
        If $iCol Then
            $aRectLV = _GUICtrlListView_GetSubItemRect($g_hListView, 0, $iCol)
        Else
            $aRectLV = _GUICtrlListView_GetItemRect($g_hListView, $iCol, $LVIR_LABEL)
            $aRectLV[0] -= (4 + $g_iIconWidth)
        EndIf
        _GUICtrlHeader_SetItemWidth( $g_hHeader, $iCol, $aRectLV[2] - (($aRectLV[0] > 0) ? $aRectLV[0] : 0) )
    Next
EndFunc   ;==>_resizeHeaderItems

;==============================================
Func _resizeLVCols() ; resize listview columns to match header widths (1st display only, before any horizontal scrolling)
    For $i = 0 To _GUICtrlHeader_GetItemCount($g_hHeader) - 1
        _GUICtrlListView_SetColumnWidth($g_hListview, $i, _GUICtrlHeader_GetItemWidth($g_hHeader, $i))
    Next

    ; In case column 0 got an icon, retrieve the width of the icon
    Local $aRectLV
    $aRectLV = _GUICtrlListView_GetItemRect($g_hListView, 0, $LVIR_ICON) ; bounding rectangle of the icon (if any)
    $g_iIconWidth = $aRectLV[2] - $aRectLV[0] ; without icon : 4 - 4 => 0 (tested, the famous "4" !)
                                              ; with icon of 20 pixels : 24 - 4 = 20
EndFunc   ;==>_resizeLVCols

;==============================================
Func _resizeLVCols2() ; called while a header item is tracked (to be improved ?) . Also called from _reorderLVCols()
    Local $aRectHD, $aRectLV
    For $iCol = 0 To _GUICtrlListView_GetColumnCount($g_hListView) - 1
        $aRectHD = _GUICtrlHeader_GetItemRect($g_hHeader, $iCol)
        If $iCol Then
            $aRectLV = _GUICtrlListView_GetSubItemRect($g_hListView, 0, $iCol)
        Else
            $aRectLV = _GUICtrlListView_GetItemRect($g_hListView, 0, $LVIR_LABEL)
            $aRectLV[0] -= (4 + $g_iIconWidth)
        EndIf
        If $aRectLV[0] < 0 And $aRectHD[2] > 0 Then $aRectHD[0] = $aRectLV[0]
        _GUICtrlListView_SetColumnWidth($g_hListview, $iCol, $aRectHD[2] - $aRectHD[0])
     Next
EndFunc   ;==>_resizeLVCols2

;==============================================
Func _reorderLVCols()
    ; remove LVS_NOCOLUMNHEADER from listview
    __WinAPI_Set_Window_Style($g_hListView, $LVS_NOCOLUMNHEADER, False, False)
    ; reorder listview columns order to match header items order
    Local $aOrder = _GUICtrlHeader_GetOrderArray($g_hHeader)
    _GUICtrlListView_SetColumnOrderArray($g_hListview, $aOrder)
    ; add LVS_NOCOLUMNHEADER back to listview
    __WinAPI_Set_Window_Style($g_hListView, $LVS_NOCOLUMNHEADER, True, False)
    _resizeLVCols2()
EndFunc   ;==>_reorderLVCols

;==============================================
Func __WinAPI_Set_Window_Style($hWnd, $i_Style, $b_Add, $b_exStyle = False) ; compacted code (from Kafu's original)
    If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd)
    Local $iIndex = $b_exStyle ? $GWL_EXSTYLE : $GWL_STYLE ; $iIndex as named by msdn & help file
    Local $i_Style_Old = _WinAPI_GetWindowLong($hWnd, $iIndex)

    If $b_Add Then
        If BitAND($i_Style_Old, $i_Style) Then Return ; style already applied
        _WinAPI_SetWindowLong($hWnd, $iIndex, BitOR($i_Style_Old, $i_Style))
    Else ; remove
        If Not BitAND($i_Style_Old, $i_Style) Then Return ; style not set
        _WinAPI_SetWindowLong($hWnd, $iIndex, BitXOR($i_Style_Old, $i_Style))
    EndIf

    Local $iRet = _WinAPI_SetWindowPos($hWnd, $HWND_TOP, 0, 0, 0, 0, BitOR($SWP_FRAMECHANGED, $SWP_NOACTIVATE, $SWP_NOMOVE, $SWP_NOSIZE, $SWP_NOZORDER)) ; +++
    If Not $iRet Then MsgBox($MB_TOPMOST, "_WinAPI_SetWindowPos", "Error = " & _WinAPI_GetLastError() & "   Message = " & _WinAPI_GetLastErrorMessage())
EndFunc   ;==>__WinAPI_Set_Window_Style

;==============================================
Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam

    Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    Local $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    Local $iCode = DllStructGetData($tNMHDR, "Code")
    Switch $hWndFrom
        Case $g_hHeader
            Switch $iCode
                Case $HDN_TRACK, $HDN_TRACKW
                    Local $tNMHEADER = DllStructCreate($tagNMHEADER, $lParam)
                    Local $iHeaderItem = $tNMHEADER.Item
                    Local $tHDITEM = DllStructCreate($tagHDITEM, $tNMHEADER.pItem)
                    Local $iHeaderItemWidth = $tHDITEM.XY
                    _GUICtrlHeader_SetItemWidth($g_hHeader, $iHeaderItem, $iHeaderItemWidth)
                    _resizeLVCols2()
                    Return False ; to continue tracking the divider

                Case $HDN_ENDDRAG
                    $g_bHeaderEndDrag = True
                    Return False ; to allow the control to automatically place and reorder the item
            EndSwitch

        Case $g_hListView
            Switch $iCode
                Case $LVN_ENDSCROLL
                    Local Static $tagNMLVSCROLL = $tagNMHDR & ";int dx;int dy"
                    Local $tNMLVSCROLL = DllStructCreate($tagNMLVSCROLL, $lParam)
                    If $tNMLVSCROLL.dy = 0 Then ; ListView horizontal scrolling
                        _resizeHeaderItems()
                    EndIf
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY

 

"I think you are searching a bug where there is no bug... don't listen to bad advice."

Posted
4 hours ago, pixelsearch said:

I just tried this revised code, in case an icon is found in column 0 . Let's hope it will solve your alignment issue.

Excellent work. Thank you. As always, your example works perfectly.

I updated all of the changed functions in Files Au3. The header drag and drop to reorder listview columns is working great and the alignment is 100% now. So that was probably the most important part that is resolved.

There is still an alignment problem with the horizontal scrolling for some reason. I don't really understand it. You may need to grab the latest FilesAu3-2025-12-10 that I just released because it has your latest fixes in there. This way you can reproduce it and you will likely understand what might be happening.

To repo: Run Files Au3 and make some of the columns larger, then make the GUI smaller (width) to show the horizontal scrollbar in the listview. If you scroll all the way to the right, everything is aligned well. But when you scroll back to the left, that is when the alignment problem shows up. Scrolling back to the right again always fixes it. But left again is always a trigger for it.

This likely isn't a high priority since there are only 3 columns (at the moment) in the listview. Users would really have to shrink the GUI and expand the columns more to trigger it. So if it's something that can be fixed, that would be great. If not, that is fine too. I feel like the header drag and drop issue (that you fixed) was more important and that is resolved now.

Thank you so much. It's amazing how smooth this synchronization between separate header control and listview is and how cohesive it all is. Very impressive and unique, that's for certain.

Posted

@WildByDesign The 1st thing I notice in your last version "FilesAu3-2025-12-10" is this...

; resize listview columns to match header widths
_resizeLVCols2()

...when it should be :

; resize listview columns to match header widths + update global variable $g_iIconWidth
_resizeLVCols()

This should fix some issues as $g_iIconWidth will be correctly defined (instead of keeping a value of 0)

1 hour ago, WildByDesign said:

To repo: Run Files Au3 and make some of the columns larger, then make the GUI smaller (width) to show the horizontal scrollbar in the listview.

I'll have to test this, having in mind that in my test script, the listview & header controls got a fixed width, no matter we enlarge the GUI or not. If something interesting comes out, I'll add a post here.

"I think you are searching a bug where there is no bug... don't listen to bad advice."

Posted
5 minutes ago, pixelsearch said:

This should fix some issues as $g_iIconWidth will be correctly defined (instead of keeping a value of 0)

I just wanted to confirm that this fixed the last remaining problem. I likely did a search and replace without checking carefully enough. Thanks for pointing that out. Everything is working as expected now. :)

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