Introduction
In MFC, resizing or repositioning controls could be quite bothersome. If you are familiar with the .NET platform, things are much more simplified with the use of the Anchor
and Dock
properties of the Control
class and design-time support for adding child controls to container controls. I tried to mimic some of these features of .NET, but the C++ way.
Background
There are other solutions available online (also on CodeProject.com) for this purpose. I think my solution stands out on its design, simplicity, and feature richness.
This solution allows you to do the following:
- Anchor dialog controls
- Create panels which contain other panels or UI controls
- Restrict the size of dialog controls or panels by using minimum size and maximum size properties
- Restrict the size of dialogs by using minimum size and maximum size properties
- Create horizontal or vertical split containers
- Freeze split containers so that users cannot use mouse to resize the panels
- Set a panel of split containers to be fixed so that when the dialog resizes, the fixed panel will remain unchanged
- Set the parent of dialog controls or panels (not related to Win32
SetParent
API)
- Show a resize grip on the lower right corner of the dialog using Visual Style (if the application supports it)
- Show resize grips for splitter of split containers
- Dock dialog control within its parent panel
- Create flow layout panels
Let's take a brief look at how .NET does its resizing of child controls:
Anchor
- It allows a child control to be anchored to the left, top, right, or bottom edge, or any combination of these four options.
Dock
- It allows a child control to be docked to the left, top, right, and bottom edges.
- Visual Studio Designer Support - If you place a frame control on a Windows Form and then drag a button control on top of the frame, the button becomes the actual child of the frame and grandchild of the Windows Form. But in the resource editor of MFC, if you place the frame on the dialog template and then drag a button on top of the frame control, the button is actually an immediate child of the dialog template and not the frame. This means if you move the frame, the button will not move.
SplitContainer
- Creating splitter windows has never been this simple since the invention of this control. It has two panels which can host other controls inside.
FlowLayout
- Represents a panel that dynamically lays out its contents horizontally or vertically.
So in .NET, all controls are children or grandchildren of the Form
; this creates a hierarchical structure of controls. When a parent is resized or repositioned, all its children are resized or repositioned according to their Anchor
or Dock
property settings.
In my solution, I create a hierarchical structure of rectangles (CRect
) instead. I have implemented the Anchor
, Panel
, SplitContainer
, Dock
, and FlowLayout
concepts.
There are several classes in this solution, but CWndResizer
is the only class that a developer will work with.
Typically, you will design your dialog template in the Visual Studio Resource Editor, and then in the dialog class implementation, you will have a member variable like this:
private:
CWndResizer m_resizer;
The samples included with this article uses the CDialog
class to demonstrate many features of this class. But this class can be used with any class derived from CWnd
(CDialog
, CPropertyPage
, CPropertySheet
, CFrmWnd
, CFormView
, etc.).
Before this class can do anything, you must call the Hook
method like this:
BOOL CExample1Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
BOOL bOk = m_resizer.Hook(this);
ASSERT(bOk == TRUE);
}
In this article, I will refer to this window (that is passed to the Hook
method) as "hooked-window".
By calling this method, it places a window procedure hook in the WndProc
chain.
When you call the Hook
method, it stores the client area of the hooked-window in a structure called CPanel
. A panel is mainly a rectangle area given in client coordinates of the hooked-window. A panel can have zero or more panels as children. During the creation of a panel, you assign a unique name for the panel. The name is used to refer to the panel or find a panel. The client area of the hooked-window is the root of the hierarchy, and it is named _root
.
Each panel has the Anchor
, MinSize
, and MaxSize
properties (along with some other properties). But you cannot directly set or get the properties of a panel; instead, you will use member methods of the CWndResizer
class.
The idea is that when a CPanel
is resized or repositioned, all its children are also resized and repositioned relatively.
Now, let's look at some code snippets.
The following code will anchor the OK and Cancel buttons of your dialog to the bottom-right corner:
BOOL CExample1Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
BOOL bOk = m_resizer.Hook(this);
ASSERT(bOk == TRUE);
bOk = m_resizer.SetAnchor(IDOK, ANCHOR_RIGHT | ANCHOR_BOTTOM);
ASSERT(bOk == TRUE);
bOk = m_resizer.SetAnchor(IDCANCEL, ANCHOR_RIGHT | ANCHOR_BOTTOM);
ASSERT(bOk == TRUE);
}
If you want to create a panel and set its Anchor
property to ANCHOR_HORIZONTALLY
, you will do this:
BOOL CExample1Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
BOOL bOk = m_resizer.Hook(this);
ASSERT(bOk == TRUE);
CRect rc(40, 40, 240, 240);
bOk = m_resizer.CreatePanel(_T("MyNewPanel"), &rc);
bOk = m_resizer.SetAnchor(_T("MyNewPanel"), ANCHOR_HORIZONTALLY);
ASSERT(bOk == TRUE);
ASSERT(bOk == TRUE);
}
About Clipping (Important)
This solution cannot clip any part of a dialog control or any part of a panel. A CPanel
object does not have any window (HWND
) associated with it in general. If a child panel is larger than its parent panel, the child's area that is outside the parent will be visible since neither the parent panel nor the child panel is actually a window. To circumvent this issue, we should set a reasonable minimum size of the parent panel by calling the SetMinimumSize
method so that the parent is never smaller than the child's minimum size. You should also consider setting the minimum size of the hooked-window as well such that it can contain all panels within its client area.
Using the Code
The sample application, created in VS2008, demonstrates many features of this class.
The CWndResizer
class has the following members:
Here are the details:
Method |
BOOL CreateFlowLayoutPanel(LPCTSTR panelName, const CRect * prcPanel);
BOOL CreateFlowLayoutPanel(LPCTSTR panelName, const CUIntArray * parrID, BOOL setAsChildren = FALSE);
|
Description |
Creates a flow layout panel in the client area of the hooked-window (.NET analogy). |
Parameters |
panelName |
A unique name for this panel. This name is used to refer to this panel later on. |
prcPanel |
A rectangle given in the hooked-window's client coordinate. |
parrID |
An array of dialog control IDs. This method creates a panel with the combined area of the dialog controls specified in this array. |
setAsChildren |
If this is TRUE , then this method will create panels for each of the dialog controls specified in parrID and make them children of its own. |
Return values |
TRUE |
Succeeded. |
FALSE |
Failed.
CWndResizer::Hook method hasn't been called.
-or-
panelName is not unique or is NULL .
-or-
parrID contains an invalid ID.
|
Remarks |
The FlowLayout panel arranges its contents in a horizontal or vertical flow direction. Its contents can be wrapped from one row to the next, or from one column to the next.
You can specify the flow direction by calling the value of the SetFlowDirection method.
Other panels can be a child of the FlowLayout panel. With this capability, you can construct sophisticated layouts that adapt to your dialog's dimensions at run time.
|
Method |
BOOL CreatePanel(LPCTSTR panelName, const CRect * prcPanel);
BOOL CreatePanel(LPCTSTR panelName, const CUIntArray * parrID, BOOL setAsChildren = FALSE);
BOOL CreatePanel(UINT panelID);
|
Description |
Creates a panel in the client area of the hooked-window. |
Parameters |
panelName |
An unique name for this panel. This name is used to refer to this panel later on. |
prcPanel |
A rectangle given in the hooked-window's client coordinate. |
parrID |
An array of dialog control IDs. This method creates a panel with the combined area of the dialog controls specified in this array. |
setAsChildren |
If this is TRUE , then this method will create panels for each of the dialog controls specified in parrID and make them children of its own. |
panelID |
An ID of the dialog control. |
Return values |
TRUE |
Succeeded. |
FALSE |
Failed.
CWndResizer::Hook method hasn't been called.
-or-
panelName is not unique or is NULL .
-or-
parrID contains an invalid ID.
-or-
panelID is invalid.
|
Remarks |
After a panel is created, by default, it will have the following initial settings:
- Parent name:
_T("_root")
- Anchor:
ANCHOR_LEFT | ANCHOR_TOP
- Dock:
DOCK_NONE
- MinimumSize:
cx = 10, cy = 10
- MaximumSize:
cx = 100000, cy = 100000
About BOOL CreatePanel(UINT panelID) :
You would typically use this overloaded method for ActiveX or OLE controls. Panels created by this overloaded method are resized/repositioned using the MoveWindow API. For other panels, CWndResizer uses BeginDeferWindowPos , DeferWindowPos , and EndDeferWindowPos to resize/reposition UI controls.
Note: CWndResizer creates panels internally for dialog controls when the dialog controls are referenced for the first time.
About BOOL CreatePanel(LPCTSTR panelName, const CUIntArray * parrID, BOOL setAsChildren = FALSE) :
This method works as a shortcut for some techniques. Suppose you have a very simple dialog with just the OK and the Cancel button, and you want to anchor both buttons at the bottom-right corner of the dialog, then you can do any one of the three techniques below to achieve this:
Technique 1:
m_resizer.SetAnchor(IDOK, ANCHOR_RIGHT | ANCHOR_BOTTOM);
m_resizer.SetAnchor(IDCANCEL, ANCHOR_RIGHT | ANCHOR_BOTTOM);
Technique 2:
CRect rcArea; CRect rcBtn;
GetDlgItem(IDOK)->GetWindowRect(&rcBtn);
rcArea.UnionRect(&rcArea, &rcBtn);
GetDlgItem(IDCANCEL)->GetWindowRect(&rcBtn);
rcArea.UnionRect(&rcArea, &rcBtn);
ScreenToClient(&rcArea);
m_resizer.CreatePanel(_T("My_OK_Cancel_Panel"), &rcArea);
m_resizer.SetParent(IDOK, _T("My_OK_Cancel_Panel") );
m_resizer.SetParent(IDCANCEL, _T("My_OK_Cancel_Panel") );
m_resizer.SetAnchor(_T("My_OK_Cancel_Panel"),
ANCHOR_RIGHT | ANCHOR_BOTTOM);
Technique 3:
CUIntArray arrID;
arrID.Add(IDOK);
arrID.Add(IDCANCEL);
m_resizer.CreatePanel(_T("My_OK_Cancel_Panel"),
&arrID, TRUE);
m_resizer.SetAnchor(_T("My_OK_Cancel_Panel"),
ANCHOR_RIGHT | ANCHOR_BOTTOM);
Technique 2 and Technique 3 produce exactly the same hierarchy of panels. But Technique 3 is way shorter than Technique 2.
|
Method |
BOOL CreateSplitContainer(LPCTSTR splitContainerName, LPCTSTR panelNameA, LPCTSTR panelNameB);
BOOL CreateSplitContainer(LPCTSTR splitContainerName, LPCTSTR panelNameA, UINT panelIDB);
BOOL CreateSplitContainer(LPCTSTR splitContainerName, UINT panelIDA, UINT panelIDB);
BOOL CreateSplitContainer(LPCTSTR splitContainerName, UINT panelIDA, LPCTSTR panelNameB);
|
Description |
Creates a split container (.NET analogy). |
Parameters |
splitContainerName |
A unique name for this split container. This name is used to refer to this panel later on. |
panelNameA |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel or CWndResizer::CreateSplitContainer .
If the resulting panel is a horizontal split container, this refers to the left panel of the split container. Otherwise, it is the top panel of the split container.
|
panelIDA |
An ID of a child control (window) of the hooked-window.
If the resulting panel is a horizontal split container, this refers to the left panel of the split container. Otherwise, it is the top panel of the split container.
|
panelNameB |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel or CWndResizer::CreateSplitContainer .
If the resulting panel is a horizontal split container, this refers to the right panel of the split container. Otherwise, it is the bottom panel of the split container.
|
panelIDB |
An ID of a child control (window) of the hooked-window.
If the resulting panel is a horizontal split container, this refers to the right panel of the split container. Otherwise, it is the bottom panel of the split container
|
Return values |
TRUE |
Succeeded. |
FALSE |
Failed.
CWndResizer::Hook method hasn't been called.
-or-
splitContainerName , panelNameA , panelIDA , panelNameB , or panelIDB is not unique or is NULL .
-or-
The specified panels (rectangles) overlapped.
-or-
One or all of the specified panels have already been used to create the split container.
|
Remarks |
CWndResizer determines whether the split container should be horizontal or vertical.
If the right member of the first rectangle (the second parameter) is less than the left member of the second rectangle (the third parameter), this method creates a horizontal split container. Otherwise, if the bottom member of the first rectangle (the second parameter) is less than the top member of the second rectangle (the third parameter), this method creates a vertical split container.
Definition of CSplitContainer
A CSplitContainer is comprised of two CSplitPanel s and one CSplitterPanel . A CSplitterPanel contains a CSplitterGripperPanel .
If you move your mouse over the space (CSpitterPanel ) between the two panels (CSplitPanel ) of CSplitContainer , the mouse pointer will change. Then you can press down the left mouse button and drag to resize the panels. If the parent of the panel is resized, the two panels of the splitter panel will resize proportionately.
|
Method |
BOOL GetAnchor(LPCTSTR panelName, UINT & anchor);
BOOL GetAnchor(UINT panelID, UINT & anchor);
|
Description |
Gets the anchor of the specified panel (.NET analogy). |
Parameters |
panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
panelID |
An ID of a child control (window) of the hooked-window. |
anchor |
Receive the anchor value. |
Return values |
TRUE |
Succeeded. anchor contains a valid value. |
FALSE |
Failed. anchor value should be ignored.
CWndResizer::Hook method hasn't been called.
-or-
panelName or panelID is not valid.
|
Remarks |
Retrieves the anchor value of a panel. See SetAnchor for details. |
Method |
BOOL GetAutoHandlePaint();
|
Description |
Gets whether or not paint should be automatically handled by CWndResizer . |
Return values |
TRUE |
Succeeded. anchor contains a valid value. |
FALSE |
Failed. anchor value should be ignored.
CWndResizer::Hook method hasn't been called.
-or-
panelName or panelID is not valid.
|
Remarks |
Please see SetAutoHandlePaint for details. |
Method |
BOOL GetDock(LPCTSTR panelName, UINT & uDock);
BOOL GetDock(UINT panelID, UINT & uDock);
|
Description |
Gets the anchor of the specified panel (.NET analogy). |
Parameters |
panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
panelID |
An ID of a child control (window) of the hooked-window. |
uDock |
Receive the dock value. |
Return values |
TRUE |
Succeeded. anchor contains a valid value. |
FALSE |
Failed. anchor value should be ignored.
CWndResizer::Hook method hasn't been called.
-or-
panelName or panelID is not valid.
|
Remarks |
Retrieves the dock value of a panel. See SetDock for details. |
Method |
BOOL GetFixedPanel(LPCTSTR splitContainer, short & panel); |
Description |
Gets the name of the panel that is fixed (if any) (.NET analogy). |
Parameters |
splitContainerName |
Name of the split container that was created by a prior call to CWndResizer::CreateSplitContainer . |
panel |
Receives the ID of the fixed panel. If this contains 1, then the left panel (or the top panel if splitContainer is a vertical split container) is fixed, or if it is 2, then the right panel (or the bottom panel if splitContainer is a vertical split container). Otherwise, splitContainer has no fixed panel. |
Return values |
TRUE |
Succeeded. panel contains a valid value. |
FALSE |
Failed. panel value should be ignored.
The CWndResizer::Hook method hasn't been called.
-or-
splitContainerName is not valid.
|
Remarks |
Retrieves the fixed panel ID of a a split container. See SetSetFixedPanel for details. |
Method |
BOOL GetFlowDirection(LPCTSTR flowPanelName, short & direction); |
Description |
Gets the flow direction of the FlowPanel (.NET analogy). |
Parameters |
flowPanelName |
Name of the split container that was created by a prior call to CWndResizer::CreateFlowLayoutPanel . |
direction |
Receives the flow direction of the FlowPanel . |
Return values |
TRUE |
Succeeded. direction contains a valid value. |
FALSE |
Failed. direction value should be ignored.
CWndResizer::Hook method hasn't been called.
-or-
flowPanelName is not valid.
|
Remarks |
See SetFlowDirection for details. |
Method |
BOOL GetFlowItemSpacingX(LPCTSTR flowPanelName, int & nSpace); |
Description |
Gets the spacing between items in x-direction in the FlowPanel . |
Parameters |
flowPanelName |
Name of the split container that was created by a prior call to CWndResizer::CreateFlowLayoutPanel . |
nSpace |
Receives the space in pixels. |
Return values |
TRUE |
Succeeded. panel contains a valid value. |
FALSE |
Failed. The nSpace value should be ignored.
CWndResizer::Hook method hasn't been called.
-or-
flowPanelName is not valid.
|
Remarks |
See SetFlowItemSpacingX for details. |
Method |
BOOL GetFlowItemSpacingY(LPCTSTR flowPanelName, int & nSpace); |
Description |
Gets the spacing between items in y-direction in the FlowPanel . |
Parameters |
flowPanelName |
Name of the split container that was created by a prior call to CWndResizer::CreateFlowLayoutPanel . |
nSpace |
Receives the space in pixels. |
Return values |
TRUE |
Succeeded. panel contains a valid value. |
FALSE |
Failed. nSpace value should be ignored.
CWndResizer::Hook method hasn't been called.
-or-
flowPanelName is not valid.
|
Remarks |
See SetFlowItemSpacingY for details. |
Method |
BOOL GetIsSplitterFixed(LPCTSTR splitContainerName , BOOL &fixed); |
Description |
Gets whether or not the splitter is set to fixed for a split container (.NET analogy). |
Parameters |
splitContainerName |
Name of the split container that was created by a prior call to CWndResizer::CreateSplitContainer . |
fixed |
Receives a boolean value. If this is TRUE , then the splitter is fixed; otherwise, the splitter is free to be moved by mouse. |
Return values |
TRUE |
Succeeded. fixed contains a valid value. |
FALSE |
Failed. The fixed value should be ignored.
CWndResizer::Hook method hasn't been called.
-or-
splitContainerName is not valid.
|
Remarks |
Retrieves a flag that indicates whether or not the splitter is free to be moved by mouse for a split container. See SetIsSplitterFixed for details. |
Method |
BOOL GetMaximumSize(LPCTSTR panelName, CSize & size) ;
BOOL GetMaximumSize(UINT panelID, CSize & size) ;
|
Description |
Gets the maximum size of the specified panel (.NET analogy). |
Parameters |
panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
panelID |
An ID of a child control (window) of the hooked-window. |
Return values |
TRUE |
Succeeded. size contains the valid value. |
FALSE |
Failed. The size value should be ignored.
CWndResizer::Hook method hasn't been called.
-or-
panelName or panelID is not valid.
|
Remarks |
If panelName or panelID refers to a panel that is part of the horizontal split container, then the CSize::cy member should be ignored. If panelName or panelID refers to a panel that is part of the vertical split container, then the CSize::cx member should be ignored. |
Method |
BOOL GetMinimumSize(LPCTSTR panelName, CSize & size) ;
BOOL GetMinimumSize(UINT panelID, CSize & size) ;
|
Description |
Gets the minimum size of the specified panel (.NET analogy). |
Parameters |
panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
panelID |
An ID of a child control (window) of the hooked-window. |
Return values |
TRUE |
Succeeded. size contains the valid value. |
FALSE |
Failed. The size value should be ignored.
CWndResizer::Hook method hasn't been called.
-or-
panelName or panelID is not valid.
|
Remarks |
If panelName or panelID refers to a panel that is part of a horizontal split container, then the CSize::cy member should be ignored. If panelName or panelID refers to a panel that is part of a vertical split container, then the CSize::cx member should be ignored. |
Method |
BOOL GetParent(LPCTSTR panelName, CString & parentName);
BOOL GetParent(UINT panelID, CString & parentName);
|
Description |
Gets the name of the parent of the specified panel. |
Parameters |
panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
panelID |
An ID of a child control (window) of the hooked-window. |
parentName |
Receives the name of the parent. |
Return values |
TRUE |
Succeeded. parentName contains a valid value. |
FALSE |
Failed. The size value should be ignored.
CWndResizer::Hook method hasn't been called.
-or-
panelName or panelID is not valid.
|
Remarks |
If parentName is _root , it indicates that the panel is an immediate child of the hooked-window. |
Method |
BOOL GetShowResizeGrip(); |
Description |
Gets a flag indication if a resize grip will be drawn on the lower-right corner of the hooked-window. |
Parameters |
N/A |
No parameters. |
Return values |
TRUE |
It will draw a resize grip at the bottom-right corner of the dialog/window. |
FALSE |
It will not draw a resize grip at the bottom-right corner of the dialog/window. |
Remarks |
This method never fails. |
Method |
BOOL GetShowSplitterGrip(LPCTSTR splitContainerName, BOOL & bShow); |
Description |
Gets whether or not the splitter grip is visible for a split container. |
Parameters |
splitContainerName |
Name of the split container that was created by a prior call to CWndResizer::CreateSplitContainer . |
bShow |
Receives a boolean value. If this is TRUE , then the splitter grip is visible, otherwise the splitter grip is hidden. |
Return values |
TRUE |
Succeeded. |
FALSE |
Failed. The bShow value should be ignored.
CWndResizer::Hook method hasn't been called.
-or-
splitContainerName is not valid.
|
Remarks |
N/A |
Method |
BOOL Hook(CWnd * pWnd); |
Description |
Places a hook into the WndProc chain. |
Parameters |
pWnd |
A pointer to a CWnd that you want to resize and its child controls. |
Return values |
TRUE |
Succeeded. |
FALSE |
Failed.
pWnd is not valid.
-or-
This method already has been called once.
|
Remarks |
You must call this method before calling any other method.
The window that the pWnd points to is called the "hooked-window".
|
Method |
BOOL InvokeOnResized(); |
Description |
Simulates resizing of the hooked-window. |
Parameters |
N/A |
No parameters. |
Return values |
TRUE |
Succeeded. |
FALSE |
Failed.
CWndResizer::Hook method hasn't been called.
|
Remarks |
This method sends a WM_SIZE message to the hooked-window.
After creating the necessary panels and settings anchors, you would want to call this to apply the settings initially.
|
Method |
BOOL SetAnchor(LPCTSTR panelName, UINT & anchor);
BOOL SetAnchor(UINT panelID, UINT & anchor);
|
Description |
Sets the anchor of the specified panel (.NET analogy). |
Parameters |
panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
panelID |
An ID of a child control (window) of the hooked-window. |
anchor |
Anchor value. See remarks. |
Return values |
TRUE |
Succeeded. anchor contains the valid value. |
FALSE |
Failed.
CWndResizer::Hook method hasn't been called.
-or-
panelName or panelID is not valid.
|
Remarks |
anchor could be one or more of the following:
Anchor values |
Description |
ANCHOR_LEFT |
Anchors the left edge of the panel to its parent. This is the default for a panel. |
ANCHOR_TOP |
Anchors the top edge of the panel to its parent. This is the default for a panel. |
ANCHOR_RIGHT |
Anchors the right edge of the panel to its parent. |
ANCHOR_BOTTOM |
Anchors the bottom edge of the panel to its parent. |
ANCHOR_CENTER_HORIZONTALLY |
The panel is horizontally centered within the parent rectangle. It has higher priority than ANCHOR_ALL . .NET does not have similar support. |
ANCHOR_CENTER_VERTICALLY |
The panel is vertically centered within the parent rectangle. It has higher priority than ANCHOR_ALL . .NET does not have similar support. |
ANCHOR_PRIORITY_RIGHT |
This is only useful if used with ANCHOR_HORIZONTALLY . If the width of the panel grows bigger than the maximum width allowed, by default, the panel's left edge will be anchored and right edge will be freed to adjust to the maximum size. If this is specified, then the right edge will be anchored and the left edge will be freed to adjust to the maximum size. .NET does not have similar support. |
ANCHOR_PRIORITY_BOTTOM |
This is only useful if used with ANCHOR_VERTICALLY . If the height of the panel grows bigger than the maximum height allowed, by default, the panel's top edge will be anchored and bottom edge will be freed to adjust to the maximum size. If this is specified, then the bottom edge will be anchored and the top edge will be freed to adjust to the maximum size. .NET does not have similar support. |
ANCHOR_VERTICALLY |
(ANCHOR_TOP | ANCHOR_BOTTOM) |
ANCHOR_HORIZONTALLY |
(ANCHOR_LEFT | ANCHOR_RIGHT) |
ANCHOR_ALL |
(ANCHOR_VERTICALLY | ANCHOR_HORIZONTALLY) |
Note: A panel will never grow less than its allowed minimum size and more than its allowed maximum size.
|
Method |
BOOL SetAutoHandlePaint(BOOL bHandle);
|
Description |
Sets whether or not paint should be automatically handled by CWndResizer . |
Parameters |
bHandle |
TRUE if CWndResizer is to automatically handle WM_PAINT message. Otherwise FALSE. |
Return values |
TRUE |
Always returns TRUE |
FALSE |
Never returns FALSE. |
Remarks |
Here is an example:
BOOL CTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
BOOL bOk = FALSE;
bOk = m_resizer.Hook(this);
ASSERT( bOk);
m_resizer.SetAutoHandlePaint(FALSE);
m_resizer.SetShowResizeGrip(TRUE);
bOk = m_resizer.InvokeOnResized();
ASSERT( bOk);
return TRUE;
}
void CTestDlg::OnPaint()
{
CPaintDC dc(this); CRect rect(10, 20, 60, 60);
CBrush brush(RGB(0,0,0));
dc.FillRect(rect, &brush);
m_resizer.Draw(&dc);
}
|
Method |
BOOL SetDock(LPCTSTR panelName, UINT & uDock);
BOOL SetDock(UINT panelID, UINT & uDock);
|
Description |
Sets the dock of the specified panel (.NET analogy). |
Parameters |
panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
panelID |
An ID of a child control (window) of the hooked-window. |
uDock |
Dock value. See remarks. |
Return values |
TRUE |
Succeeded. |
FALSE |
Failed.
CWndResizer::Hook method hasn't been called.
-or-
panelName or panelID is not valid.
|
Remarks |
uDock could be one of the following:
Dock values |
Description |
DOCK_NONE |
Panel will not be docked. This is the default for a panel. |
DOCK_LEFT |
Panel will be docked on the left edge of its parent. |
DOCK_TOP |
Panel will be docked on the top edge of its parent. |
DOCK_RIGHT |
Panel will be docked on the right edge of its parent. |
DOCK_BOTTOM |
Panel will be docked on the bottom edge of its parent. |
|
Method |
BOOL SetFixedPanel(LPCTSTR splitContainerName, short panel); |
Description |
Sets one of the panels of a split container to be fixed (.NET analogy). |
Parameters |
splitContainerName |
Name of a panel that was created by a prior call to CWndResizer::CreateSplitContainer . |
panel |
ID of the fixed panel. If this contains 1, then the left panel (or the top panel if splitContainer is a vertical split container) is fixed, or if it is 2, then the right panel (or the bottom panel if splitContainer is a vertical split container). Otherwise, splitContainer has no fixed panel. |
Return values |
TRUE |
Succeeded. |
FALSE |
Failed.
CWndResizer::Hook method hasn't been called.
|
Remarks |
As the hooked-window is resized, the fixed panel remains the same and the other panel resizes.
Note: A panel will never grow less than its allowed minimum size and more than its allowed maximum size.
|
Method |
BOOL SetFlowDirection(LPCTSTR flowPanelName, short direction); |
Description |
Sets the flow direction of the FlowPanel (.NET analogy). |
Parameters |
flowPanelName |
Name of the split container that was created by a prior call to CWndResizer::CreateFlowLayoutPanel . |
direction |
Flow direction of the FlowPanel . |
Return values |
TRUE |
Succeeded. |
FALSE |
Failed.
CWndResizer::Hook method hasn't been called.
-or-
flowPanelName is not valid.
|
Remarks |
Set direction to 1 to indicate left-to-right direction, any other value will indicate top-to-bottom direction. |
Method |
BOOL SetFlowItemSpacingX(LPCTSTR flowPanelName, int nSpace); |
Description |
Sets spacing between items in x-direction in FlowPanel . |
Parameters |
flowPanelName |
Name of the split container that was created by a prior call to CWndResizer::CreateFlowLayoutPanel . |
nSpace |
Space in pixels. |
Return values |
TRUE |
Succeeded. |
FALSE |
Failed. The nSpace value should be ignored.
CWndResizer::Hook method hasn't been called.
-or-
flowPanelName is not valid.
|
Remarks |
If the flow direction is left-to-right, nSpace indicates the space between two items in the x-direction, otherwise it indicates the space between two columns of items. |
Method |
BOOL SetFlowItemSpacingY(LPCTSTR flowPanelName, int & nSpace); |
Description |
Gets the spacing between items in y-direction in the FlowPanel . |
Parameters |
flowPanelName |
Name of the split container that was created by a prior call to CWndResizer::CreateFlowLayoutPanel . |
nSpace |
Space in pixels. |
Return values |
TRUE |
Succeeded. |
FALSE |
Failed. nSpace value should be ignored.
CWndResizer::Hook method hasn't been called.
-or-
flowPanelName is not valid.
|
Remarks |
If the flow direction is left-to-right, nSpace indicates the space between two items in the y-direction, otherwise it indicates the space between two rows of items. |
Method |
BOOL SetIsSplitterFixed(LPCTSTR splitContainerName , BOOL fixed); |
Description |
Sets whether or not the splitter is set to fixed for a split container (.NET analogy). |
Parameters |
splitContainerName |
Name of the split container that was created by a prior call to CWndResizer::CreateSplitContainer . |
fixed |
A boolean value. If this is TRUE , then the splitter is fixed, otherwise the splitter is free to be moved by mouse. |
Return values |
TRUE |
Succeeded. |
FALSE |
Failed.
CWndResizer::Hook method hasn't been called.
|
Remarks |
If the splitter is set to fixed, user cannot move the splitter using mouse. |
Method |
BOOL SetMaximumSize(LPCTSTR panelName, CSize & size) ;
BOOL SetMaximumSize(UINT panelID, CSize & size) ;
|
Description |
Sets the maximum size of the specified panel (.NET analogy). |
Parameters |
panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
panelID |
An ID of a child control (window) of the hooked-window. |
size |
Minimum size of the panel. |
Return values |
TRUE |
Succeeded. |
FALSE |
Failed.
CWndResizer::Hook method hasn't been called.
-or-
size is smaller than CWndResizer::GetMinimumSize .
|
Remarks |
If panelName or panelID refers to a panel that is part of a horizontal split container, then the cy member of size should be ignored. If panelName or panelID refers to a panel that is part of a vertical split container, then the cx member of size should be ignored. |
Method |
BOOL SetMinimumSize(LPCTSTR panelName, CSize & size);
BOOL SetMinimumSize(UINT panelID, CSize & size) ;
|
Description |
Sets the minimum size of the specified panel (.NET analogy). |
Parameters |
panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
panelID |
An ID of a child control (window) of the hooked-window. |
Return values |
TRUE |
Succeeded. |
FALSE |
Failed.
CWndResizer::Hook method hasn't been called.
-or-
panelName or panelID is not valid.
|
Remarks |
If panelName or panelID refers to a panel that is part of a horizontal split container, then the cy member of size should be ignored. If panelName or panelID refers to a panel that is part of a vertical split container, then the cx member of size should be ignored. |
Method |
BOOL SetParent(LPCTSTR panelName, LPCTSTR parentName);
BOOL SetParent(UINT panelID, LPCTSTR parentName);
BOOL SetParent(LPCTSTR panelName, UINT parentID);
BOOL SetParent(UINT panelID, UINT parentID);
|
Description |
Sets the name of the parent of the specified panel. |
Parameters |
panelName |
Name of a panel that was created by a prior call to CWndResizer::CreatePanel . |
panelID |
An ID of a child control (window) of the hooked-window. |
parentID |
Name of the parent. |
parentName |
Name of the parent. |
Return values |
TRUE |
Succeeded. parentName contains a valid value. |
FALSE |
Failed. The size value should be ignored.
CWndResizer::Hook method hasn't been called.
-or-
panelName , panelID , parentName , or parentName is not valid.
|
Remarks |
This should not be thought of as the Win32 SetParent API.
Set parentName to _root to indicate that the panel is an immediate child of the hooked-window.
|
Method |
void SetShowResizeGrip(BOOL show = TRUE); |
Description |
Sets a flag indication if a resize grip will be drawn on the lower-right corner of the hooked-window. |
Parameters |
show |
TRUE if the resize grip should be drawn, otherwise FALSE . |
Return values |
N/A |
No return values. |
Remarks |
This method never fails. |
Method |
BOOL SetShowSplitterGrip(LPCTSTR splitContainerName, BOOL bShow); |
Description |
Sets a flag indication whether to show or hide a gripper for the splitter of a split container. |
Parameters |
splitContainerName |
Name of the split container that was created by a prior call to CWndResizer::CreateSplitContainer . |
bShow |
TRUE if the resize grip should be drawn, otherwise hidden. |
Return values |
TRUE |
Succeeded. |
FALSE |
Failed.
CWndResizer::Hook method hasn't been called.
-or-
splitContainerName is invalid.
|
Remarks |
This method never fails. |
Method |
BOOL Unhook(); |
Parameters |
N/A |
No parameters. |
Return values |
TRUE |
Succeeded. |
FALSE |
Failed.
CWndResizer::Hook method hasn't been called.
|
Remarks |
You do not have to call this method.
This method is atomically called when the hooked-window receives a WM_DESTROY message.
|
Points of Interest
This solution does not require you to calculate any numbers on your part. This solution encapsulates all the functionalities in the CWndResizer
.
One can re-design it such that it exposes other objects (CPanel
, CSplitContainer
, etc.). The reason I designed it this way is because I did not want the user to have to know too many objects, and to simplify memory allocation and de-allocation.
Another thing is that it depends on MFC classes (CWnd
etc.); again, you can remove all references to MFC classes and use pure Win32 APIs.
History
- 13 Jan 2011: Article updated to include new features,
Docking
and FlowLayoutPanel
- 25 Feb 2011:
CWndResizer::UnHook
method changed as per member 2272507's comments
- 16 Mar 2011: Updated demo and source code
- 23 May 2011: Updated demo and source code
- 1 Aug 2011: Updated demo and source code
- 17 Aug 2011: Updated demo and source code - now supports scrolling on
CFormView
- 7 Dec 2011: Updated demo and source code
- 25 Jan 2013: New example added (Example 11) and a bug fixed
- 28 Jun 2013: Updated source code
- 9 Nov 2013: Error in Example 9 was fixed after reported by users
- 20 Oct 2014: Added three new methods:
Draw(CPaintDC * pDC)
, GetAutoHandlePaint()
and SetAutoHandlePaint(BOOL bHandle)
. These help when you have your custom painting. See documentation above for details.
-
20 Oct 2014: Reported bugs fixed