Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

MFC/C++ Helper Class for Window Resizing

0.00/5 (No votes)
21 Oct 2014 125  
Gives you total control when it comes to resizing windows

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; // a combine area of the two buttons
CRect rcBtn; // area of a single button

GetDlgItem(IDOK)->GetWindowRect(&rcBtn);
rcArea.UnionRect(&rcArea, &rcBtn);
GetDlgItem(IDCANCEL)->GetWindowRect(&rcBtn);
rcArea.UnionRect(&rcArea, &rcBtn);
ScreenToClient(&rcArea);
// create the panel
m_resizer.CreatePanel(_T("My_OK_Cancel_Panel"), &rcArea);

// set the buttons as children of the newly created panel
m_resizer.SetParent(IDOK, _T("My_OK_Cancel_Panel") );
m_resizer.SetParent(IDCANCEL, _T("My_OK_Cancel_Panel") );

// set the anchor of the newly created 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 CSplitPanels 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);

  // turning off auto paint handle
  m_resizer.SetAutoHandlePaint(FALSE); 
  m_resizer.SetShowResizeGrip(TRUE);

  bOk = m_resizer.InvokeOnResized();
  ASSERT( bOk);

  return TRUE;
}

void CTestDlg::OnPaint()
{
    CPaintDC dc(this); // device context for painting
    // Your custom drawing
    CRect rect(10, 20, 60, 60);
    CBrush brush(RGB(0,0,0));
    dc.FillRect(rect, &brush);
    // call CWndResizer::Draw method so it can draw
    // any classes derived from CVisualPanel
    m_resizer.Draw(&dc);  // <= calling Draw method
}
 
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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here