Figure 1: The application window fits the whole client window exactly plus a statusbar and some toolbars.
Figure 2: One of the toolbars is docked to the side, one floating and the statusbar is removed. The application window is resized automatically to still just fit the client window.
Introduction
I created a SDI application where I put all the controls on the view, having it derived from CFormView
. The application also had a couple of controlbars, like a statusbar and a few dockable toolbars. When the application started, I had the view and the parent frame resized to snugly fit my controls on it. However, when a user would toggle on or off my toolbars or the statusbar, the size of the frame window, it was either too big or too small to display the view. All my hard work with resizing the application is wasted. So I added some code, so my CMainFrame
class could resize itself whenever the user toggled on or off the controlbars to fit the view. Not very complicated, but it took me some time to figure out how to do it most effectively.
Details
Below are the steps to create a CMainFrame
class with automatic resizing. See also attached source code for a full demo application and more explanations.
- Create a Single Doc Application with a view derived from
CFormView
. - Using Class Wizard, for
MainFrame
, add a message handler for the message called RecalcLayout
. - Add
private
int
members called m_nControlBarsWidth
and m_nControlBarsHeight
in the CMainFrame
class and initialize them to 0
in the constructor. - Add a
private
bool
member called m_bCtrlBarsChanged
in the CMainFrame
class and initialize it to false
in the constructor. - Change the implementation of
CMainFrame::RecalcLayout
to the following:
void CMainFrame::RecalcLayout(BOOL bNotify)
{
if (m_bCtrlBarsChanged)
{
return;
}
if (!IsIconic() && !IsZoomed())
{
int nControlBarsHeightCurrent = 0;
int nControlBarsWidthCurrent = 0;
CRect rectControlBar;
RepositionBars(AFX_IDW_STATUS_BAR, AFX_IDW_STATUS_BAR,
AFX_IDW_PANE_FIRST, reposQuery, &rectControlBar, NULL, FALSE);
nControlBarsHeightCurrent += rectControlBar.Height();
RepositionBars(AFX_IDW_DOCKBAR_TOP, AFX_IDW_DOCKBAR_TOP,
AFX_IDW_PANE_FIRST, reposQuery, &rectControlBar, NULL, FALSE);
nControlBarsHeightCurrent += rectControlBar.Height();
RepositionBars(AFX_IDW_DOCKBAR_BOTTOM, AFX_IDW_DOCKBAR_BOTTOM,
AFX_IDW_PANE_FIRST, reposQuery, &rectControlBar, NULL, FALSE);
nControlBarsHeightCurrent += rectControlBar.Height();
RepositionBars(AFX_IDW_DOCKBAR_LEFT, AFX_IDW_DOCKBAR_LEFT,
AFX_IDW_PANE_FIRST, reposQuery, &rectControlBar, NULL, FALSE);
nControlBarsWidthCurrent += rectControlBar.Width();
RepositionBars(AFX_IDW_DOCKBAR_RIGHT, AFX_IDW_DOCKBAR_RIGHT,
AFX_IDW_PANE_FIRST, reposQuery, &rectControlBar, NULL, FALSE);
nControlBarsWidthCurrent += rectControlBar.Width();
if (nControlBarsHeightCurrent != m_nControlBarsHeight ||
nControlBarsWidthCurrent != m_nControlBarsWidth)
{
m_bCtrlBarsChanged = true;
CRect rectFrame;
GetWindowRect(rectFrame);
SetWindowPos(NULL, 0, 0,
rectFrame.Width() +
nControlBarsWidthCurrent - m_nControlBarsWidth,
rectFrame.Height() +
nControlBarsHeightCurrent - m_nControlBarsHeight,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
m_nControlBarsWidth = nControlBarsWidthCurrent;
m_nControlBarsHeight = nControlBarsHeightCurrent;
m_bCtrlBarsChanged = false;
}
}
CFrameWnd::RecalcLayout(bNotify);
}
Comments
CWnd::RepositionBars()
is used to calculate the width
and height
of the different bars. It needs to be called differently depending on how our controlbars are docked.
If you want to add support for CMainFrame::OnGetMinMaxInfo
(message handler for WM_GETMINMAXINFO
), you need to do some extra coding, since the min size must also be updated when the user toggles control bars on or off. See the source code for how this is implemented.
Article History
The original article was posted on 28th January, 2002. This update is done to fix a bug when the window was maximized and to handle several toolbars. Also, side docking was not handled before. I added another picture to better show the code in action.