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

I know that this is somewhat unusual. I also understand that MDI (WS_EX_MDICHILD) windows are intended to move with the parent GUI window, not a child window (WS_CHILD).

However, for the sake of learning and trying unusual things, here we are...

From the example, you will notice that if you move the GUI around the screen, the pink-ish square (MDI window) stays in the middle and does not move with the GUI. That is because I have associated it with the white square which is a child window (WS_CHILD).

The first part of what I want to achieve is to synchronize the movement of the MDI window with the movement of the white child window, specifically.

The second part of what I would like to achieve would be to also synchronize the sizing of MDI window.

I am still very much a rookie when it comes to WM_SIZE and WM_MOVE which I am assuming would be required here.

Anyway, if anyone has a moment and is willing to assist with this, I would appreciate it very much. Thank you. :)

#include <WindowsStylesConstants.au3>
#include <GUIConstantsEx.au3>

Example()

Func Example()
    ; Create a GUI with various controls.
    Local $hGUI = GUICreate("Example", 400, 400)
    GUISetBkColor(0x202020)



    ; Display the GUI.
    GUISetState(@SW_SHOW, $hGUI)

    $hChild = GUICreate("ChildWindow", 200, 200, 100, 100, 0x40000000, -1, $hGUI)
    GUISetBkColor(0xffffff)
    GUISetState(@SW_SHOW, $hChild)

    $hChildMDI = GUICreate("", 100, 100, 5, 5, $WS_POPUP, $WS_EX_MDICHILD, $hChild)
    GUISetBkColor(0xff00ff)
    GUISetState(@SW_SHOW, $hChildMDI)

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

        EndSwitch
    WEnd

    ; Delete the previous GUI and all controls.
    GUIDelete($hGUI)
EndFunc   ;==>Example

 

Posted

Unless there is a specific reason to mix $WS_CHILD and $WS_EX_MDICHILD, you shouldn't do that.  All you will get out of it is useless complexity.  Now if you want to adapt the child windows to the size of its parent, here one easy way :

; From Nine
#include <GUIConstants.au3>
#include <WindowsConstants.au3>

Opt("MustDeclareVars", True)

Global $hChild1, $hChild2, $aPosGUI

Example()

Func Example()
  Local $hGUI = GUICreate("Example", 400, 400, Default, Default, $WS_OVERLAPPEDWINDOW, $WS_EX_COMPOSITED)
  GUISetBkColor(0x202020)
  GUISetState()

  $hChild1 = GUICreate("", 200, 200, 100, 100, $WS_POPUP, $WS_EX_MDICHILD, $hGUI)
  GUISetBkColor(0xFFFFFF)
  GUISetState()

  $hChild2 = GUICreate("", 100, 100, 5, 5, $WS_POPUP, $WS_EX_MDICHILD, $hChild1)
  GUISetBkColor(0xFF00FF)
  GUISetState()

  GUIRegisterMsg($WM_SIZE, WM_SIZE)
  $aPosGUI = WinGetPos($hGUI)

  While True
    Switch GUIGetMsg()
      Case $GUI_EVENT_CLOSE
        ExitLoop
    EndSwitch
  WEnd
EndFunc   ;==>Example

Func WM_SIZE($hWnd, $iMsg, $wParam, $lParam)
  Local $aPos = WinGetPos($hWnd), $aChild1 = WinGetPos($hChild1), $aChild2 = WinGetPos($hChild2)
  WinMove($hChild1, "", $aChild1[0], $aChild1[1], $aChild1[2] + $aPos[2] - $aPosGUI[2], $aChild1[3] + $aPos[3] - $aPosGUI[3])
  WinMove($hChild2, "", $aChild2[0], $aChild2[1], $aChild2[2] + $aPos[2] - $aPosGUI[2], $aChild2[3] + $aPos[3] - $aPosGUI[3])
  $aPosGUI = $aPos
EndFunc   ;==>WM_SIZE

 

Posted (edited)
On 11/27/2025 at 3:09 PM, WildByDesign said:

The first part of what I want to achieve is to synchronize the movement of the MDI window with the movement of the white child window, specifically.

I tried it like this, in the following script.
Please note that you can move separately the pink MDI window by dragging it with the mouse (I applied to it the "AutoIt special" Exstyle $WS_EX_CONTROLPARENT)

In this script, we don't need (for now) 2 separate functions WM_EXITSIZEMOVE() and WM_MOVE() as their content would duplicate the code found in functions WM_ENTERSIZEMOVE() and WM_SIZE() . This could change in case you'll have to work later on separate functions WM_SIZE() and WM_MOVE()

#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>

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

Global $g_hGUI, $g_hChild, $g_hChildMDI, $g_aPosChild, $g_iDeltaX, $g_iDeltaY

Example()

Func Example()
    $g_hGUI = GUICreate("Example", 400, 400, -1, -1, $WS_OVERLAPPEDWINDOW, $WS_EX_COMPOSITED)
    GUISetBkColor(0x202020)
    GUISetState(@SW_SHOW, $g_hGUI)

    $g_hChild = GUICreate("ChildWindow", 200, 200, 100, 100, $WS_CHILD, -1, $g_hGUI)
    GUISetBkColor(0xffffff)
    GUISetState(@SW_SHOW, $g_hChild)

    $g_hChildMDI = GUICreate("", 100, 100, 5, 5, $WS_POPUP, BitOr($WS_EX_MDICHILD, $WS_EX_CONTROLPARENT), $g_hChild)
    GUISetBkColor(0xff00ff)
    GUISetState(@SW_SHOW, $g_hChildMDI)

    _CalcPosAndDelta()

    GUIRegisterMsg($WM_ENTERSIZEMOVE, "WM_ENTERSIZEMOVE")
    GUIRegisterMsg($WM_EXITSIZEMOVE,  "WM_ENTERSIZEMOVE")
    GUIRegisterMsg($WM_SIZE, "WM_SIZE")
    GUIRegisterMsg($WM_MOVE, "WM_SIZE")

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

    GUIDelete($g_hChildMDI)
    GUIDelete($g_hChild)
    GUIDelete($g_hGUI)
EndFunc   ;==>Example

Func _CalcPosAndDelta()
    Local $aPosChildMDI = WinGetPos($g_hChildMDI)
    $g_aPosChild = WinGetPos($g_hChild)
    $g_iDeltaX = $g_aPosChild[0] - $aPosChildMDI[0]
    $g_iDeltaY = $g_aPosChild[1] - $aPosChildMDI[1]
EndFunc ;==>_CalcPosAndDelta

Func WM_ENTERSIZEMOVE($hWnd, $iMsg, $wParam, $lParam)
    _CalcPosAndDelta()
EndFunc ;==>WM_ENTERSIZEMOVE

Func WM_SIZE($hWnd, $iMsg, $wParam, $lParam)
    If $hWnd = $g_hGUI Then
        $g_aPosChild = WinGetPos($g_hChild)
        WinMove($g_hChildMDI, "", $g_aPosChild[0] - $g_iDeltaX, $g_aPosChild[1] - $g_iDeltaY)
    EndIf
EndFunc ;==>WM_SIZE

 

Edited by pixelsearch
typo

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

Posted

Here is a quick addition of the ListView with LVS_EX_DOUBLEBUFFER.

By the way, @pixelsearch, I see that you just edited your example. My ListView addition is based on your example that was originally posted. I still have to check your updated example.

#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>
#include <GuiListView.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_hChildMDI, $g_aPosChild, $g_iDeltaX, $g_iDeltaY

Example()

Func Example()
    $g_hGUI = GUICreate("Example", 400, 400, -1, -1, $WS_OVERLAPPEDWINDOW, $WS_EX_COMPOSITED)
    GUISetBkColor(0x202020)
    GUISetState(@SW_SHOW, $g_hGUI)

    $g_hChild = GUICreate("ChildWindow", 320, 320, 40, 40, 0x40000000, -1, $g_hGUI)
    GUISetBkColor(0x606060)
    GUISetState(@SW_SHOW, $g_hChild)

    $g_hChildMDI = GUICreate("", 280, 280, 20, 20, $WS_POPUP, BitOr($WS_EX_MDICHILD, $WS_EX_CONTROLPARENT), $g_hChild)
    GUISetBkColor(0xff00ff)

    ; add listview
    Local $idListview = GUICtrlCreateListView("Column1|Column2", 20, 20, 240, 240, -1, BitOR($LVS_EX_FULLROWSELECT, $WS_EX_CLIENTEDGE, $LVS_EX_DOUBLEBUFFER, $LVS_EX_CHECKBOXES))
    Local $idLVi_Item1 = GUICtrlCreateListViewItem("1|1", $idListview)
    Local $idLVi_Item2 = GUICtrlCreateListViewItem("2|2", $idListview)
    Local $idLVi_Item3 = GUICtrlCreateListViewItem("3|3", $idListview)

    ; get rid of selection rectangle on listview
    GUICtrlSendMsg($idListview, $WM_CHANGEUISTATE, 65537, 0)

    GUISetState(@SW_SHOW, $g_hChildMDI)

    _CalcPosAndDelta()

    GUIRegisterMsg($WM_ENTERSIZEMOVE, "WM_ENTERSIZEMOVE")
    GUIRegisterMsg($WM_EXITSIZEMOVE,  "WM_ENTERSIZEMOVE")
    GUIRegisterMsg($WM_SIZE, "WM_SIZE")
    GUIRegisterMsg($WM_MOVE, "WM_SIZE")

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

    GUIDelete($g_hChildMDI)
    GUIDelete($g_hChild)
    GUIDelete($g_hGUI)
EndFunc   ;==>Example

Func _CalcPosAndDelta()
    Local $aPosChildMDI = WinGetPos($g_hChildMDI)
    $g_aPosChild = WinGetPos($g_hChild)
    $g_iDeltaX = $g_aPosChild[0] - $aPosChildMDI[0]
    $g_iDeltaY = $g_aPosChild[1] - $aPosChildMDI[1]
EndFunc ;==>_CalcPosAndDelta

Func WM_ENTERSIZEMOVE($hWnd, $iMsg, $wParam, $lParam)
    _CalcPosAndDelta()
EndFunc ;==>WM_ENTERSIZEMOVE

Func WM_SIZE($hWnd, $iMsg, $wParam, $lParam)
    If $hWnd = $g_hGUI Then
        $g_aPosChild = WinGetPos($g_hChild)
        WinMove($g_hChildMDI, "", $g_aPosChild[0] - $g_iDeltaX, $g_aPosChild[1] - $g_iDeltaY)
    EndIf
EndFunc ;==>WM_SIZE

 

Posted (edited)
47 minutes ago, WildByDesign said:

I see that you just edited your example.

No big deal, I simply replaced the constant 0x40000000 with its more comprehensive variable name $WS_CHILD, no more no less :D

As you can see in my script, it's very easy to synchronize a MDI (WS_EX_MDICHILD) window with a Child Window (WS_CHILD) . It also works if you minimize / maximize / restore the GUI.

I like the fact that the 2 registered functions WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE are called only 1 time per moving/sizing loop (I added a counter during tests and it's true). This is what MSDN stipulates about them :

WM_ENTERSIZEMOVE : Sent one time to a window after it enters the moving or sizing modal loop
WM_EXITSIZEMOVE  : Sent one time to a window, after it has exited the moving or sizing modal loop.

So they are "light" messages that don't require a lot of CPU at all and you can (re)initialize variables inside the functions, very useful !

According to trancexx at the very end of an old interesting thread... wait, let me paste the post and the link to the thread :

On 3/25/2010 at 8:10 AM, trancexx said:
On 3/25/2010 at 7:48 AM, 'martin said:

I notice there is a listview extended style $LVS_EX_DOUBLEBUFFER which I thought might be a better solution than using $WS_EX_COMPOSITED for the window. I tried it but it doesn't reduce the flicker for me.

 

It's not supposed to. That style reduces flicker when you resize columns, not the whole listview.

 

Edited by pixelsearch
typo

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

Posted

@WildByDesign great result with the listview inside the MDI window

So $WS_EX_COMPOSITED is now applied to the GUI (less flicker when resizing the GUI) and doesn't interfere with the "$LVS_EX_DOUBLEBUFFER listview" which is a control of an external MDI window, bravo !

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

Posted
16 minutes ago, pixelsearch said:

I like the fact that the 2 registered functions WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE are called only 1 time per moving/sizing loop (I added a counter during tests and it's true). This is what MSDN stipulates about them :

That is fantastic. Very efficient. By the way, this is all really quite genius.

17 minutes ago, pixelsearch said:

According to trancexx at the very end of an old interesting thread... wait, let me paste the post and the link to the thread :

What a coincidence, I was actually reading this same thread earlier in the morning. Sometimes I spend a good amount of time reading older threads here in the forum because it's literally a gold mine worth of information.

Anyways, your technique is working. I've got it working with WS_EX_COMPOSITE and a perfectly working ListView thanks to the MDI GUI trick.

I'll post the screenshot and more details in another comment because I just ran out of quota again...

Posted

This is what I have working so far. I'll have to open up a thread soon so that anyone can help if they want. I've got lots of ideas already for adding menubar, toolbar, statusbar, etc.

The only thing that I need to figure out still is resizing the MDI GUI when the child GUI resizes.

 

image.thumb.png.91e6173c7ae0b4d7617e3bbf4213d154.png

  • Solution
Posted (edited)
13 hours ago, WildByDesign said:

The only thing that I need to figure out still is resizing the MDI GUI when the child GUI resizes.

Fingers crossed :)

#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>
#include <GuiListView.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_hChildMDI, $g_aPosChild, $g_aDelta[4], $g_nRatio[4]

Example()

Func Example()
    Local $iParentW = 400, $iParentH = 400
    Local $iChildW = 320, $iChildH = 320, $iChildX = 40, $iChildY = 40

    $g_hGUI = GUICreate("Example", $iParentW, $iParentH, -1, -1, $WS_OVERLAPPEDWINDOW, $WS_EX_COMPOSITED)
    GUISetBkColor(0x202020)
    GUISetState(@SW_SHOW, $g_hGUI)

    $g_hChild = GUICreate("ChildWindow", $iChildW, $iChildH, $iChildX, $iChildY, $WS_CHILD, -1, $g_hGUI)
    GUISetBkColor(0x606060)
    GUISetState(@SW_SHOW, $g_hChild)

    $g_hChildMDI = GUICreate("", 280, 280, 20, 20, $WS_POPUP, BitOr($WS_EX_MDICHILD, $WS_EX_CONTROLPARENT), $g_hChild)
    GUISetBkColor(0xff00ff)
    ; add listview
    Local $idListview = GUICtrlCreateListView("Column1|Column2", 20, 20, 240, 240, -1, BitOR($LVS_EX_FULLROWSELECT, $WS_EX_CLIENTEDGE, $LVS_EX_DOUBLEBUFFER, $LVS_EX_CHECKBOXES))
    Local $idLVi_Item1 = GUICtrlCreateListViewItem("1|1", $idListview)
    Local $idLVi_Item2 = GUICtrlCreateListViewItem("2|2", $idListview)
    Local $idLVi_Item3 = GUICtrlCreateListViewItem("3|3", $idListview)
    ; get rid of selection rectangle on listview
    GUICtrlSendMsg($idListview, $WM_CHANGEUISTATE, 65537, 0)
    GUISetState(@SW_SHOW, $g_hChildMDI)

    $g_nRatio[0] = $iChildX / $iParentW
    $g_nRatio[1] = $iChildY / $iParentH
    $g_nRatio[2] = $iChildW / $iParentW
    $g_nRatio[3] = $iChildH / $iParentH

    _CalcPosAndDelta()

    GUIRegisterMsg($WM_ENTERSIZEMOVE, "WM_ENTERSIZEMOVE")
    GUIRegisterMsg($WM_EXITSIZEMOVE,  "WM_ENTERSIZEMOVE")
    GUIRegisterMsg($WM_SIZE, "WM_SIZE")
    GUIRegisterMsg($WM_MOVE, "WM_MOVE")

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

    GUIDelete($g_hChildMDI)
    GUIDelete($g_hChild)
    GUIDelete($g_hGUI)
EndFunc   ;==>Example

Func _CalcPosAndDelta()
    Local $aPosChildMDI = WinGetPos($g_hChildMDI)
    $g_aPosChild = WinGetPos($g_hChild)
    For $i = 0 To 3
        $g_aDelta[$i] = $g_aPosChild[$i] - $aPosChildMDI[$i]
    Next
EndFunc ;==>_CalcPosAndDelta

Func WM_ENTERSIZEMOVE($hWnd, $iMsg, $wParam, $lParam)
    _CalcPosAndDelta()
EndFunc ;==>WM_ENTERSIZEMOVE

Func WM_SIZE($hWnd, $iMsg, $wParam, $lParam)
    If $hWnd = $g_hGUI Then
        Local $iParentNewCliW = BitAND($lParam, 0xFFFF) ; low word
        Local $iParentNewCliH = BitShift($lParam, 16) ; high word
        WinMove($g_hChild, "", $iParentNewCliW * $g_nRatio[0], $iParentNewCliH * $g_nRatio[1], _
            $iParentNewCliW * $g_nRatio[2], $iParentNewCliH * $g_nRatio[3]) ; WinMove will use Int coords

        $g_aPosChild = WinGetPos($g_hChild)
        WinMove($g_hChildMDI, "", $g_aPosChild[0] - $g_aDelta[0], $g_aPosChild[1] - $g_aDelta[1] , _
            $g_aPosChild[2] - $g_aDelta[2], $g_aPosChild[3] - $g_aDelta[3])
    EndIf
EndFunc ;==>WM_SIZE

Func WM_MOVE($hWnd, $iMsg, $wParam, $lParam)
    If $hWnd = $g_hGUI Then
        $g_aPosChild = WinGetPos($g_hChild)
        WinMove($g_hChildMDI, "", $g_aPosChild[0] - $g_aDelta[0], $g_aPosChild[1] - $g_aDelta[1])
    EndIf
EndFunc ;==>WM_MOVE

 

Edited by pixelsearch
removed 2 superfluous lines (explanation in next post)

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

Posted
19 minutes ago, pixelsearch said:

Fingers crossed :)

This is absolute perfection! Thank you so much. 🍷

I know it may seem like a lot of "extra" here, for anyone else that may be following along or reading later. I really enjoy smooth GUI movement (flicker-free) with WS_EX_COMPOSITED, particularly combined with the GUIFrame UDF in this case. But at the same time, I am a huge fan of ListViews and use many of them in most of my bigger projects. I love ListViews. So this MDI window trick was a blessing.

Posted

@WildByDesign Thanks for the kind words !
I just edited the last script, removing these 2 lines :

Local $idLabel = GUICtrlCreateLabel("", 0, 0, $iChildW, $iChildH)
GUICtrlSetState(-1, $GUI_DISABLE)

Resizing the Child Gui using the ratio way (as in the script) doesn't require the use of a disabled label (which is another way of resizing a Child Gui)

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

Posted (edited)
8 hours ago, WildByDesign said:

So this MDI window trick was a blessing.

On second thought, $WS_EX_MDICHILD "creates a child window that will be moved with its parent (simulation of a MDI window maximize/minimize are not simulated)" (help file)

As we added all code to take care of move/resize, then why do we really need a $WS_EX_MDICHILD child window in the script ? So Instead of this...

$g_hChildMDI = GUICreate("", 280, 280, 20, 20, $WS_POPUP, BitOr($WS_EX_MDICHILD, $WS_EX_CONTROLPARENT), $g_hChild)

...we could have used the following, for the same result (except the -1, -1 isn't accurate enough, easy to fix)

$g_hChildMDI = GUICreate("", 280, 280, -1, -1, $WS_POPUP, $WS_EX_CONTROLPARENT, $g_hChild)

Edit: I just posted a final version which includes Melba23 UDF "GuiFrame.au3" in his thread, here.

Edited by pixelsearch

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

Posted (edited)

@pixelsearch It looks like I was wildly wrong about WS_EX_MDICHILD being required to make this work. Thank you for this finding. So it looks like all that is needed is WS_POPUP. This is a good and interesting finding.

Anyway, my curiosity got the better of me and I have discovered a way to do ListViews on a GUI that has WS_EX_COMPOSITED without having to do WS_POPUP. It works well in WS_CHILD which would represent how GUIFrame UDF does it. Also this is flicker-free.

So basically it is the LV header that is not playing nice. I disabled the LV header with LVS_NOCOLUMNHEADER and created my own header with _GUICtrlHeader_Create in the WS_CHILD window.

Now, the trick is synchronizing the width of the header items with the width of the listview columns. Sorting and dragging columns can likely be added later quite easily.

If you uncomment my AdlibRegister you will see that it works quick well (the column resizing when you resize the header items). But my WM_NOTIFY methods to resize are always one step behind. This is something that I still have to figure out. I assume that when it triggers, it still has the previous width of the header item.

 

EDIT: I fixed the listview/header sync with an Adlib registered in WM_NOTIFY which seems to work smoothly.

#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>
#include <GuiListView.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
Global $g_hHeader, $idListview
Global $idLVi_Item1, $idLVi_Item2, $idLVi_Item3

Example()

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

    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)
    _GUICtrlHeader_AddItem($g_hHeader, "Column1", 100)
    _GUICtrlHeader_AddItem($g_hHeader, "Column2", 100)
    _GUICtrlHeader_AddItem($g_hHeader, "Column3", 100)

    $idListview = GUICtrlCreateListView("col1|col2|col3  ", 0, 24, 320, 320, BitOR($GUI_SS_DEFAULT_LISTVIEW, $LVS_NOCOLUMNHEADER), BitOR($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT))
    $idLVi_Item1 = GUICtrlCreateListViewItem("item1|item1|item1", $idListview)
    $idLVi_Item2 = GUICtrlCreateListViewItem("item2|item2|item2", $idListview)
    $idLVi_Item3 = GUICtrlCreateListViewItem("item3|item3|item3", $idListview)

    ; resize listview columns to match header widths
    _resizeLVCols()
    ;AdlibRegister("_resizeLVCols")

    GUIRegisterMsg($WM_NOTIFY, WM_NOTIFY)

    GUISetState(@SW_SHOW, $g_hChild)

    GUICtrlSendMsg($idListview, $WM_CHANGEUISTATE, 65537, 0)

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

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

Func _resizeLVCols()
    _GUICtrlListView_SetColumnWidth($idListview, 0, _GUICtrlHeader_GetItemWidth($g_hHeader, 0))
    _GUICtrlListView_SetColumnWidth($idListview, 1, _GUICtrlHeader_GetItemWidth($g_hHeader, 1))
    _GUICtrlListView_SetColumnWidth($idListview, 2, _GUICtrlHeader_GetItemWidth($g_hHeader, 2))
    GUICtrlSendMsg($idListview, $WM_CHANGEUISTATE, 65537, 0)
EndFunc

Func _resizeLVColsAdlib()
    _resizeLVCols()
    AdlibUnRegister("_resizeLVColsAdlib")
EndFunc

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_ENDTRACK, $HDN_ENDTRACKW
                    AdlibRegister("_resizeLVColsAdlib", 5)
                    ;_resizeLVCols()
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY

 

Edited by WildByDesign
Posted (edited)

@pixelsearch Thanks to your previous __WinAPI_Set_Window_Style / _WinAPI_SetWindowLong work, I've got the ListView drag-to-reorder columns working. Part of the trick involved removing LVS_NOCOLUMNHEADER and adding it back after the column reorder.

The dragging of columns to reorder columns ended up very smooth.

After this, I have to implement the sorting of column items. That should be easy.

EDIT: Also, I need to get the height of the header control because it is different depending on the DPI that the user has set. I will probably look into using _GUICtrlHeader_GetItemRect for that but have not tried yet.

#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>
#include <GuiListView.au3>
#include <WinAPISysWin.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
Global $g_hHeader, $idListview, $aOrder, $hListView
Global $idLVi_Item1, $idLVi_Item2, $idLVi_Item3

Example()

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

    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)
    _GUICtrlHeader_AddItem($g_hHeader, "Column0", 100)
    _GUICtrlHeader_AddItem($g_hHeader, "Column1", 100)
    _GUICtrlHeader_AddItem($g_hHeader, "Column2", 100)

    $idListview = GUICtrlCreateListView("col1|col2|col3  ", 0, 24, 320, 320, BitOR($GUI_SS_DEFAULT_LISTVIEW, $LVS_NOCOLUMNHEADER), BitOR($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT, $LVS_EX_HEADERDRAGDROP))
    $hListView = GUICtrlGetHandle($idListview)
    $idLVi_Item1 = GUICtrlCreateListViewItem("col0-item1|col1-item1|col2-item1", $idListview)
    $idLVi_Item2 = GUICtrlCreateListViewItem("col0-item2|col1-item2|col2-item2", $idListview)
    $idLVi_Item3 = GUICtrlCreateListViewItem("col0-item3|col1-item3|col2-item3", $idListview)

    ; resize listview columns to match header widths
    _resizeLVCols()
    ;AdlibRegister("_resizeLVCols")

    GUIRegisterMsg($WM_NOTIFY, WM_NOTIFY)

    GUISetState(@SW_SHOW, $g_hChild)

    GUICtrlSendMsg($idListview, $WM_CHANGEUISTATE, 65537, 0)

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

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

Func _resizeLVCols()
    _GUICtrlListView_SetColumnWidth($idListview, 0, _GUICtrlHeader_GetItemWidth($g_hHeader, 0))
    _GUICtrlListView_SetColumnWidth($idListview, 1, _GUICtrlHeader_GetItemWidth($g_hHeader, 1))
    _GUICtrlListView_SetColumnWidth($idListview, 2, _GUICtrlHeader_GetItemWidth($g_hHeader, 2))
    GUICtrlSendMsg($idListview, $WM_CHANGEUISTATE, 65537, 0)
EndFunc

Func  _reorderLVCols()
    ; remove LVS_NOCOLUMNHEADER from listview
    __WinAPI_Set_Window_Style($hListView, $LVS_NOCOLUMNHEADER, _
        False, _ ; True (add) or False (remove)...
        False) ; True = ...the extended style or False = style

    $aOrder = _GUICtrlHeader_GetOrderArray($g_hHeader)
    _GUICtrlListView_SetColumnOrderArray($idListview, $aOrder)

    ; add LVS_NOCOLUMNHEADER back to listview
    __WinAPI_Set_Window_Style($hListView, $LVS_NOCOLUMNHEADER, _
        True, _ ; True (add) or False (remove)...
        False) ; True = ...the extended style or False = style
EndFunc

Func _resizeLVColsAdlib()
    _resizeLVCols()
    AdlibUnRegister("_resizeLVColsAdlib")
EndFunc

Func _reorderLVColsAdlib()
    _reorderLVCols()
    AdlibUnRegister("_reorderLVColsAdlib")
EndFunc

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_BEGINDRAG ; Sent by a header control when a drag operation has begun on one of its items
                    ;_WM_NOTIFY_DebugEvent("$HDN_BEGINDRAG", $tagNMHEADER, $lParam, "IDFrom,,Item,Button")
                    Return False ; To allow the header control to automatically manage drag-and-drop operations
                    ; Return True  ; To indicate external (manual) drag-and-drop management allows the owner of the
                    ; control to provide custom services as part of the drag-and-drop process
                Case $HDN_ENDTRACK, $HDN_ENDTRACKW
                    AdlibRegister("_resizeLVColsAdlib", 5)
                    ;_resizeLVCols()
                Case $HDN_ENDDRAG ; Sent by a header control when a drag operation has ended on one of its items
                    ;_WM_NOTIFY_DebugEvent("$HDN_ENDDRAG", $tagNMHEADER, $lParam, "IDFrom,,Item,Button")
                    AdlibRegister("_reorderLVColsAdlib", 5)
                    Return False ; To allow the control to automatically place and reorder the item
                    ; Return True  ; To prevent the item from being placed
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY

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

 

Edited by WildByDesign
Posted

Hello. I was trying your penultimate script above and could improve it a bit (no more Adib but $HDN_ENDTRACK changed to $HDN_ITEMCHANGED, added  _GUICtrlHeader_Destroy) etc...

The problem with a separate header control is that you'll have to add plenty of code in your script to get (nearly) the same results as a native Listview control (which takes care of its native header items), this means sorting listview columns by clicking their headers, changing header items order by dragging a header item at the left or right of another, double-clicking a header separator etc...)

For example, in my script below, please notice what will happen when you enlarge a header item : an horizontal scrollbar will appear in the listview. Now when you scroll horizontally, the headers items won't align the listview columns, so you'll have to take care of this part too etc...

Of course all these points could be fixed with additional code, it's up to you to decide if you're ready to add all this code to avoid flicker.

#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>
#include <GuiListView.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
Global $g_hHeader, $g_idListview

Example()

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

    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)
    _GUICtrlHeader_AddItem($g_hHeader, "Column1", 100)
    _GUICtrlHeader_AddItem($g_hHeader, "Column2", 100)
    _GUICtrlHeader_AddItem($g_hHeader, "Column3", 100)

    $g_idListview = GUICtrlCreateListView("col1|col2|col3  ", 0, 24, 320, 295, BitOR($GUI_SS_DEFAULT_LISTVIEW, $LVS_NOCOLUMNHEADER), BitOR($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT))
    For $i = 1 To 30
        GUICtrlCreateListViewItem("item" & $i & "|item" & $i  & "|item" & $i, $g_idListview)
    Next

    ; resize listview columns to match header widths
    _resizeLVCols()

    GUISetState(@SW_SHOW, $g_hChild)

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

    GUIRegisterMsg($WM_NOTIFY, WM_NOTIFY)

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

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

Func _resizeLVCols()
    For $i = 0 To _GUICtrlHeader_GetItemCount($g_hHeader) - 1
        _GUICtrlListView_SetColumnWidth($g_idListview, $i, _GUICtrlHeader_GetItemWidth($g_hHeader, $i))
    Next
EndFunc   ;==>_resizeLVCols

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_ITEMCHANGED, $HDN_ITEMCHANGEDW
                    _resizeLVCols()
            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."

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
  • Recently Browsing   0 members

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