Docking Windows example?

Started by Frank Bruebach, September 11, 2025, 04:37:51 PM

Previous topic - Next topic

Frank Bruebach

Hello Jose. Did you have create already a docking window and perhaps with tabbed MDI style with afxnova ?

José Roca


Frank Bruebach


hello jose, here I show a simple little docking Window example (winapi pure),
click the panel via mouse And move it To another place
you can make also many different controls and move it ... later per drag and drop too..
perhaps you can use it for afxnova..

regards, frank

' docking window winapi, 12-09-2025, frank bruebach
' freebasic

#Include "windows.bi"

Dim Shared hDock As HWND
Dim Shared hClient As HWND
Dim Shared isDragging As BOOL = FALSE
Dim Shared dragOffsetX As Integer, dragOffsetY As Integer

Const DOCK_WIDTH  = 240
Const DOCK_HEIGHT = 160

Enum DockPos
    DOCK_NONE
    DOCK_LEFT
    DOCK_RIGHT
    DOCK_TOP
    DOCK_BOTTOM
    DOCK_FLOAT
End Enum

Dim Shared dockState As DockPos = DOCK_LEFT

Function WndProc(hWnd As HWND, msg As UINT, wParam As WPARAM, lParam As LPARAM) As LRESULT
    Select Case msg
        Case WM_CREATE
            ' Dockbares Panel
            hDock = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "Dock Panel move me", _
                        WS_CHILD Or WS_VISIBLE Or SS_CENTER, _
                        20, 20, DOCK_WIDTH, 100, _
                        hWnd, Cast(HMENU, 1001), GetModuleHandle(NULL), NULL)

            ' Client Bereich
            hClient = CreateWindowEx(0, "STATIC", "Client Area", _
                        WS_CHILD Or WS_VISIBLE Or SS_CENTER, _
                        10, 10, 200, 200, _
                        hWnd, 0, GetModuleHandle(NULL), NULL)
            Return 0

        Case WM_SIZE
            Dim cx As Integer = LoWord(lParam)
            Dim cy As Integer = HiWord(lParam)

            Select Case dockState
                Case DOCK_LEFT
                    MoveWindow(hDock, 0, 0, DOCK_WIDTH, cy, TRUE)
                    MoveWindow(hClient, DOCK_WIDTH, 0, cx-DOCK_WIDTH, cy, TRUE)
                Case DOCK_RIGHT
                    MoveWindow(hDock, cx-DOCK_WIDTH, 0, DOCK_WIDTH, cy, TRUE)
                    MoveWindow(hClient, 0, 0, cx-DOCK_WIDTH, cy, TRUE)
                Case DOCK_TOP
                    MoveWindow(hDock, 0, 0, cx, DOCK_HEIGHT, TRUE)
                    MoveWindow(hClient, 0, DOCK_HEIGHT, cx, cy-DOCK_HEIGHT, TRUE)
                Case DOCK_BOTTOM
                    MoveWindow(hDock, 0, cy-DOCK_HEIGHT, cx, DOCK_HEIGHT, TRUE)
                    MoveWindow(hClient, 0, 0, cx, cy-DOCK_HEIGHT, TRUE)
                'Case DOCK_FLOAT
                '    ' bleibt an aktueller Position
                '    MoveWindow(hClient, 0, 0, cx, cy, TRUE)
            End Select
            Return 0

        Case WM_LBUTTONDOWN
            Dim pt As POINT
            pt.x = LoWord(lParam)
            pt.y = HiWord(lParam)
            Dim rc As RECT
            GetWindowRect(hDock, @rc)
            ScreenToClient(hWnd, Cast(LPPOINT, @rc.Left))
            ScreenToClient(hWnd, Cast(LPPOINT, @rc.Right))
            If PtInRect(@rc, pt) Then
                isDragging = TRUE
                SetCapture(hWnd)
                dragOffsetX = pt.x - rc.Left
                dragOffsetY = pt.y - rc.Top
            EndIf
            Return 0

        Case WM_MOUSEMOVE
            If isDragging Then
                Dim pt As POINT
                pt.x = LoWord(lParam)
                pt.y = HiWord(lParam)
                ' Panel folgt Maus
                MoveWindow(hDock, pt.x - dragOffsetX, pt.y - dragOffsetY, 120, 80, TRUE)
            EndIf
            Return 0

        Case WM_LBUTTONUP
            If isDragging Then
                ReleaseCapture()
                isDragging = FALSE

                ' Dock-Bereich ermitteln
                Dim As Integer cx,cy
                Dim rc As RECT
                GetClientRect(hWnd, @rc)
                cx = rc.Right
                cy = rc.Bottom

                Dim dockRc As RECT
                GetWindowRect(hDock, @dockRc)
                ScreenToClient(hWnd, Cast(LPPOINT, @dockRc.Left))
                ScreenToClient(hWnd, Cast(LPPOINT, @dockRc.Right))

                If dockRc.Left < 50 Then
                    dockState = DOCK_LEFT
                ElseIf dockRc.Right > cx-50 Then
                    dockState = DOCK_RIGHT
                ElseIf dockRc.Top < 50 Then
                    dockState = DOCK_TOP
                ElseIf dockRc.Bottom > cy-50 Then
                    dockState = DOCK_BOTTOM
                Else
                    dockState = DOCK_FLOAT
                EndIf

                ' neu zeichnen
                SendMessage(hWnd, WM_SIZE, 0, cy Shl 16 Or cx)
            EndIf
            Return 0

        Case WM_DESTROY
            PostQuitMessage(0)
            Return 0
    End Select
    Return DefWindowProc(hWnd, msg, wParam, lParam)
End Function


Function WinMain(hInstance As HINSTANCE, hPrevInstance As HINSTANCE, szCmdLine As String, nCmdShow As Integer) As Integer
    Dim wc As WNDCLASS
    Dim msg As MSG
    Dim hWnd As HWND

    wc.style = CS_HREDRAW Or CS_VREDRAW
    wc.lpfnWndProc = @WndProc
    wc.cbClsExtra = 0
    wc.cbWndExtra = 0
    wc.hInstance = hInstance
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION)
    wc.hCursor = LoadCursor(NULL, IDC_ARROW)
    wc.hbrBackground = GetStockObject(WHITE_BRUSH)
    wc.lpszMenuName = NULL
    wc.lpszClassName = @"DockTest"

    If RegisterClass(@wc) = FALSE Then Return 0

    hWnd = CreateWindowEx(0, @"DockTest", "Docking Drag Example", WS_OVERLAPPEDWINDOW, _
                          CW_USEDEFAULT, CW_USEDEFAULT, 700, 500, _
                          NULL, NULL, hInstance, NULL)

    ShowWindow(hWnd, nCmdShow)
    UpdateWindow(hWnd)

    While GetMessage(@msg, NULL, 0, 0) <> FALSE
        TranslateMessage(@msg)
        DispatchMessage(@msg)
    Wend

    Return msg.wParam
End Function


End WinMain(GetModuleHandle(NULL), NULL, Command(), SW_NORMAL)
'ends

Frank Bruebach

#3
hello jose, perhaps you can check my afxnova..docking window test example.
something is wrong or missing. cannot see the panel how I wished.
it's not possible to add controls in wndproc()  below wm_create part?
code example isnt perfect, I know..

regards, frank

' docking window test with tiko & afxnova
'
#define UNICODE
#INCLUDE ONCE "AfxNova/CWindow.inc"
USING AfxNova

DECLARE FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                           BYVAL hPrevInstance AS HINSTANCE, _
                           BYVAL pwszCmdLine AS WSTRING PTR, _
                           BYVAL nCmdShow AS LONG) AS LONG
   END wWinMain(GetModuleHandleW(NULL), NULL, wCommand(), SW_NORMAL)

' // Forward declaration
DECLARE FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

CONST IDC_DOCK1 = 1003
CONST IDC_DOCK2 = 1004

' ========================================================================================
' Docking Support (Globals)
' ========================================================================================
Enum DockPos
    DOCK_LEFT
    DOCK_RIGHT
    DOCK_TOP
    DOCK_BOTTOM
    DOCK_FLOAT
End Enum

Dim Shared dockState As DockPos = DOCK_LEFT
Dim Shared hDock As HWND
Dim Shared hClient As HWND
Dim Shared isDragging As Boolean
Dim Shared dragOffsetX As Integer, dragOffsetY As Integer

Const DOCK_WIDTH  = 240
Const DOCK_HEIGHT = 160
' ========================================================================================

' ========================================================================================
' Main
' ========================================================================================
FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                   BYVAL hPrevInstance AS HINSTANCE, _
                   BYVAL pwszCmdLine AS WSTRING PTR, _
                   BYVAL nCmdShow AS LONG) AS LONG

   ' // Set process DPI aware
   SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
   ' // Enable visual styles without including a manifest file
   AfxEnableVisualStyles

   ' // Creates the main window
   DIM pWindow AS CWindow = "MyClassName"   ' Use the name you wish
   DIM hWin AS HWND = pWindow.Create(NULL, "CWindow - Docking Drag Example", @WndProc)
   ' // Sizes it by setting the wanted width and height of its client area
   pWindow.SetClientSize(500, 320)
   ' // Centers the window
   pWindow.Center
   ' // Set the main window background color
   pWindow.SetBackColor(RGB_GOLD)

   ' Add a dockable Panel as Control
         DIM hDock AS HWND  = pWindow.AddControl("Static", hWin, IDC_DOCK1, "Dock Panel move me", _
                                    20, 20, DOCK_WIDTH, DOCK_HEIGHT)

         ' Client Area als Control
         hClient = pWindow.AddControl("Static", hWin, IDC_DOCK2, "Client Area", _
                                      150, 20, 200, 200)
   ' // Anchor the controls
   pWindow.AnchorControl(hWin, IDC_DOCK1, AFX_ANCHOR_WIDTH)
   pWindow.AnchorControl(hWin, IDC_DOCK2, AFX_ANCHOR_HEIGHT_WIDTH)
   ' Add a dockable Panel as Control ends

   ' // Adds a button
   pWindow.AddControl("Button", hWin, IDCANCEL, "&Close", 305, 270, 75, 30)
   ' // Anchors the button to the bottom and the right side of the main window
   pWindow.AnchorControl(IDCANCEL, AFX_ANCHOR_BOTTOM_RIGHT)

   ' // Set the focus In the edit control
   SetFocus hDock '


   ' // Displays the window and dispatches the Windows messages
   FUNCTION = pWindow.DoEvents(nCmdShow)

END FUNCTION
' ========================================================================================


' ========================================================================================
' Main window procedure (adapted for Docking + AfxNova)
' ========================================================================================
FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
   DIM pWindow AS CWindow = "MyClassName"
   
   SELECT CASE uMsg

      CASE WM_CREATE
         
         ' Dockbares Panel als Control, this control below doesn't work why not?
         DIM hDock AS HWND  = pWindow.AddControl("Static", hwnd, IDC_DOCK1, "Dock Panel move me", _
                                    20, 20, DOCK_WIDTH, DOCK_HEIGHT)

         ' Client Area als Control
         hClient = pWindow.AddControl("Static", hwnd, IDC_DOCK2, "Client Area", _
                                      150, 20, 200, 200)
         'RETURN 0

      CASE WM_SIZE
         Dim cx As Integer = LoWord(lParam)
         Dim cy As Integer = HiWord(lParam)

         SELECT CASE dockState
            CASE DOCK_LEFT
               MoveWindow(hDock, 0, 0, DOCK_WIDTH, cy, TRUE)
               MoveWindow(hClient, DOCK_WIDTH, 0, cx-DOCK_WIDTH, cy, TRUE)
            CASE DOCK_RIGHT
               MoveWindow(hDock, cx-DOCK_WIDTH, 0, DOCK_WIDTH, cy, TRUE)
               MoveWindow(hClient, 0, 0, cx-DOCK_WIDTH, cy, TRUE)
            CASE DOCK_TOP
               MoveWindow(hDock, 0, 0, cx, DOCK_HEIGHT, TRUE)
               MoveWindow(hClient, 0, DOCK_HEIGHT, cx, cy-DOCK_HEIGHT, TRUE)
            CASE DOCK_BOTTOM
               MoveWindow(hDock, 0, cy-DOCK_HEIGHT, cx, DOCK_HEIGHT, TRUE)
               MoveWindow(hClient, 0, 0, cx, cy-DOCK_HEIGHT, TRUE)
            CASE DOCK_FLOAT
               ' bleibt frei beweglich
         END SELECT
         RETURN 0

      CASE WM_LBUTTONDOWN
         Dim pt As POINT
         pt.x = LoWord(lParam)
         pt.y = HiWord(lParam)
         Dim rc As RECT
         GetWindowRect(hDock, @rc)
         ScreenToClient(hwnd, Cast(LPPOINT, @rc.Left))
         ScreenToClient(hwnd, Cast(LPPOINT, @rc.Right))
         IF PtInRect(@rc, pt) THEN
            isDragging = TRUE
            SetCapture(hwnd)
            dragOffsetX = pt.x - rc.Left
            dragOffsetY = pt.y - rc.Top
         END IF
         RETURN 0

      CASE WM_MOUSEMOVE
         IF isDragging THEN
            Dim pt As POINT
            pt.x = LoWord(lParam)
            pt.y = HiWord(lParam)
            MoveWindow(hDock, pt.x - dragOffsetX, pt.y - dragOffsetY, DOCK_WIDTH, DOCK_HEIGHT, TRUE)
         END IF
         RETURN 0

      CASE WM_LBUTTONUP
         IF isDragging THEN
            ReleaseCapture()
            isDragging = FALSE

            Dim rcWin As RECT
            GetClientRect(hwnd, @rcWin)
            Dim as integer cx = rcWin.Right
            Dim as integer cy = rcWin.Bottom

            Dim dockRc As RECT
            GetWindowRect(hDock, @dockRc)
            ScreenToClient(hwnd, Cast(LPPOINT, @dockRc.Left))
            ScreenToClient(hwnd, Cast(LPPOINT, @dockRc.Right))

            IF dockRc.Left < 50 THEN
               dockState = DOCK_LEFT
            ELSEIF dockRc.Right > cx-50 THEN
               dockState = DOCK_RIGHT
            ELSEIF dockRc.Top < 50 THEN
               dockState = DOCK_TOP
            ELSEIF dockRc.Bottom > cy-50 THEN
               dockState = DOCK_BOTTOM
            ELSE
               dockState = DOCK_FLOAT
            END IF

            ' neu layouten
            SendMessage(hwnd, WM_SIZE, 0, cy Shl 16 Or cx)
         END IF
         RETURN 0

      CASE WM_DESTROY
         PostQuitMessage(0)
         RETURN 0

   END SELECT

   FUNCTION = DefWindowProcW(hwnd, uMsg, wParam, lParam)

END FUNCTION


José Roca

I also have fixed the repainting problems of the original SDK code.

' docking window
' freebasic

#Include "windows.bi"
#Include "AfxNova/CWindow.inc"

Dim Shared hDock As HWND
Dim Shared hClient As HWND
Dim Shared isDragging As BOOL = FALSE
Dim Shared dragOffsetX As Integer, dragOffsetY As Integer

Const DOCK_WIDTH  = 240
Const DOCK_HEIGHT = 160

Enum DockPos
    DOCK_NONE
    DOCK_LEFT
    DOCK_RIGHT
    DOCK_TOP
    DOCK_BOTTOM
    DOCK_FLOAT
End Enum

Dim Shared dockState As DockPos = DOCK_LEFT

Function WndProc(BYVAL hWnd As HWND, BYVAL msg As UINT, BYVAL wParam As WPARAM, BYVAL lParam As LPARAM) As LRESULT
    Select Case msg
        Case WM_CREATE
             AfxEnableDarkModeForWindow(hwnd)

             ' Dockbares Panel
             DIM pWindow AS CWindow PTR = AfxCWindowPtr(lParam)
             IF pWindow THEN
               hDock = pWindow->AddControl("Static", hWnd, 1001, "Dock Panel move me", _
                                    20, 20, DOCK_WIDTH, DOCK_HEIGHT, WS_CHILD Or WS_VISIBLE Or SS_CENTER, WS_EX_CLIENTEDGE)

               ' Client Area als Control
               hClient = pWindow->AddControl("Static", hWnd, 0, "Client Area", _
                                      220, 20, 260, DOCK_HEIGHT, WS_CHILD Or WS_VISIBLE Or SS_CENTER, WS_EX_CLIENTEDGE)

             END IF

            Return 0

        ' // Sent when the user selects a command item from a menu, when a control sends a
        ' // notification message to its parent window, or when an accelerator keystroke is translated.
        CASE WM_COMMAND
           SELECT CASE CBCTL(wParam, lParam)
              CASE IDCANCEL
                 ' // If ESC key pressed, close the application by sending an WM_CLOSE message
                 IF CBCTLMSG(wParam, lParam) = BN_CLICKED THEN SendMessageW(hwnd, WM_CLOSE, 0, 0)
           END SELECT
           RETURN 0

        Case WM_SIZE
            Dim cx As Integer = LoWord(lParam)
            Dim cy As Integer = HiWord(lParam)

            Select Case dockState
                Case DOCK_LEFT
                    MoveWindow(hDock, 0, 0, DOCK_WIDTH, cy, TRUE)
                    MoveWindow(hClient, DOCK_WIDTH, 0, cx-DOCK_WIDTH, cy, TRUE)
                    InvalidateRect(hDock, NULL, TRUE)
                    UpdateWindow(hDock)
                    InvalidateRect(hClient, NULL, TRUE)
                    UpdateWindow(hClient)
                Case DOCK_RIGHT
                    MoveWindow(hDock, cx-DOCK_WIDTH, 0, DOCK_WIDTH, cy, TRUE)
                    MoveWindow(hClient, 0, 0, cx-DOCK_WIDTH, cy, TRUE)
                    InvalidateRect(hDock, NULL, TRUE)
                    UpdateWindow(hDock)
                    InvalidateRect(hClient, NULL, TRUE)
                    UpdateWindow(hClient)
                Case DOCK_TOP
                    MoveWindow(hDock, 0, 0, cx, DOCK_HEIGHT, TRUE)
                    MoveWindow(hClient, 0, DOCK_HEIGHT, cx, cy-DOCK_HEIGHT, TRUE)
                    InvalidateRect(hDock, NULL, TRUE)
                    UpdateWindow(hDock)
                    InvalidateRect(hClient, NULL, TRUE)
                    UpdateWindow(hClient)
                Case DOCK_BOTTOM
                    Dim clientHeight As Integer = cy - DOCK_HEIGHT
                    If clientHeight < 0 Then clientHeight = 0 ' Prevent negative height
                    MoveWindow(hDock, 0, clientHeight, cx, DOCK_HEIGHT, TRUE)
                    MoveWindow(hClient, 0, 0, cx, clientHeight, TRUE)
                    InvalidateRect(hDock, NULL, TRUE)
                    UpdateWindow(hDock)
                    InvalidateRect(hClient, NULL, TRUE)
                    UpdateWindow(hClient)
                'Case DOCK_FLOAT
                '    ' bleibt an aktueller Position
                '    MoveWindow(hClient, 0, 0, cx, cy, TRUE)
            End Select
            Return 0

        Case WM_LBUTTONDOWN
            Dim pt As POINT
            pt.x = LoWord(lParam)
            pt.y = HiWord(lParam)
            Dim rc As RECT
            GetWindowRect(hDock, @rc)
            ScreenToClient(hWnd, Cast(LPPOINT, @rc.Left))
            ScreenToClient(hWnd, Cast(LPPOINT, @rc.Right))
            If PtInRect(@rc, pt) Then
                isDragging = TRUE
                SetCapture(hWnd)
                dragOffsetX = pt.x - rc.Left
                dragOffsetY = pt.y - rc.Top
            EndIf
            Return 0

        Case WM_MOUSEMOVE
'            If isDragging Then
'                Dim pt As POINT
'                pt.x = LoWord(lParam)
'                pt.y = HiWord(lParam)
'                ' Panel folgt Maus
'                MoveWindow(hDock, pt.x - dragOffsetX, pt.y - dragOffsetY, 120, 80, TRUE)
'            EndIf
'            Return 0

            If isDragging Then
               Dim pt As POINT
               pt.x = LoWord(lParam)
               pt.y = HiWord(lParam)
               Dim rcDock As RECT
               GetWindowRect(hDock, @rcDock)
               ScreenToClient(hWnd, Cast(LPPOINT, @rcDock.Left))
               ScreenToClient(hWnd, Cast(LPPOINT, @rcDock.Right))
               Dim dockWidth As Integer = rcDock.Right - rcDock.Left
               Dim dockHeight As Integer = rcDock.Bottom - rcDock.Top
               MoveWindow(hDock, pt.x - dragOffsetX, pt.y - dragOffsetY, dockWidth, dockHeight, TRUE)
               InvalidateRect(hDock, NULL, TRUE)
               UpdateWindow(hDock)
            EndIf
            Return 0

        Case WM_LBUTTONUP
            If isDragging Then
                ReleaseCapture()
                isDragging = FALSE

                ' Dock-Bereich ermitteln
                Dim As Integer cx,cy
                Dim rc As RECT
                GetClientRect(hWnd, @rc)
                cx = rc.Right
                cy = rc.Bottom

                Dim dockRc As RECT
                GetWindowRect(hDock, @dockRc)
                ScreenToClient(hWnd, Cast(LPPOINT, @dockRc.Left))
                ScreenToClient(hWnd, Cast(LPPOINT, @dockRc.Right))

'                If dockRc.Left < 50 Then
'                    dockState = DOCK_LEFT
'                ElseIf dockRc.Right > cx - AfxScaleX(50) Then
'                    dockState = DOCK_RIGHT
'                ElseIf dockRc.Top < AfxScaleX(50) Then
'                    dockState = DOCK_TOP
'                ElseIf dockRc.Bottom > cy - AfxScaleY(50) Then
'                    dockState = DOCK_BOTTOM
'                Else
'                    dockState = DOCK_FLOAT
'                EndIf

                Dim edgeThresholdX As LONG = cx \ 10  ' 10% of width
                Dim edgeThresholdY As LONG = cy \ 10  ' 10% of height

                If dockRc.Left < edgeThresholdX Then
                    dockState = DOCK_LEFT
                ElseIf dockRc.Right > cx - edgeThresholdX Then
                    dockState = DOCK_RIGHT
                ElseIf dockRc.Top < edgeThresholdY Then
                    dockState = DOCK_TOP
                ElseIf dockRc.Bottom > cy - edgeThresholdY Then
                    dockState = DOCK_BOTTOM
                Else
                    dockState = DOCK_FLOAT
                End If


                ' neu zeichnen
                SendMessage(hWnd, WM_SIZE, 0, cy Shl 16 Or cx)
            EndIf
            Return 0

        Case WM_DESTROY
            PostQuitMessage(0)
            Return 0
    End Select
    Return DefWindowProc(hWnd, msg, wParam, lParam)
End Function


Function WinMain(BYVAL hInstance As HINSTANCE, BYVAL hPrevInstance As HINSTANCE, BYREF szCmdLine As String, BYVAL nCmdShow As Integer) As Integer
    Dim wc As WNDCLASS
    Dim msg As MSG
    Dim hWnd As HWND

   ' // Set process DPI aware
   SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
   ' // Enable visual styles without including a manifest file
   AfxEnableVisualStyles

'    wc.style = CS_HREDRAW Or CS_VREDRAW
'    wc.lpfnWndProc = @WndProc
'    wc.cbClsExtra = 0
'    wc.cbWndExtra = 0
'    wc.hInstance = hInstance
'    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION)
'    wc.hCursor = LoadCursor(NULL, IDC_ARROW)
'    wc.hbrBackground = GetStockObject(WHITE_BRUSH)
'    wc.lpszMenuName = NULL
'    wc.lpszClassName = @"DockTest"

'    If RegisterClass(@wc) = FALSE Then Return 0

'    hWnd = CreateWindowEx(0, "DockTest", "Docking Drag Example", WS_OVERLAPPEDWINDOW, _
'                          CW_USEDEFAULT, CW_USEDEFAULT, 700, 500, _
'                          NULL, NULL, hInstance, NULL)

   ' // Creates the main window
   DIM pWindow AS CWindow = "DockTest"
   hwnd = pWindow.Create(NULL, "Docking Drag Example", @WndProc, CW_USEDEFAULT, CW_USEDEFAULT, 700, 500)
   pWindow.SetBackColor(RGB_GOLD)
   pWindow.Center

'    ShowWindow(hWnd, nCmdShow)
'    UpdateWindow(hWnd)

'    While GetMessage(@msg, NULL, 0, 0) <> FALSE
'        TranslateMessage(@msg)
'        DispatchMessage(@msg)
'    Wend

'    Return msg.wParam

   ' // Displays the window and dispatches the Windows messages
   FUNCTION = pWindow.DoEvents(nCmdShow)

End Function

End WinMain(GetModuleHandle(NULL), NULL, Command(), SW_NORMAL)
'ends

José Roca

#5
BTW I have put the creation of the controls in WM_CREATE because you have asked if it could be done, but it is easier to do it in Winmain:

   ' Dockbar Panel
   hDock = pWindow.AddControl("Static", hWnd, 1001, "Dock Panel move me", _
                              0, 0, DOCK_WIDTH, DOCK_HEIGHT, WS_CHILD Or WS_VISIBLE Or SS_CENTER, WS_EX_CLIENTEDGE)

   ' Client Area control
   hClient = pWindow.AddControl("Static", hWnd, 0, "Client Area", _
                                220, 0, 260, DOCK_HEIGHT, WS_CHILD Or WS_VISIBLE Or SS_CENTER, WS_EX_CLIENTEDGE)

Also don't anchor controls that you are going to resize manually in WM_SIZE.


Frank Bruebach

many thanks jose your example works fine.
unfortunately not my example  below. the winmain part I suppose isnt perfect.
I cannot drag the panel via mouse click to another place.

' docking window, sdk, part two
' freebasic
#define UNICODE
#Include "windows.bi"
#INCLUDE ONCE "AfxNova/CWindow.inc"
USING AfxNova

DECLARE FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                           BYVAL hPrevInstance AS HINSTANCE, _
                           BYVAL pwszCmdLine AS WSTRING PTR, _
                           BYVAL nCmdShow AS LONG) AS LONG
   END wWinMain(GetModuleHandleW(NULL), NULL, wCommand(), SW_NORMAL)

' // Forward declaration
DECLARE FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

Dim Shared hDock As HWND
Dim Shared hClient As HWND
Dim Shared isDragging As BOOL = FALSE
Dim Shared dragOffsetX As Integer, dragOffsetY As Integer

Const DOCK_WIDTH  = 240
Const DOCK_HEIGHT = 160

Enum DockPos
    DOCK_NONE
    DOCK_LEFT
    DOCK_RIGHT
    DOCK_TOP
    DOCK_BOTTOM
    DOCK_FLOAT
End Enum

Dim Shared dockState As DockPos = DOCK_LEFT

FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                   BYVAL hPrevInstance AS HINSTANCE, _
                   BYVAL pwszCmdLine AS WSTRING PTR, _
                   BYVAL nCmdShow AS LONG) AS LONG

   ' // Set process DPI aware
   SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
   ' // Enable visual styles without including a manifest file
   AfxEnableVisualStyles

   ' // Creates the main window
   DIM pWindow AS CWindow = "DockTest"  ' Use the name you wish
   DIM hWin AS HWND = pWindow.Create(NULL, "Docking Drag Example", @WndProc,CW_USEDEFAULT, CW_USEDEFAULT )
   ' // Sizes it by setting the wanted width and height of its client area
   pWindow.SetClientSize(500, 320)
   pWindow.SetBackColor(RGB_GOLD)

   ' // Centers the window
   pWindow.Center

   ' Dockbares Panel
   pWindow.AddControl("Static", hWin, 1001, "Dock Panel move me", _
                                    20, 20, DOCK_WIDTH, DOCK_HEIGHT, WS_CHILD Or WS_VISIBLE Or SS_CENTER, WS_EX_CLIENTEDGE)

   ' Client Area als Control
   pWindow.AddControl("Static", hWin, 0, "Client Area", _
                                      220, 20, 260, DOCK_HEIGHT, WS_CHILD Or WS_VISIBLE Or SS_CENTER, WS_EX_CLIENTEDGE)

   ' // Adds a button
   pWindow.AddControl("Button", hWin, IDCANCEL, "&Close", 270, 255, 75, 30)
   ' // Anchors the button to the bottom and the right side of the main window
   pWindow.AnchorControl(IDCANCEL, AFX_ANCHOR_BOTTOM_RIGHT)


   ' // Displays the window and dispatches the Windows messages
   FUNCTION = pWindow.DoEvents(nCmdShow)

END FUNCTION
'End WinMain(GetModuleHandle(NULL), NULL, Command(), SW_NORMAL)


Function WndProc(BYVAL hWnd As HWND, BYVAL msg As UINT, BYVAL wParam As WPARAM, BYVAL lParam As LPARAM) As LRESULT
    Select Case msg
        Case WM_CREATE
             
        ' // Sent when the user selects a command item from a menu, when a control sends a
        ' // notification message to its parent window, or when an accelerator keystroke is translated.
        CASE WM_COMMAND
           SELECT CASE CBCTL(wParam, lParam)
              CASE IDCANCEL
                 ' // If ESC key pressed, close the application by sending an WM_CLOSE message
                 IF CBCTLMSG(wParam, lParam) = BN_CLICKED THEN SendMessageW(hwnd, WM_CLOSE, 0, 0)
           END SELECT
           RETURN 0

        Case WM_SIZE
            Dim cx As Integer = LoWord(lParam)
            Dim cy As Integer = HiWord(lParam)

            Select Case dockState
                Case DOCK_LEFT
                    MoveWindow(hDock, 0, 0, DOCK_WIDTH, cy, TRUE)
                    MoveWindow(hClient, DOCK_WIDTH, 0, cx-DOCK_WIDTH, cy, TRUE)
                    InvalidateRect(hDock, NULL, TRUE)
                    UpdateWindow(hDock)
                    InvalidateRect(hClient, NULL, TRUE)
                    UpdateWindow(hClient)
                Case DOCK_RIGHT
                    MoveWindow(hDock, cx-DOCK_WIDTH, 0, DOCK_WIDTH, cy, TRUE)
                    MoveWindow(hClient, 0, 0, cx-DOCK_WIDTH, cy, TRUE)
                    InvalidateRect(hDock, NULL, TRUE)
                    UpdateWindow(hDock)
                    InvalidateRect(hClient, NULL, TRUE)
                    UpdateWindow(hClient)
                Case DOCK_TOP
                    MoveWindow(hDock, 0, 0, cx, DOCK_HEIGHT, TRUE)
                    MoveWindow(hClient, 0, DOCK_HEIGHT, cx, cy-DOCK_HEIGHT, TRUE)
                    InvalidateRect(hDock, NULL, TRUE)
                    UpdateWindow(hDock)
                    InvalidateRect(hClient, NULL, TRUE)
                    UpdateWindow(hClient)
                Case DOCK_BOTTOM
                    Dim clientHeight As Integer = cy - DOCK_HEIGHT
                    If clientHeight < 0 Then clientHeight = 0 ' Prevent negative height
                    MoveWindow(hDock, 0, clientHeight, cx, DOCK_HEIGHT, TRUE)
                    MoveWindow(hClient, 0, 0, cx, clientHeight, TRUE)
                    InvalidateRect(hDock, NULL, TRUE)
                    UpdateWindow(hDock)
                    InvalidateRect(hClient, NULL, TRUE)
                    UpdateWindow(hClient)
                'Case DOCK_FLOAT
                '    ' bleibt an aktueller Position
                '    MoveWindow(hClient, 0, 0, cx, cy, TRUE)
            End Select
            Return 0

        Case WM_LBUTTONDOWN
            Dim pt As POINT
            pt.x = LoWord(lParam)
            pt.y = HiWord(lParam)
            Dim rc As RECT
            GetWindowRect(hDock, @rc)
            ScreenToClient(hWnd, Cast(LPPOINT, @rc.Left))
            ScreenToClient(hWnd, Cast(LPPOINT, @rc.Right))
            If PtInRect(@rc, pt) Then
                isDragging = TRUE
                SetCapture(hWnd)
                dragOffsetX = pt.x - rc.Left
                dragOffsetY = pt.y - rc.Top
            EndIf
            Return 0

        Case WM_MOUSEMOVE
'            If isDragging Then
'                Dim pt As POINT
'                pt.x = LoWord(lParam)
'                pt.y = HiWord(lParam)
'                ' Panel folgt Maus
'                MoveWindow(hDock, pt.x - dragOffsetX, pt.y - dragOffsetY, 120, 80, TRUE)
'            EndIf
'            Return 0

            If isDragging Then
               Dim pt As POINT
               pt.x = LoWord(lParam)
               pt.y = HiWord(lParam)
               Dim rcDock As RECT
               GetWindowRect(hDock, @rcDock)
               ScreenToClient(hWnd, Cast(LPPOINT, @rcDock.Left))
               ScreenToClient(hWnd, Cast(LPPOINT, @rcDock.Right))
               Dim dockWidth As Integer = rcDock.Right - rcDock.Left
               Dim dockHeight As Integer = rcDock.Bottom - rcDock.Top
               MoveWindow(hDock, pt.x - dragOffsetX, pt.y - dragOffsetY, dockWidth, dockHeight, TRUE)
               InvalidateRect(hDock, NULL, TRUE)
               UpdateWindow(hDock)
            EndIf
            Return 0

        Case WM_LBUTTONUP
            If isDragging Then
                ReleaseCapture()
                isDragging = FALSE

                ' Dock-Bereich ermitteln
                Dim As Integer cx,cy
                Dim rc As RECT
                GetClientRect(hWnd, @rc)
                cx = rc.Right
                cy = rc.Bottom

                Dim dockRc As RECT
                GetWindowRect(hDock, @dockRc)
                ScreenToClient(hWnd, Cast(LPPOINT, @dockRc.Left))
                ScreenToClient(hWnd, Cast(LPPOINT, @dockRc.Right))

'                If dockRc.Left < 50 Then
'                    dockState = DOCK_LEFT
'                ElseIf dockRc.Right > cx - AfxScaleX(50) Then
'                    dockState = DOCK_RIGHT
'                ElseIf dockRc.Top < AfxScaleX(50) Then
'                    dockState = DOCK_TOP
'                ElseIf dockRc.Bottom > cy - AfxScaleY(50) Then
'                    dockState = DOCK_BOTTOM
'                Else
'                    dockState = DOCK_FLOAT
'                EndIf

                Dim edgeThresholdX As LONG = cx \ 10  ' 10% of width
                Dim edgeThresholdY As LONG = cy \ 10  ' 10% of height

                If dockRc.Left < edgeThresholdX Then
                    dockState = DOCK_LEFT
                ElseIf dockRc.Right > cx - edgeThresholdX Then
                    dockState = DOCK_RIGHT
                ElseIf dockRc.Top < edgeThresholdY Then
                    dockState = DOCK_TOP
                ElseIf dockRc.Bottom > cy - edgeThresholdY Then
                    dockState = DOCK_BOTTOM
                Else
                    dockState = DOCK_FLOAT
                End If


                ' neu zeichnen
                SendMessage(hWnd, WM_SIZE, 0, cy Shl 16 Or cx)
            EndIf
            Return 0

        Case WM_DESTROY
            PostQuitMessage(0)
            Return 0
    End Select
    Return DefWindowProc(hWnd, msg, wParam, lParam)
End Function

'ends

José Roca

#7
I have posted

   ' Dockbar Panel
   hDock = pWindow.AddControl("Static", hWnd, 1001, "Dock Panel move me", _
                              0, 0, DOCK_WIDTH, DOCK_HEIGHT, WS_CHILD Or WS_VISIBLE Or SS_CENTER, WS_EX_CLIENTEDGE)

   ' Client Area control
   hClient = pWindow.AddControl("Static", hWnd, 0, "Client Area", _
                                220, 0, 260, DOCK_HEIGHT, WS_CHILD Or WS_VISIBLE Or SS_CENTER, WS_EX_CLIENTEDGE)

and you are using

   ' Dockbares Panel
   pWindow.AddControl("Static", hWin, 1001, "Dock Panel move me", _
                                    20, 20, DOCK_WIDTH, DOCK_HEIGHT, WS_CHILD Or WS_VISIBLE Or SS_CENTER, WS_EX_CLIENTEDGE)

   ' Client Area als Control
   pWindow.AddControl("Static", hWin, 0, "Client Area", _
                                      220, 20, 260, DOCK_HEIGHT, WS_CHILD Or WS_VISIBLE Or SS_CENTER, WS_EX_CLIENTEDGE)

Doing that, hDock and hClient are null handles.


Also if you change the size of the controls, you will have to change also the code that moves and resizes the controls.

Frank Bruebach

Thanks I See and have fixed my Problem now all OK with my example

' docking window, sdk, part three, GO
' freebasic with tiko editor
'
#define UNICODE
#Include "windows.bi"
#INCLUDE ONCE "AfxNova/CWindow.inc"
USING AfxNova

DECLARE FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                           BYVAL hPrevInstance AS HINSTANCE, _
                           BYVAL pwszCmdLine AS WSTRING PTR, _
                           BYVAL nCmdShow AS LONG) AS LONG
   END wWinMain(GetModuleHandleW(NULL), NULL, wCommand(), SW_NORMAL)

' // Forward declaration
DECLARE FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

Dim Shared hDock As HWND
Dim Shared hClient As HWND
Dim Shared isDragging As BOOL = FALSE
Dim Shared dragOffsetX As Integer, dragOffsetY As Integer

Const DOCK_WIDTH  = 240
Const DOCK_HEIGHT = 160

Enum DockPos
    DOCK_NONE
    DOCK_LEFT
    DOCK_RIGHT
    DOCK_TOP
    DOCK_BOTTOM
    DOCK_FLOAT
End Enum

Dim Shared dockState As DockPos = DOCK_LEFT

FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                   BYVAL hPrevInstance AS HINSTANCE, _
                   BYVAL pwszCmdLine AS WSTRING PTR, _
                   BYVAL nCmdShow AS LONG) AS LONG

   ' // Set process DPI aware
   SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
   ' // Enable visual styles without including a manifest file
   AfxEnableVisualStyles

   ' // Creates the main window
   DIM pWindow AS CWindow = "DockTest"  ' Use the name you wish
   DIM hWin AS HWND = pWindow.Create(NULL, "Docking Drag Example", @WndProc,CW_USEDEFAULT, CW_USEDEFAULT )
   ' // Sizes it by setting the wanted width and height of its client area
   pWindow.SetClientSize(500, 320)
   pWindow.SetBackColor(RGB_GOLD)

   ' // Centers the window
   pWindow.Center

   ' Dockbares Panel
   hDock = pWindow.AddControl("Static", hWin, 1001, "Dock Panel move me", _
                                    20, 20, DOCK_WIDTH, DOCK_HEIGHT, WS_CHILD Or WS_VISIBLE Or SS_CENTER, WS_EX_CLIENTEDGE)

   ' Client Area als Control
   hClient = pWindow.AddControl("Static", hWin, 0, "Client Area", _
                                      220, 20, 260, DOCK_HEIGHT, WS_CHILD Or WS_VISIBLE Or SS_CENTER, WS_EX_CLIENTEDGE)

   ' // Adds a button
   pWindow.AddControl("Button", hWin, IDCANCEL, "&Close", 270, 255, 75, 30)
   ' // Anchors the button to the bottom and the right side of the main window
   pWindow.AnchorControl(IDCANCEL, AFX_ANCHOR_BOTTOM_RIGHT)


   ' // Displays the window and dispatches the Windows messages
   FUNCTION = pWindow.DoEvents(nCmdShow)

END FUNCTION
'End WinMain(GetModuleHandle(NULL), NULL, Command(), SW_NORMAL)


Function WndProc(BYVAL hWnd As HWND, BYVAL msg As UINT, BYVAL wParam As WPARAM, BYVAL lParam As LPARAM) As LRESULT
    Select Case msg
        Case WM_CREATE
             
        ' // Sent when the user selects a command item from a menu, when a control sends a
        ' // notification message to its parent window, or when an accelerator keystroke is translated.
        CASE WM_COMMAND
           SELECT CASE CBCTL(wParam, lParam)
              CASE IDCANCEL
                 ' // If ESC key pressed, close the application by sending an WM_CLOSE message
                 IF CBCTLMSG(wParam, lParam) = BN_CLICKED THEN SendMessageW(hwnd, WM_CLOSE, 0, 0)
           END SELECT
           RETURN 0

        Case WM_SIZE
            Dim cx As Integer = LoWord(lParam)
            Dim cy As Integer = HiWord(lParam)

            Select Case dockState
                Case DOCK_LEFT
                    MoveWindow(hDock, 0, 0, DOCK_WIDTH, cy, TRUE)
                    MoveWindow(hClient, DOCK_WIDTH, 0, cx-DOCK_WIDTH, cy, TRUE)
                    InvalidateRect(hDock, NULL, TRUE)
                    UpdateWindow(hDock)
                    InvalidateRect(hClient, NULL, TRUE)
                    UpdateWindow(hClient)
                Case DOCK_RIGHT
                    MoveWindow(hDock, cx-DOCK_WIDTH, 0, DOCK_WIDTH, cy, TRUE)
                    MoveWindow(hClient, 0, 0, cx-DOCK_WIDTH, cy, TRUE)
                    InvalidateRect(hDock, NULL, TRUE)
                    UpdateWindow(hDock)
                    InvalidateRect(hClient, NULL, TRUE)
                    UpdateWindow(hClient)
                Case DOCK_TOP
                    MoveWindow(hDock, 0, 0, cx, DOCK_HEIGHT, TRUE)
                    MoveWindow(hClient, 0, DOCK_HEIGHT, cx, cy-DOCK_HEIGHT, TRUE)
                    InvalidateRect(hDock, NULL, TRUE)
                    UpdateWindow(hDock)
                    InvalidateRect(hClient, NULL, TRUE)
                    UpdateWindow(hClient)
                Case DOCK_BOTTOM
                    Dim clientHeight As Integer = cy - DOCK_HEIGHT
                    If clientHeight < 0 Then clientHeight = 0 ' Prevent negative height
                    MoveWindow(hDock, 0, clientHeight, cx, DOCK_HEIGHT, TRUE)
                    MoveWindow(hClient, 0, 0, cx, clientHeight, TRUE)
                    InvalidateRect(hDock, NULL, TRUE)
                    UpdateWindow(hDock)
                    InvalidateRect(hClient, NULL, TRUE)
                    UpdateWindow(hClient)
                'Case DOCK_FLOAT
                '    ' bleibt an aktueller Position
                '    MoveWindow(hClient, 0, 0, cx, cy, TRUE)
            End Select
            Return 0

        Case WM_LBUTTONDOWN
            Dim pt As POINT
            pt.x = LoWord(lParam)
            pt.y = HiWord(lParam)
            Dim rc As RECT
            GetWindowRect(hDock, @rc)
            ScreenToClient(hWnd, Cast(LPPOINT, @rc.Left))
            ScreenToClient(hWnd, Cast(LPPOINT, @rc.Right))
            If PtInRect(@rc, pt) Then
                isDragging = TRUE
                SetCapture(hWnd)
                dragOffsetX = pt.x - rc.Left
                dragOffsetY = pt.y - rc.Top
            EndIf
            Return 0

        Case WM_MOUSEMOVE
'            If isDragging Then
'                Dim pt As POINT
'                pt.x = LoWord(lParam)
'                pt.y = HiWord(lParam)
'                ' Panel folgt Maus
'                MoveWindow(hDock, pt.x - dragOffsetX, pt.y - dragOffsetY, 120, 80, TRUE)
'            EndIf
'            Return 0

            If isDragging Then
               Dim pt As POINT
               pt.x = LoWord(lParam)
               pt.y = HiWord(lParam)
               Dim rcDock As RECT
               GetWindowRect(hDock, @rcDock)
               ScreenToClient(hWnd, Cast(LPPOINT, @rcDock.Left))
               ScreenToClient(hWnd, Cast(LPPOINT, @rcDock.Right))
               Dim dockWidth As Integer = rcDock.Right - rcDock.Left
               Dim dockHeight As Integer = rcDock.Bottom - rcDock.Top
               MoveWindow(hDock, pt.x - dragOffsetX, pt.y - dragOffsetY, dockWidth, dockHeight, TRUE)
               InvalidateRect(hDock, NULL, TRUE)
               UpdateWindow(hDock)
            EndIf
            Return 0

        Case WM_LBUTTONUP
            If isDragging Then
                ReleaseCapture()
                isDragging = FALSE

                ' Dock-Bereich ermitteln
                Dim As Integer cx,cy
                Dim rc As RECT
                GetClientRect(hWnd, @rc)
                cx = rc.Right
                cy = rc.Bottom

                Dim dockRc As RECT
                GetWindowRect(hDock, @dockRc)
                ScreenToClient(hWnd, Cast(LPPOINT, @dockRc.Left))
                ScreenToClient(hWnd, Cast(LPPOINT, @dockRc.Right))

'                If dockRc.Left < 50 Then
'                    dockState = DOCK_LEFT
'                ElseIf dockRc.Right > cx - AfxScaleX(50) Then
'                    dockState = DOCK_RIGHT
'                ElseIf dockRc.Top < AfxScaleX(50) Then
'                    dockState = DOCK_TOP
'                ElseIf dockRc.Bottom > cy - AfxScaleY(50) Then
'                    dockState = DOCK_BOTTOM
'                Else
'                    dockState = DOCK_FLOAT
'                EndIf

                Dim edgeThresholdX As LONG = cx \ 10  ' 10% of width
                Dim edgeThresholdY As LONG = cy \ 10  ' 10% of height

                If dockRc.Left < edgeThresholdX Then
                    dockState = DOCK_LEFT
                ElseIf dockRc.Right > cx - edgeThresholdX Then
                    dockState = DOCK_RIGHT
                ElseIf dockRc.Top < edgeThresholdY Then
                    dockState = DOCK_TOP
                ElseIf dockRc.Bottom > cy - edgeThresholdY Then
                    dockState = DOCK_BOTTOM
                Else
                    dockState = DOCK_FLOAT
                End If


                ' neu zeichnen
                SendMessage(hWnd, WM_SIZE, 0, cy Shl 16 Or cx)
            EndIf
            Return 0

        Case WM_DESTROY
            PostQuitMessage(0)
            Return 0
    End Select
    Return DefWindowProc(hWnd, msg, wParam, lParam)
End Function
'ends