Includes AfxCtl.inc, with wrappers for the Windows Common Controls messages that have no macros in windowsx.bi or commonctrl.bi, and some more. The only missing is the Rich Edit control. Will do later.
The help file documents all the wrappers and also the macros for the common controls in windowsx and commctrl.
It has been an huge task. Hope there aren't too many mistakes.
Wow! Incredible! The FB version of CWindow has certainly taken shape and is extremely usable. These common wrappers are the icing on the cake.
I concur. Outstanding job.
James
An example that demonstrates the use of some of the TreeView wrappers:
' ########################################################################################
' Microsoft Windows
' File: CW_COMMCTRL_TreeView_Layout2.fbtpl
' Contents: Template - CWindow with a TreeView
' Compiler: Free Basic
' Copyright (c) 2016 Jose Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################
#INCLUDE ONCE "Afx/CWindow.inc"
#INCLUDE ONCE "Afx/AfxCtl.inc"
#INCLUDE ONCE "Afx/CLayout.inc"
USING Afx.CWindowClass
USING Afx.CLayoutClass
#define IDC_TREEVIEW 1001
#define IDC_EXPAND 1002
#define IDC_COLLAPSE 1003
#define IDC_TOGGLE 1004
DECLARE FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
END WinMain(GetModuleHandleW(NULL), NULL, COMMAND(), SW_NORMAL)
' ========================================================================================
' Window procedure
' ================================================================e========================
FUNCTION WndProc (BYVAL hWnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
' // Scaling ratios
STATIC AS SINGLE rxRatio, ryRatio
SELECT CASE uMsg
CASE WM_CREATE
' // Get the scaling ratios
rxRatio = AfxCWindowPtr(lParam)->rxRatio
ryRatio = AfxCWindowPtr(lParam)->ryRatio
EXIT FUNCTION
CASE WM_COMMAND
SELECT CASE LOWORD(wParam)
' // If ESC key pressed, close the application sending an WM_CLOSE message
CASE IDCANCEL
IF HIWORD(wParam) = BN_CLICKED THEN
SendMessageW hwnd, WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
' // Collapse the root node
CASE IDC_COLLAPSE
DIM hTreeView AS HWND = GetDlgItem(hwnd, IDC_TREEVIEW)
TreeView_Expand(hTreeView, TreeView_GetRoot(hTreeView), TVE_COLLAPSE)
EXIT FUNCTION
' // Expand the root node
CASE IDC_EXPAND
DIM hTreeView AS HWND = GetDlgItem(hwnd, IDC_TREEVIEW)
TreeView_Expand(hTreeView, TreeView_GetRoot(hTreeView), TVE_EXPAND)
EXIT FUNCTION
' // Toggle the root node
CASE IDC_TOGGLE
DIM hTreeView AS HWND = GetDlgItem(hwnd, IDC_TREEVIEW)
TreeView_Expand(hTreeView, TreeView_GetRoot(hTreeView), TVE_TOGGLE)
EXIT FUNCTION
END SELECT
CASE WM_NOTIFY
DIM ptnmhdr AS NMHDR PTR = CAST(NMHDR PTR, lParam)
SELECT CASE ptnmhdr->idFrom
CASE IDC_TREEVIEW
IF ptnmhdr->code = NM_DBLCLK THEN
' // Retrieve the handle of the TreeView
DIM hTreeView AS HWND = GetDlgItem(hwnd, IDC_TREEVIEW)
' // Retrieve the selected item
DIM hItem AS HTREEITEM = TreeView_GetSelection(hTreeView)
' // Retrieve the text of the selected item
DIM wszText AS WSTRING * 260
TreeView_GetItemText(hTreeView, hItem, @wszText, 260)
MessageBox hwnd, wszText, "", MB_OK
EXIT FUNCTION
END IF
END SELECT
CASE WM_GETMINMAXINFO
' // Set the pointer to the address of the MINMAXINFO structure
DIM ptmmi AS MINMAXINFO PTR = CAST(MINMAXINFO PTR, lParam)
' // Set the minimum and maximum sizes that can be produced by dragging the borders of the window
ptmmi->ptMinTrackSize.x = 350 * rxRatio
ptmmi->ptMinTrackSize.y = 150 * ryRatio
EXIT FUNCTION
CASE WM_SIZE
' // Adjusts the controls
DIM pLayout AS CLayout PTR = CAST(CLayout PTR, GetPropW(hwnd, "CLAYOUTPTR"))
IF pLayout THEN pLayout->AdjustControls
EXIT FUNCTION
CASE WM_DESTROY
' // Remove the property
RemovePropW hwnd, "CLAYOUTPTR"
' // End the application
PostQuitMessage(0)
EXIT FUNCTION
END SELECT
' // Pass unprocessed messages to DefWindowProc
FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
' // Set process DPI aware
AfxSetProcessDPIAware
DIM pWindow AS CWindow
pWindow.Create(NULL, "CWindow with a TreeView", @WndProc)
pWindow.ClassStyle = CS_DBLCLKS ' // Change the window style to avoid flicker
pWindow.SetClientSize(337, 370)
pWindow.Center
' // Add a TreeView
DIM hTreeView AS HWND
hTreeView = pWindow.AddControl("TreeView", , IDC_TREEVIEW, "")
pWindow.SetWindowPos hTreeView, NULL, 8, 8, 320, 320, SWP_NOZORDER
' // Add items to the TreeView
DIM AS HTREEITEM hRoot, hNode, hItem
' // Create the root node
hRoot = TreeView_AddRootItem(hTreeView, "Root")
' // Create a node
hNode = TreeView_AppendItem(hTreeView, hRoot, "Node 1")
' // Insert items in the node
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 1")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 2")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 3")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 4")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 5")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 6")
' // Expand the node
TreeView_Expand(hTreeView, hNode, TVM_EXPAND)
' // Create another node
hNode = TreeView_AppendItem(hTreeView, hRoot, "Node 2")
' // Insert items in the node
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 1")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 2")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 3")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 4")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 5")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 6")
' // Expand the node
TreeView_Expand(hTreeView, hNode, TVM_EXPAND)
' // Create another node
hNode = TreeView_AppendItem(hTreeView, hRoot, "Node 3")
' // Insert items in the node
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 1")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 2")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 3")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 4")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 5")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 6")
' // Expand the node
TreeView_Expand(hTreeView, hNode, TVM_EXPAND)
' // Create another node
hNode = TreeView_AppendItem(hTreeView, hRoot, "Node 4")
' // Insert items in the node
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 1")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 2")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 3")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 4")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 5")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 6")
' // Expand the node
TreeView_Expand(hTreeView, hNode, TVM_EXPAND)
' // Expand the root node
TreeView_Expand(hTreeView, hRoot, TVE_EXPAND)
pWindow.AddControl("Button", , IDC_EXPAND, "&Expand", 8, 338, 75, 23)
pWindow.AddControl("Button", , IDC_COLLAPSE, "&Collapse", 90, 338, 75, 23)
pWindow.AddControl("Button", , IDC_TOGGLE, "&Toggle", 172, 338, 75, 23)
' // Add a cancel button
pWindow.AddControl("Button", , IDCANCEL, "&Cancel", 254, 338, 75, 23)
' // Anchor the controls
DIM pLayout AS CLayout = pWindow.hWindow
SetPropW pWindow.hWindow, "CLAYOUTPTR", @pLayout
pLayout.AnchorControl(GetDlgItem(pWindow.hWindow, IDC_TREEVIEW), AFX_ANCHOR_HEIGHT_WIDTH)
pLayout.AnchorControl(GetDlgItem(pWindow.hWindow, IDC_EXPAND), AFX_ANCHOR_BOTTOM)
pLayout.AnchorControl(GetDlgItem(pWindow.hWindow, IDC_COLLAPSE), AFX_ANCHOR_BOTTOM)
pLayout.AnchorControl(GetDlgItem(pWindow.hWindow, IDC_TOGGLE), AFX_ANCHOR_BOTTOM)
pLayout.AnchorControl(GetDlgItem(pWindow.hWindow, IDCANCEL), AFX_ANCHOR_BOTTOM_RIGHT)
' // Process Windows messages
FUNCTION = pWindow.DoEvents(nCmdShow)
END FUNCTION
' ========================================================================================
As some like to create the controls in WM_CREATE (Ã la Petzold), there are two ways to do it with CWindow. In the following example, we get a pointer to the CWindow class calling AfxCWindowPtr(lParam) and using hwnd as the parent instead of pWindow.hWindow or omiting that parameter because CWindow does not know the window handle until WM_CREATE has exited.
' ########################################################################################
' Microsoft Windows
' File: CW_COMMCTRL_TreeView_Layout2.fbtpl
' Contents: Template - CWindow with a TreeView
' Compiler: Free Basic
' Copyright (c) 2016 Jose Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################
#INCLUDE ONCE "Afx/CWindow.inc"
#INCLUDE ONCE "Afx/AfxCtl.inc"
#INCLUDE ONCE "Afx/CLayout.inc"
USING Afx.CWindowClass
USING Afx.CLayoutClass
#define IDC_TREEVIEW 1001
#define IDC_EXPAND 1002
#define IDC_COLLAPSE 1003
#define IDC_TOGGLE 1004
DECLARE FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
END WinMain(GetModuleHandleW(NULL), NULL, COMMAND(), SW_NORMAL)
' ========================================================================================
' Window procedure
' ================================================================e========================
FUNCTION WndProc (BYVAL hWnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
STATIC AS SINGLE rxRatio, ryRatio
STATIC hTreeView AS HWND
SELECT CASE uMsg
CASE WM_CREATE
' Get a pointer to the CWindow clas
DIM pWindow AS CWindow PTR = AfxCWindowPtr(lParam)
' // Get the scaling ratios
rxRatio = pWindow->rxRatio
ryRatio = pWindow->ryRatio
' // Add a TreeView
' // Note that we have are using hwnd as the parent instead of pWindow.hWindow
' // because the CWindow class does not know the handle of the window until
' // WM_CREATE message has exited.
hTreeView = pWindow->AddControl("TreeView", hwnd, IDC_TREEVIEW, "")
pWindow->SetWindowPos hTreeView, NULL, 8, 8, 320, 320, SWP_NOZORDER
' // Add items to the TreeView
DIM AS HTREEITEM hRoot, hNode, hItem
' // Create the root node
hRoot = TreeView_AddRootItem(hTreeView, "Root")
' // Create a node
hNode = TreeView_AppendItem(hTreeView, hRoot, "Node 1")
' // Insert items in the node
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 1")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 2")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 3")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 4")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 5")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 6")
' // Expand the node
TreeView_Expand(hTreeView, hNode, TVM_EXPAND)
' // Create another node
hNode = TreeView_AppendItem(hTreeView, hRoot, "Node 2")
' // Insert items in the node
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 1")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 2")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 3")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 4")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 5")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 6")
' // Expand the node
TreeView_Expand(hTreeView, hNode, TVM_EXPAND)
' // Create another node
hNode = TreeView_AppendItem(hTreeView, hRoot, "Node 3")
' // Insert items in the node
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 1")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 2")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 3")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 4")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 5")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 6")
' // Expand the node
TreeView_Expand(hTreeView, hNode, TVM_EXPAND)
' // Create another node
hNode = TreeView_AppendItem(hTreeView, hRoot, "Node 4")
' // Insert items in the node
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 1")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 2")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 3")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 4")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 5")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 6")
' // Expand the node
TreeView_Expand(hTreeView, hNode, TVM_EXPAND)
' // Expand the root node
TreeView_Expand(hTreeView, hRoot, TVE_EXPAND)
' // Add the buttons
pWindow->AddControl("Button", hwnd, IDC_EXPAND, "&Expand", 8, 338, 75, 23)
pWindow->AddControl("Button", hwnd, IDC_COLLAPSE, "&Collapse", 90, 338, 75, 23)
pWindow->AddControl("Button", hwnd, IDC_TOGGLE, "&Toggle", 172, 338, 75, 23)
pWindow->AddControl("Button", hwnd, IDCANCEL, "&Cancel", 254, 338, 75, 23)
EXIT FUNCTION
CASE WM_COMMAND
SELECT CASE LOWORD(wParam)
' // If ESC key pressed, close the application sending an WM_CLOSE message
CASE IDCANCEL
IF HIWORD(wParam) = BN_CLICKED THEN
SendMessageW hwnd, WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
' // Collapse the root node
CASE IDC_COLLAPSE
TreeView_Expand(hTreeView, TreeView_GetRoot(hTreeView), TVE_COLLAPSE)
EXIT FUNCTION
' // Expand the root node
CASE IDC_EXPAND
TreeView_Expand(hTreeView, TreeView_GetRoot(hTreeView), TVE_EXPAND)
EXIT FUNCTION
' // Toggle the root node
CASE IDC_TOGGLE
TreeView_Expand(hTreeView, TreeView_GetRoot(hTreeView), TVE_TOGGLE)
EXIT FUNCTION
END SELECT
CASE WM_NOTIFY
DIM ptnmhdr AS NMHDR PTR = CAST(NMHDR PTR, lParam)
SELECT CASE ptnmhdr->idFrom
CASE IDC_TREEVIEW
IF ptnmhdr->code = NM_DBLCLK THEN
' // Retrieve the selected item
DIM hItem AS HTREEITEM = TreeView_GetSelection(hTreeView)
' // Retrieve the text of the selected item
DIM wszText AS WSTRING * 260
TreeView_GetItemText(hTreeView, hItem, @wszText, 260)
MessageBox hwnd, wszText, "", MB_OK
EXIT FUNCTION
END IF
END SELECT
CASE WM_GETMINMAXINFO
' // Set the pointer to the address of the MINMAXINFO structure
DIM ptmmi AS MINMAXINFO PTR = CAST(MINMAXINFO PTR, lParam)
' // Set the minimum and maximum sizes that can be produced by dragging the borders of the window
ptmmi->ptMinTrackSize.x = 350 * rxRatio
ptmmi->ptMinTrackSize.y = 150 * ryRatio
EXIT FUNCTION
CASE WM_SIZE
' // Adjusts the controls
DIM pLayout AS CLayout PTR = CAST(CLayout PTR, GetPropW(hwnd, "CLAYOUTPTR"))
IF pLayout THEN pLayout->AdjustControls
EXIT FUNCTION
CASE WM_DESTROY
' // Remove the property
RemovePropW hwnd, "CLAYOUTPTR"
' // End the application
PostQuitMessage(0)
EXIT FUNCTION
END SELECT
' // Pass unprocessed messages to DefWindowProc
FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
' // Set process DPI aware
AfxSetProcessDPIAware
DIM pWindow AS CWindow
pWindow.Create(NULL, "CWindow with a TreeView", @WndProc)
pWindow.ClassStyle = CS_DBLCLKS ' // Change the window style to avoid flicker
pWindow.SetClientSize(337, 370)
pWindow.Center
' // Anchor the controls
DIM pLayout AS CLayout = pWindow.hWindow
SetPropW pWindow.hWindow, "CLAYOUTPTR", @pLayout
pLayout.AnchorControl(GetDlgItem(pWindow.hWindow, IDC_TREEVIEW), AFX_ANCHOR_HEIGHT_WIDTH)
pLayout.AnchorControl(GetDlgItem(pWindow.hWindow, IDC_EXPAND), AFX_ANCHOR_BOTTOM)
pLayout.AnchorControl(GetDlgItem(pWindow.hWindow, IDC_COLLAPSE), AFX_ANCHOR_BOTTOM)
pLayout.AnchorControl(GetDlgItem(pWindow.hWindow, IDC_TOGGLE), AFX_ANCHOR_BOTTOM)
pLayout.AnchorControl(GetDlgItem(pWindow.hWindow, IDCANCEL), AFX_ANCHOR_BOTTOM_RIGHT)
' // Process Windows messages
FUNCTION = pWindow.DoEvents(nCmdShow)
END FUNCTION
' ========================================================================================
The second way is to let to know CWindow the handle of the window calling pWindow->hWindow = hwnd. Adter that, we can use both hTreeView = pWindow->AddControl("TreeView", pWindow->hWIndow, IDC_TREEVIEW, "") or hTreeView = pWindow->AddControl("TreeView", , IDC_TREEVIEW, "").
' ########################################################################################
' Microsoft Windows
' File: CW_COMMCTRL_TreeView_Layout2.fbtpl
' Contents: Template - CWindow with a TreeView
' Compiler: Free Basic
' Copyright (c) 2016 Jose Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################
#INCLUDE ONCE "Afx/CWindow.inc"
#INCLUDE ONCE "Afx/AfxCtl.inc"
#INCLUDE ONCE "Afx/CLayout.inc"
USING Afx.CWindowClass
USING Afx.CLayoutClass
#define IDC_TREEVIEW 1001
#define IDC_EXPAND 1002
#define IDC_COLLAPSE 1003
#define IDC_TOGGLE 1004
DECLARE FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
END WinMain(GetModuleHandleW(NULL), NULL, COMMAND(), SW_NORMAL)
' ========================================================================================
' Window procedure
' ================================================================e========================
FUNCTION WndProc (BYVAL hWnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
STATIC AS SINGLE rxRatio, ryRatio
DIM hTreeView AS HWND
SELECT CASE uMsg
CASE WM_CREATE
' // Get a pointer to the CWindow clas
DIM pWindow AS CWindow PTR
pWindow = AfxCWindowPtr(lParam)
' // Get the scaling ratios
rxRatio = pWindow->rxRatio
ryRatio = pWindow->ryRatio
' // Set the window handle
pWindow->hWindow = hwnd
' // Add a TreeView
hTreeView = pWindow->AddControl("TreeView", , IDC_TREEVIEW, "")
pWindow->SetWindowPos hTreeView, NULL, 8, 8, 320, 320, SWP_NOZORDER
' // Add items to the TreeView
DIM AS HTREEITEM hRoot, hNode, hItem
' // Create the root node
hRoot = TreeView_AddRootItem(hTreeView, "Root")
' // Create a node
hNode = TreeView_AppendItem(hTreeView, hRoot, "Node 1")
' // Insert items in the node
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 1")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 2")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 3")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 4")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 5")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 1 Item 6")
' // Expand the node
TreeView_Expand(hTreeView, hNode, TVM_EXPAND)
' // Create another node
hNode = TreeView_AppendItem(hTreeView, hRoot, "Node 2")
' // Insert items in the node
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 1")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 2")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 3")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 4")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 5")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 2 Item 6")
' // Expand the node
TreeView_Expand(hTreeView, hNode, TVM_EXPAND)
' // Create another node
hNode = TreeView_AppendItem(hTreeView, hRoot, "Node 3")
' // Insert items in the node
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 1")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 2")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 3")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 4")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 5")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 3 Item 6")
' // Expand the node
TreeView_Expand(hTreeView, hNode, TVM_EXPAND)
' // Create another node
hNode = TreeView_AppendItem(hTreeView, hRoot, "Node 4")
' // Insert items in the node
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 1")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 2")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 3")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 4")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 5")
hItem = TreeView_AppendItem(hTreeView, hNode, "Node 4 Item 6")
' // Expand the node
TreeView_Expand(hTreeView, hNode, TVM_EXPAND)
' // Expand the root node
TreeView_Expand(hTreeView, hRoot, TVE_EXPAND)
' // Add the buttons
pWindow->AddControl("Button", , IDC_EXPAND, "&Expand", 8, 338, 75, 23)
pWindow->AddControl("Button", , IDC_COLLAPSE, "&Collapse", 90, 338, 75, 23)
pWindow->AddControl("Button", , IDC_TOGGLE, "&Toggle", 172, 338, 75, 23)
pWindow->AddControl("Button", , IDCANCEL, "&Cancel", 254, 338, 75, 23)
EXIT FUNCTION
CASE WM_COMMAND
SELECT CASE LOWORD(wParam)
' // If ESC key pressed, close the application sending an WM_CLOSE message
CASE IDCANCEL
IF HIWORD(wParam) = BN_CLICKED THEN
SendMessageW hwnd, WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
' // Collapse the root node
CASE IDC_COLLAPSE
TreeView_Expand(hTreeView, TreeView_GetRoot(hTreeView), TVE_COLLAPSE)
EXIT FUNCTION
' // Expand the root node
CASE IDC_EXPAND
TreeView_Expand(hTreeView, TreeView_GetRoot(hTreeView), TVE_EXPAND)
EXIT FUNCTION
' // Toggle the root node
CASE IDC_TOGGLE
TreeView_Expand(hTreeView, TreeView_GetRoot(hTreeView), TVE_TOGGLE)
EXIT FUNCTION
END SELECT
CASE WM_NOTIFY
DIM ptnmhdr AS NMHDR PTR = CAST(NMHDR PTR, lParam)
SELECT CASE ptnmhdr->idFrom
CASE IDC_TREEVIEW
IF ptnmhdr->code = NM_DBLCLK THEN
' // Retrieve the selected item
DIM hItem AS HTREEITEM = TreeView_GetSelection(hTreeView)
' // Retrieve the text of the selected item
DIM wszText AS WSTRING * 260
TreeView_GetItemText(hTreeView, hItem, @wszText, 260)
MessageBox hwnd, wszText, "", MB_OK
EXIT FUNCTION
END IF
END SELECT
CASE WM_GETMINMAXINFO
' // Set the pointer to the address of the MINMAXINFO structure
DIM ptmmi AS MINMAXINFO PTR = CAST(MINMAXINFO PTR, lParam)
' // Set the minimum and maximum sizes that can be produced by dragging the borders of the window
ptmmi->ptMinTrackSize.x = 350 * rxRatio
ptmmi->ptMinTrackSize.y = 150 * ryRatio
EXIT FUNCTION
CASE WM_SIZE
' // Adjusts the controls
DIM pLayout AS CLayout PTR = CAST(CLayout PTR, GetPropW(hwnd, "CLAYOUTPTR"))
IF pLayout THEN pLayout->AdjustControls
EXIT FUNCTION
CASE WM_DESTROY
' // Remove the property
RemovePropW hwnd, "CLAYOUTPTR"
' // End the application
PostQuitMessage(0)
EXIT FUNCTION
END SELECT
' // Pass unprocessed messages to DefWindowProc
FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
' // Set process DPI aware
AfxSetProcessDPIAware
DIM pWindow AS CWindow
pWindow.Create(NULL, "CWindow with a TreeView", @WndProc)
pWindow.ClassStyle = CS_DBLCLKS ' // Change the window style to avoid flicker
pWindow.SetClientSize(337, 370)
pWindow.Center
' // Anchor the controls
DIM pLayout AS CLayout = pWindow.hWindow
SetPropW pWindow.hWindow, "CLAYOUTPTR", @pLayout
pLayout.AnchorControl(GetDlgItem(pWindow.hWindow, IDC_TREEVIEW), AFX_ANCHOR_HEIGHT_WIDTH)
pLayout.AnchorControl(GetDlgItem(pWindow.hWindow, IDC_EXPAND), AFX_ANCHOR_BOTTOM)
pLayout.AnchorControl(GetDlgItem(pWindow.hWindow, IDC_COLLAPSE), AFX_ANCHOR_BOTTOM)
pLayout.AnchorControl(GetDlgItem(pWindow.hWindow, IDC_TOGGLE), AFX_ANCHOR_BOTTOM)
pLayout.AnchorControl(GetDlgItem(pWindow.hWindow, IDCANCEL), AFX_ANCHOR_BOTTOM_RIGHT)
' // Process Windows messages
FUNCTION = pWindow.DoEvents(nCmdShow)
END FUNCTION
' ========================================================================================
The next version of CWindow will feature the ControlHandle method, so besides GetDlgItem(pWindow.hWindow, IDC_TREEVIEW) we could also use pWindow.ControlHandle(IDC_TREEVIEW). Not a big improvement, but maybe beginners will find it easier to remember and understand than GetDlgItem.
For one of the examples below I had the need of retrieve the path of a system dll, so I have incoporated this wrapper function to AfxWin.inc.
' ========================================================================================
' Retrieves the fully qualified path of the specified system DLL.
' ========================================================================================
PRIVATE FUNCTION AfxGetSystemDllPath (BYVAL pwszDllName AS WSTRING PTR, BYVAL pwszDllPath AS WSTRING PTR, BYVAL cchCount AS DWORD) AS LONG
IF pwszDllName = NULL OR pwszDllPath = NULL OR cchCount = 0 THEN EXIT FUNCTION
DIM hLib AS HMODULE
hLib = LoadLibraryW(pwszDllName)
IF hLib THEN
FUNCTION = GetModuleFileNameW(hLib, pwszDllPath, cchCount)
FreeLibrary hLib
END IF
END FUNCTION
' ========================================================================================
Pick Icon File Dialog
The PickIconFileDialog allows to add an icon-selection dialog box to your applications.
The following example demonstrates how to use it to display the icons from the resource file of shell32.dll. If you select an icon, the application extraxts it and sets it as the application icon.
' ########################################################################################
' Microsoft Windows
' File: CW_COMMCTRL_PickIconDlg.fbtpl
' Contents: Demonstrates the use of the Pick icon dialog.
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2016 Jose Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################
#INCLUDE ONCE "Afx/CWindow.inc"
#INCLUDE ONCE "win/shlobj.bi"
USING Afx.CWindowClass
CONST IDC_PICKDLG = 1001 ' // Pick icon dialog identifier
' // Forward declaration
DECLARE FUNCTION WndProc (BYVAL hWnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
DECLARE FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
END WinMain(GetModuleHandleW(NULL), NULL, COMMAND(), SW_NORMAL)
' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
' // Set process DPI aware
AfxSetProcessDPIAware
DIM pWindow AS CWindow
pWindow.Create(NULL, "CWindow with a button", @WndProc)
pWindow.SetClientSize(500, 320)
pWindow.Center
' // Add buttons without position or size (it will be resized in the WM_SIZE message).
pWindow.AddControl("Button", , IDC_PICKDLG, "&Pick")
pWindow.AddControl("Button", , IDCANCEL, "&Close")
' // Process Windows messages
FUNCTION = pWindow.DoEvents(nCmdShow)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Window procedure
' ========================================================================================
FUNCTION WndProc (BYVAL hWnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
STATIC wszIconPath AS WSTRING * MAX_PATH ' // Path of the resource file containing the icons
STATIC nIconIndex AS LONG ' // Icon index
STATIC hIcon AS HICON ' // Icon handle
SELECT CASE uMsg
CASE WM_COMMAND
' // If ESC key pressed, close the application sending an WM_CLOSE message
SELECT CASE LOWORD(wParam)
CASE IDCANCEL
IF HIWORD(wParam) = BN_CLICKED THEN
SendMessageW hwnd, WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
' // Launch the Pick icon dialog
CASE IDC_PICKDLG
IF HIWORD(wParam) = BN_CLICKED THEN
IF LEN(wszIconPath) = 0 THEN AfxGetSystemDllPath("Shell32.dll", wszIconPath, MAX_PATH)
IF LEN(wszIconPath) = 0 THEN EXIT FUNCTION
' // Activate the Pick Icon Common Dialog Box
DIM hr AS LONG = PickIconDlg(0, wszIconPath, SIZEOF(wszIconPath), @nIconIndex)
' // If an icon has been selected...
IF hr = 1 THEN
' // Destroy previously loaded icon, if any
IF hIcon THEN DestroyIcon(hIcon)
' // Get the handle of the new selected icon
hIcon = ExtractIconW(GetModuleHandle(NULL), wszIconPath, nIconIndex)
' // Replace the application icons
IF hIcon THEN
SendMessageW(hwnd, WM_SETICON, ICON_SMALL, cast(LPARAM, hIcon))
SendMessageW(hwnd, WM_SETICON, ICON_BIG, cast(LPARAM, hIcon))
END IF
END IF
EXIT FUNCTION
END IF
END SELECT
CASE WM_SIZE
IF wParam <> SIZE_MINIMIZED THEN
' // Resize the buttons
DIM pWindow AS CWindow PTR = AfxCWindowPtr(hwnd)
IF pWindow THEN pWindow->MoveWindow GetDlgItem(hwnd, IDC_PICKDLG), pWindow->ClientWidth - 230, pWindow->ClientHeight - 50, 75, 23, CTRUE
IF pWindow THEN pWindow->MoveWindow GetDlgItem(hwnd, IDCANCEL), pWindow->ClientWidth - 120, pWindow->ClientHeight - 50, 75, 23, CTRUE
END IF
CASE WM_DESTROY
' // Destroy the icon
IF hIcon THEN DestroyIcon(hIcon)
' // End the application sending a WM_QUIT message
PostQuitMessage(0)
EXIT FUNCTION
END SELECT
' // Process Windows messages
FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
The following example demonstrates how to use the pick icon dialog to display the icons from our own resource file. The attached file contains the source code and the icons.
' ########################################################################################
' Microsoft Windows
' File: CW_COMMCTRL_PickIconDlg.fbtpl
' Contents: Demonstrates the use of the Pick icon dialog.
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2016 Jose Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################
#INCLUDE ONCE "Afx/CWindow.inc"
#INCLUDE ONCE "win/shlobj.bi"
USING Afx.CWindowClass
' $FB_RESPATH = "PickIconDialog.rc"
CONST IDC_PICKDLG = 1001 ' // Pick icon dialog identifier
' // Forward declaration
DECLARE FUNCTION WndProc (BYVAL hWnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
DECLARE FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
END WinMain(GetModuleHandleW(NULL), NULL, COMMAND(), SW_NORMAL)
' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
' // Set process DPI aware
AfxSetProcessDPIAware
DIM pWindow AS CWindow
pWindow.Create(NULL, "CWindow with a button", @WndProc)
pWindow.SetClientSize(500, 320)
pWindow.Center
' // Add buttons without position or size (it will be resized in the WM_SIZE message).
pWindow.AddControl("Button", , IDC_PICKDLG, "&Pick")
pWindow.AddControl("Button", , IDCANCEL, "&Close")
' // Process Windows messages
FUNCTION = pWindow.DoEvents(nCmdShow)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Window procedure
' ========================================================================================
FUNCTION WndProc (BYVAL hWnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
STATIC wszIconPath AS WSTRING * MAX_PATH ' // Path of the resource file containing the icons
STATIC nIconIndex AS LONG ' // Icon index
STATIC hIcon AS HICON ' // Icon handle
SELECT CASE uMsg
CASE WM_COMMAND
' // If ESC key pressed, close the application sending an WM_CLOSE message
SELECT CASE LOWORD(wParam)
CASE IDCANCEL
IF HIWORD(wParam) = BN_CLICKED THEN
SendMessageW hwnd, WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
' // Launch the Pick icon dialog
CASE IDC_PICKDLG
IF HIWORD(wParam) = BN_CLICKED THEN
IF LEN(wszIconPath) = 0 THEN
' // Get the full path of our executable
GetModuleFileNameW(NULL, wszIconPath, MAX_PATH)
END IF
' // Activate the Pick Icon Common Dialog Box
DIM hr AS LONG = PickIconDlg(0, wszIconPath, SIZEOF(wszIconPath), @nIconIndex)
' // If an icon has been selected...
IF hr = 1 THEN
' // Destroy previously loaded icon, if any
IF hIcon THEN DestroyIcon(hIcon)
' // Get the handle of the new selected icon
hIcon = ExtractIconW(GetModuleHandle(NULL), wszIconPath, nIconIndex)
' // Replace the application icons
IF hIcon THEN
SendMessageW(hwnd, WM_SETICON, ICON_SMALL, cast(LPARAM, hIcon))
SendMessageW(hwnd, WM_SETICON, ICON_BIG, cast(LPARAM, hIcon))
END IF
END IF
EXIT FUNCTION
END IF
END SELECT
CASE WM_SIZE
IF wParam <> SIZE_MINIMIZED THEN
' // Resize the buttons
DIM pWindow AS CWindow PTR = AfxCWindowPtr(hwnd)
IF pWindow THEN pWindow->MoveWindow GetDlgItem(hwnd, IDC_PICKDLG), pWindow->ClientWidth - 230, pWindow->ClientHeight - 50, 75, 23, CTRUE
IF pWindow THEN pWindow->MoveWindow GetDlgItem(hwnd, IDCANCEL), pWindow->ClientWidth - 120, pWindow->ClientHeight - 50, 75, 23, CTRUE
END IF
CASE WM_DESTROY
' // Destroy the icon
IF hIcon THEN DestroyIcon(hIcon)
' // End the application sending a WM_QUIT message
PostQuitMessage(0)
EXIT FUNCTION
END SELECT
' // Process Windows messages
FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
Hi Jose,
I see that you are #Include the windowsx.bi file when you use AfxCtl.inc. The windowsx.bi file is not translated correctly in the latest FB distribution. Maybe you should code without using it?
#include once "win/windowsx.bi"
I made a cleaned/fixed up version of windowsx.bi and I manually include it my code (well, in WinFBE that is what I am doing). Of course using the windowsx.bi and my windowsxx.bi files together cause duplicate definition errors. So, at this time whenever I include your AfxWin.inc include, i get duplicate errors because of these two lines at the end of the file:
' // Other include files - Must go here for a matter of forward references
'#INCLUDE ONCE "Afx/AfxMenu.inc"
'#INCLUDE ONCE "Afx/AfxCtl.inc"
AfxCtl.inc includes windowsx.bi
Paul,
I just replaced the original with yours from your Fb Forum Post.
Is that one up to date?
James
Yes, that one is up to date.
Quote
' // Other include files - Must go here for a matter of forward references
'#INCLUDE ONCE "Afx/AfxMenu.inc"
'#INCLUDE ONCE "Afx/AfxCtl.inc"
Remove them. I will add them to my code when needed.
Quote
AfxCtl.inc includes windowsx.bi
Indeed. In AfxCtl.inc I only have added wrappers for the messages that are not covered by windowsx.bi and commctrl.bi. AFAIK the only problem are the message crackers, that I don't use. If I change the name to windowsxx.bi, then the code will fail if somebody that does not have your modified file tries to compile it. Maybe you should replace windows.bi with your include instead of using another name.
Jose,
I am exploring CBStr and noticed there is not Len(gth) PROPERTY?
James
Don't know which version are you using. There is not a length property, but a LEN operator.
' ========================================================================================
' Returns the length of the BSTR in characters
' ========================================================================================
OPERATOR Len (BYREF pCBStr AS CBStr) AS INTEGER
OPERATOR = SysStringLen(pCBStr.Handle)
END OPERATOR
' ========================================================================================
Usage:
DIM cbs AS CBStr = "test string"
print len(cbs)
Jose,
I don't see it in CBStr.inc in RC10?
James
It is at the end of the file.
Duh...
I'm getting too old for this :)
I added PROPERTY Length any way!!!!
James
You need to increase the DPI to get bigger fonts :)
Jose,
Why do you default to a sizable window with CWindow?
James
Just a matter of preference.
Quote from: Jose Roca on June 20, 2016, 04:23:10 PM
Just a matter of preference.
Jose,
I question it because very seldom have I seen you use layout code with any of your examples.
It would stand to reason (by me anyway) that the window would/should not be sizable unless layout code is used.
And yes I know I can .... but I'm lazy and I want it to be the default :)
James
> I question it because very seldom have I seen you use layout code with any of your examples.
These are examples to demonstrate how to create the controls. It doesn't matter if they are resizable or not.
I'm tired of seeing fixed size popup dialogs whose bottom goes outside of my monitor. I want them to be resizable and scrollable. Check if the height of your dialog would be greater than the height of the monitor and adjust it and make the contents scrollable.
I fyou want to code as you did more than ten years ago, it's your choice.
Jose,
I understand what your saying but I am using YOUR DPI aware CWindow so hopefully these issues will not surface. Not all windows warrant a full control layout.
How about checking for a CW_DEFAULT_STYLE #define and use that if it is defined?
James
If you want to make it not resizable use
pWindow.Create(NULL, "CWindow with a button", @WndProc)
AfxRemoveWindowStyle(pWindow.hWindow, WS_THICKFRAME)
I have been working in a class, CScrollWindow, to make windows scrollable. Tomorrow I will do more tests.
The idea is:
1. Create our window.
2. Set the needed client size to fit all the controls.
3. When the program starts, check the size of the working area.
4. If our window fits in the working area, do nothing.
5. If it is bigger, create an instance of the class passing the handle of our window.
6. Shrink the size of the window to not exceed the size of the working area.
Et voilà , the window will be scrollable. No more Ok, Apply or Cancel buttons unclickable because they're outside the working area.
Quote from: Jose Roca on June 20, 2016, 07:47:11 PM
If you want to make it not resizable use
pWindow.Create(NULL, "CWindow with a button", @WndProc)
AfxRemoveWindowStyle(pWindow.hWindow, WS_THICKFRAME)
That will work just fine. Thank You.
Edit: Woops still has a max button. This works"
AfxRemoveWindowStyle(pWindow.hWindow, WS_THICKFRAME OR WS_MAXIMIZEBOX)
James
A new class for our framework. SCrollWindow allows to make a window or popup dialog scrollable.
A littlt test:
' ########################################################################################
' Microsoft Windows
' File: CW_ScrollWindow.fbtpl
' Contents: Scrollable window
' Compiler: Free Basic
' Copyright (c) 2016 Jose Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################
#define unicode
#INCLUDE ONCE "Afx/CWindow.inc"
#INCLUDE ONCE "Afx/AfxCtl.inc"
#INCLUDE ONCE "Afx/CScrollWindow.inc"
USING Afx.CWindowClass
#define IDC_LISTBOX 1001
DECLARE FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
END WinMain(GetModuleHandleW(NULL), NULL, COMMAND(), SW_NORMAL)
DECLARE FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
' // Set process DPI aware
AfxSetProcessDPIAware
DIM pWindow AS CWindow
DIM hwndMain AS HWND = pWindow.Create(NULL, "Scrollable window", @WndProc)
pWindow.ClassStyle = CS_DBLCLKS ' // Change the window style to avoid flicker
' // Set a client size big enough to display all the controls
pWindow.SetClientSize(320, 335)
' // Add a listbox
DIM hListBox AS HWND
hListBox = pWindow.AddControl("ListBox", , IDC_LISTBOX)
pWindow.SetWindowPos hListBox, NULL, 8, 8, 300, 280, SWP_NOZORDER
' // Fill the list box
DIM i AS LONG, wszText AS WSTRING * 260
FOR i = 1 TO 50
wszText = "Item " & RIGHT("00" & STR(i), 2)
ListBox_AddString(hListBox, @wszText)
NEXT
' // Select the first item
ListBox_SetCursel(hListBox, 0)
' // Add a cancel button
pWindow.AddControl("Button", , IDCANCEL, "&Cancel", 233, 298, 75, 23)
' // Create an instance of the CScrollWindow class and attach the main window to it
DIM pScrollWindow AS CScrollWindow = hwndMain
SetPropW hwndMain,"CSCROLLWINDOWPTR", @pScrollWindow
' // Shrink the client size
pWindow.SetClientSize(250, 260)
' // Center the window
pWindow.Center
' // Message pump
FUNCTION = pWindow.DoEvents(nCmdShow)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Window procedure
' ================================================================e========================
FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
SELECT CASE uMsg
CASE WM_COMMAND
SELECT CASE LOWORD(wParam)
' // If ESC key pressed, close the application sending an WM_CLOSE message
CASE IDCANCEL
IF HIWORD(wParam) = BN_CLICKED THEN
SendMessageW hwnd, WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
CASE IDC_LISTBOX
SELECT CASE HIWORD(wParam)
CASE LBN_DBLCLK
' // Get the handle of the Listbox
DIM hListBox AS HWND = GetDlgItem(hwnd, IDC_LISTBOX)
' // Get the current selection
DIM curSel AS LONG = ListBox_GetCursel(hListBox)
' // Get the length of the ListBox item text
DIM nLen AS LONG = ListBox_GetTextLen(hListBox, curSel)
' // Allocate memory for the buffer
DIM pwszText AS WSTRING PTR = CAllocate(nLen + 1, 2)
' // Get the text and display it
ListBox_GetText(hListBox, curSel, pwszText)
MessageBoxW(hwnd, pwszText, "ListBox test", MB_OK)
' // Deallocate the memory used by the buffer
DeAllocate pwszText
pwszText = NULL
EXIT FUNCTION
END SELECT
END SELECT
CASE WM_SIZE
DIM pScrollWindow AS CScrollWindow PTR = CAST(CScrollWindow PTR, GetPropW(hwnd, "CSCROLLWINDOWPTR"))
IF pScrollWindow THEN pScrollWindow->OnSize(wParam, lParam)
EXIT FUNCTION
CASE WM_VSCROLL
DIM pScrollWindow AS CScrollWindow PTR = CAST(CScrollWindow PTR, GetPropW(hwnd, "CSCROLLWINDOWPTR"))
IF pScrollWindow THEN pScrollWindow->OnVScroll(wParam, lParam)
EXIT FUNCTION
CASE WM_HSCROLL
DIM pScrollWindow AS CScrollWindow PTR = CAST(CScrollWindow PTR, GetPropW(hwnd, "CSCROLLWINDOWPTR"))
IF pScrollWindow THEN pScrollWindow->OnHScroll(wParam, lParam)
EXIT FUNCTION
CASE WM_DESTROY
' // Remove the property
RemovePropW hwnd,"CSCROLLWINDOWPTR"
' // End the application
PostQuitMessage(0)
EXIT FUNCTION
END SELECT
' // Pass unprocessed messages to DefWindowProc
FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
Its main use could be to make sure that our window or dialog will fit in the dimensions of the user's monitor.
Often, we need to make dialogs with many options, using all the available height. But because monitors have many different sizes and resolutions, and also the user could be using a high dpi setting, we risk that part of it becomes unreachable to the user.
ScrollWindow allows us to make a window or dialog scrollable easily. At runtime, we can check if the size of our dialog will exceed the size of the user monitor and make the window scrollable by adjusting the size of the window or dialog to fit the dimensions of the working area of the monitor.
This is such a great idea. Many times I have been asked how to scroll regular forms/windows. It is great that now there is a class for this and also a class for automatic resizing and repositioning of forms and controls. The framework is really growing into a strong toolbox.
Quote from: James Fuller on June 21, 2016, 07:05:23 AM
Quote from: Jose Roca on June 20, 2016, 07:47:11 PM
If you want to make it not resizable use
pWindow.Create(NULL, "CWindow with a button", @WndProc)
AfxRemoveWindowStyle(pWindow.hWindow, WS_THICKFRAME)
That will work just fine. Thank You.
Edit: Woops still has a max button. This works"
AfxRemoveWindowStyle(pWindow.hWindow, WS_THICKFRAME OR WS_MAXIMIZEBOX)
James
With the next version of CWindow you can do:
pWindow.ClassStyle = <styles>
e.g.
pWindow.WindowStyle = WS_VISIBLE OR WS_CAPTION OR WS_POPUPWINDOW
There is also a property to set the extended styles.
Quote from: TechSupport on June 21, 2016, 05:40:26 PM
This is such a great idea. Many times I have been asked how to scroll regular forms/windows. It is great that now there is a class for this and also a class for automatic resizing and repositioning of forms and controls. The framework is really growing into a strong toolbox.
These are the kind of things that I no longer do for PB because PBer's don't use it. I'm so bored of only seeing DDT code, that I only read a few posts.
One good thing is that as it is standalone class, if someone can do it better he simply has to write his own class and offer it as an alternative.
Now that I have the scroll class working well, I will modify the CWindow and CTabPage classes to allow to store the pointer to the scroll class to delete it when the window or the tab page is destroyed. It will also allow to get a pointer to it calling a method instead of having to use a SetPropW / GetPropW / RemovePropW.
In this post, an user asked you how to create a scrollable tab page: http://www.planetsquires.com/protect/forum/index.php?topic=3795.msg27648#msg27648
With CWindow and CScrollWindow it is a piece of cake:
The "trick" is to get the client size of the tab page, make it larger, attach it to an instance of CScrollWindow, and restore the original client size.
' ************************************************************************
' // Make the first tab page scrollable
' // TODO: Modify the CTabPage class to set the pointer to CScrollWindow
' // to delete this class when the tab page is destroyed.
' ************************************************************************
' // Get the client size of the first page and make it greater
DIM nWidth AS LONG = pTabPage1->ClientWidth
DIM nHeight AS LONG = pTabPage1->ClientHeight
pTabPage1->SetClientSize(nWidth + 150, nHeight + 150)
' // Create an instance of the CScrollWindow class and attach the window handle to it
DIM pScrollWindow AS CScrollWindow = pTabPage1->hTabPage
SetPropW pTabPage1->hTabPage,"CSCROLLWINDOWPTR", @pScrollWindow
' // Shrink the client size back to original
pTabPage1->SetClientSize(nWidth, nHeight)
CScrollWindow will automatically display or hide the scroll bars and do the scrolling. The best thing of the good ideas is that they are simple; the worst thing is that we only got it after having tried many other complicated and unsatisfactory ways.
Full example code:
' ########################################################################################
' Microsoft Windows
' File: CW_COMMCTRL_TabControlDemo.fbtpl - Template
' Contents: CWindow Tab Control template
' Remarks: Demonstrates the use of the CTabPage class
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2016 Jose Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################
#INCLUDE ONCE "Afx/CWindow.inc"
#INCLUDE ONCE "Afx/CScrollWindow.inc"
USING Afx.CWindowClass
CONST IDC_TAB = 1001
CONST IDC_EDIT1 = 1002
CONST IDC_EDIT2 = 1003
CONST IDC_BTNSUBMIT = 1004
CONST IDC_COMBO = 1005
CONST IDC_LISTBOX = 1006
' // Forward declarations
DECLARE FUNCTION TabPage1_WndProc(BYVAL hWnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
DECLARE FUNCTION TabPage2_WndProc(BYVAL hWnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
DECLARE FUNCTION TabPage3_WndProc(BYVAL hWnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
DECLARE FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
END WinMain(GetModuleHandleW(NULL), NULL, COMMAND(), SW_NORMAL)
' ========================================================================================
' Window procedure
' ========================================================================================
FUNCTION WndProc (BYVAL hWnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
SELECT CASE uMsg
CASE WM_CREATE
EXIT FUNCTION
CASE WM_COMMAND
SELECT CASE LOWORD(wParam)
' // If ESC key pressed, close the application sending an WM_CLOSE message
CASE IDCANCEL
IF HIWORD(wParam) = BN_CLICKED THEN
SendMessageW hwnd, WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
END SELECT
CASE WM_GETMINMAXINFO
' Set the pointer to the address of the MINMAXINFO structure
DIM ptmmi AS MINMAXINFO PTR = CAST(MINMAXINFO PTR, lParam)
' Set the minimum and maximum sizes that can be produced by dragging the borders of the window
DIM pWindow AS CWindow PTR = AfxCWindowPtr(hwnd)
IF pWindow THEN
ptmmi->ptMinTrackSize.x = 460 * pWindow->rxRatio
ptmmi->ptMinTrackSize.y = 320 * pWindow->ryRatio
END IF
EXIT FUNCTION
CASE WM_SIZE
DIM pWindow AS CWindow PTR = AfxCWindowPtr(hwnd)
DIM hTab AS HWND = GetDlgItem(hwnd, IDC_TAB)
' / Move the close button
IF pWindow THEN pWindow->MoveWindow GetDlgItem(hwnd, IDCANCEL), pWindow->ClientWidth - 85, pWindow->ClientHeight - 28, 75, 23, CTRUE
' // Resize the tab control
IF pWindow THEN pWindow->MoveWindow(hTab, 10, 10, pWindow->ClientWidth - 20, pWindow->ClientHeight - 42, CTRUE)
' // Resize the tab pages
AfxResizeTabPages hTab
EXIT FUNCTION
CASE WM_NOTIFY
DIM nPage AS DWORD ' // Page number
DIM pTabPage AS CTabPage PTR ' // Tab page object reference
DIM tci AS TCITEMW ' // TCITEMW structure
DIM ptnmhdr AS NMHDR PTR ' // Information about a notification message
ptnmhdr = CAST(NMHDR PTR, lParam)
SELECT CASE ptnmhdr->idFrom
CASE IDC_TAB
SELECT CASE ptnmhdr->code
CASE TCN_SELCHANGE
' // Show the selected page
pTabPage = AfxCTabPagePtr(ptnmhdr->hwndFrom)
IF pTabPage THEN ShowWindow pTabPage->hTabPage, SW_SHOW
CASE TCN_SELCHANGING
' // Hide the current page
pTabPage = AfxCTabPagePtr(ptnmhdr->hwndFrom)
IF pTabPage THEN ShowWindow pTabPage->hTabPage, SW_HIDE
END SELECT
END SELECT
CASE WM_DESTROY
' // Destroy the tab pages
AfxDestroyAllTabPages(GetDlgItem(hwnd, IDC_TAB))
' // Quit the application
PostQuitMessage(0)
EXIT FUNCTION
END SELECT
FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
' // Set process DPI aware
AfxSetProcessDPIAware
DIM pWindow AS CWindow
pWindow.Create(NULL, "CWindow with a Tab Control", @WndProc)
pWindow.SetClientSize(500, 320)
pWindow.Center
' // Add a tab control
DIM hTab AS HWND = pWindow.AddControl("Tab", , IDC_TAB, "", 10, 10, pWindow.ClientWidth - 20, pWindow.ClientHeight - 42)
' // Create the first tab page
DIM pTabPage1 AS CTabPage PTR = NEW CTabPage
pTabPage1->InsertPage(hTab, 0, "Tab 1", -1, @TabPage1_WndProc)
' // Add controls to the first page
pTabPage1->AddControl("Label", pTabPage1->hTabPage, -1, "First name", 15, 15, 121, 21)
pTabPage1->AddControl("Label", pTabPage1->hTabPage, -1, "Last name", 15, 50, 121, 21)
pTabPage1->AddControl("Edit", pTabPage1->hTabPage, IDC_EDIT1, "", 165, 15, 186, 21)
pTabPage1->AddControl("Edit", pTabPage1->hTabPage, IDC_EDIT2, "", 165, 50, 186, 21)
pTabPage1->AddControl("Button", pTabPage1->hTabPage, IDC_BTNSUBMIT, "Submit", 340, 185, 76, 26, BS_DEFPUSHBUTTON)
' // Create the second tab page
DIM pTabPage2 AS CTabPage PTR = NEW CTabPage
pTabPage2->InsertPage(hTab, 1, "Tab 2", -1, @TabPage2_WndProc)
' // Add controls to the second page
DIM hComboBox AS HWND = pTabPage2->AddControl("ComboBox", pTabPage2->hTabPage, IDC_COMBO, "", 20, 20, 191, 105)
' // Create the third tab page
DIM pTabPage3 AS CTabPage PTR = NEW CTabPage
pTabPage3->InsertPage(hTab, 2, "Tab 3", -1, @TabPage3_WndProc)
' // Add controls to the third page
' DIM hListBox AS HWND = pTabPage3->AddControl("ListBox", pTabPage3->hTabPage, IDC_LISTBOX, "", 15, 20, 161, 120)
DIM hListBox AS HWND = pTabPage3->AddControl("ListBox", pTabPage3->hTabPage, IDC_LISTBOX)
pTabPage3->SetWindowPos hListBox, NULL, 15, 20, 161, 120, SWP_NOZORDER
' // Fill the controls with some data
DIM i AS LONG = 1, wszText AS WSTRING * 260
FOR i = 1 TO 9
wszText = "Item " & RIGHT("00" & STR(i), 2)
SendMessageW(hComboBox, CB_ADDSTRING, 0, CAST(LPARAM, @wszText))
SendMessageW(hListBox, LB_ADDSTRING, 0, CAST(LPARAM, @wszText))
NEXT
' // Select the first item in the combo box and the list box
SendMessageW(hComboBox, CB_SETCURSEL, 0, 0)
SendMessageW(hListBox, LB_SETCURSEL, 0, 0)
' // Add a button
pWindow.AddControl("Button", , IDCANCEL, "&Close", 415, 292, 75, 23)
' ************************************************************************
' // Make the first tab page scrollable
' // TODO: Modify the CTabPage class to set the pointer to CScrollWindow
' // to delete this class when the tab page is destroyed.
' ************************************************************************
' // Get the client size of the first page and make it greater
DIM nWidth AS LONG = pTabPage1->ClientWidth
DIM nHeight AS LONG = pTabPage1->ClientHeight
pTabPage1->SetClientSize(nWidth + 150, nHeight + 150)
' // Create an instance of the CScrollWindow class and attach the main window to it
DIM pScrollWindow AS CScrollWindow = pTabPage1->hTabPage
SetPropW pTabPage1->hTabPage,"CSCROLLWINDOWPTR", @pScrollWindow
' // Shrink the client size back to original
pTabPage1->SetClientSize(nWidth, nHeight)
' ************************************************************************
' // Display the first tab page
ShowWindow pTabPage1->hTabPage, SW_SHOW
' // Set the focus to the first tab
SendMessageW hTab, TCM_SETCURFOCUS, 0, 0
' // Dispatch messages
FUNCTION = pWindow.DoEvents(nCmdShow)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Tab page 1 window procedure
' To get a pointer to the CTabPage interface:
' DIM pTabPage AS CTabPage PTR = CAST(CTabPage PTR, GetWindowLongPtr(hwnd, 0))
' ========================================================================================
FUNCTION TabPage1_WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
SELECT CASE uMsg
CASE WM_COMMAND
SELECT CASE LOWORD(wParam)
CASE IDC_BTNSUBMIT
IF HIWORD(wParam) = BN_CLICKED THEN
MessageBoxW(hWnd, "Submit", "Tab 1", MB_OK)
EXIT FUNCTION
END IF
END SELECT
CASE WM_SIZE
DIM pScrollWindow AS CScrollWindow PTR = CAST(CScrollWindow PTR, GetPropW(hwnd, "CSCROLLWINDOWPTR"))
IF pScrollWindow THEN pScrollWindow->OnSize(wParam, lParam)
EXIT FUNCTION
CASE WM_VSCROLL
DIM pScrollWindow AS CScrollWindow PTR = CAST(CScrollWindow PTR, GetPropW(hwnd, "CSCROLLWINDOWPTR"))
IF pScrollWindow THEN pScrollWindow->OnVScroll(wParam, lParam)
EXIT FUNCTION
CASE WM_HSCROLL
DIM pScrollWindow AS CScrollWindow PTR = CAST(CScrollWindow PTR, GetPropW(hwnd, "CSCROLLWINDOWPTR"))
IF pScrollWindow THEN pScrollWindow->OnHScroll(wParam, lParam)
EXIT FUNCTION
CASE WM_DESTROY
' // Remove the property
RemovePropW hwnd,"CSCROLLWINDOWPTR"
EXIT FUNCTION
END SELECT
FUNCTION = DefWindowProcW(hwnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Tab page 2 window procedure
' To get a pointer to the CTabPage interface:
' DIM pTabPage AS CTabPage PTR = CAST(CTabPage PTR, GetWindowLongPtr(hwnd, 0))
' ========================================================================================
FUNCTION TabPage2_WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
DIM hBrush AS HBRUSH, rc AS RECT, tlb AS LOGBRUSH
SELECT CASE uMsg
CASE WM_ERASEBKGND
GetClientRect hWnd, @rc
' Create custom brush
tlb.lbStyle = BS_SOLID
tlb.lbColor = &H00CB8734
tlb.lbHatch = 0
hBrush = CreateBrushIndirect(@tlb)
' Erase background
FillRect CAST(HDC, wParam), @rc, hBrush
DeleteObject hBrush
FUNCTION = CTRUE
EXIT FUNCTION
END SELECT
FUNCTION = DefWindowProcW(hwnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Tab page 3 window procedure
' To get a pointer to the CTabPage interface:
' DIM pTabPage AS CTabPage PTR = CAST(CTabPage PTR, GetWindowLongPtr(hwnd, 0))
' ========================================================================================
FUNCTION TabPage3_WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
DIM hBrush AS HBRUSH, rc AS RECT, tlb AS LOGBRUSH
SELECT CASE uMsg
CASE WM_ERASEBKGND
GetClientRect hWnd, @rc
' Create custom brush
tlb.lbStyle = BS_SOLID
tlb.lbColor = &H0000FF00
tlb.lbHatch = 0
hBrush = CreateBrushIndirect(@tlb)
' Erase background
FillRect CAST(HDC, wParam), @rc, hBrush
DeleteObject hBrush
FUNCTION = CTRUE
EXIT FUNCTION
END SELECT
FUNCTION = DefWindowProcW(hwnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
I have added the ScrollWindowPtr property to CWindow.
Now we can use
' // Create an instance of the CScrollWindow class and attach the main window to it
DIM pScrollWindow AS CScrollWindow PTR = NEW CScrollWindow(hwndMain)
pWindow.ScrollWindowPtr = pScrollWindow
and don't have to worry about destroying the CScrollWindow class, that will be destroyed by CWindow.
Also we don't need to use SetPropW, etc., but we can use:
CASE WM_VSCROLL
DIM pScrollWindow AS CScrollWindow PTR = AfxCWindowPtr(hwnd)->ScrollWindowPtr
IF pScrollWindow THEN pScrollWindow->OnVScroll(wParam, lParam)
EXIT FUNCTION
Regarding tab pages, we will use:
' // Create an instance of the CScrollWindow class and attach the tab page handle to it
DIM pScrollWindow AS CScrollWindow PTR = NEW CScrollWindow(pTabPage1->hTabPage)
pTabPage1->ScrollWindowPtr = pScrollWindow
and as the pointer to the class is not stored in the tab page, but in the tab control...
CASE WM_VSCROLL
DIM pScrollWindow AS CScrollWindow PTR = AfxCTabPagePtr(GetParent(hwnd))->ScrollWindowPtr
IF pScrollWindow THEN pScrollWindow->OnVScroll(wParam, lParam)
EXIT FUNCTION
We could even use this compound syntax:
AfxCWindowPtr(hwnd)->ScrollWindowPtr->OnSize(wParam, lParam)
DIM pScrollWindow AS CScrollWindow PTR = AfxCWindowPtr(hwnd)->ScrollWindowPtr
IF pScrollWindow THEN pScrollWindow->OnVScroll(wParam, lParam)
but it's unsafe, because if ScrollWindowPtr returns a null pointer it will GPF.
Only with a compiler with full support for compound syntax could we use it safely. The compiler will check if the result of AfxCWindowPtr(hwnd)->ScrollWindowPtr is null or not and, if it is null, will not call OnVScroll(wParam, lParam). This is what I requested to the late Bob Zale because without this feature, the compound syntax is unsafe.