当用户切换控件栏的显示/隐藏时, FormView 应用程序的自动调整大小
解释如何在用户切换控制栏的显示与隐藏时实现应用程序的自动调整大小。
图 1:应用程序窗口完全贴合客户端窗口,加上状态栏和一些工具栏。
图 2:其中一个工具栏停靠在侧面,一个浮动,状态栏被移除。应用程序窗口自动调整大小,仍然恰好适合客户端窗口。
引言
我创建了一个 SDI 应用程序,并将所有控件放在视图中,使其派生自 CFormView
。该应用程序还具有几个控制栏,如状态栏和几个可停靠的工具栏。当应用程序启动时,我将视图和父框架调整大小以使其紧密贴合我的控件。但是,当用户切换我的工具栏或状态栏的显示与隐藏时,框架窗口的大小,要么太大要么太小,无法显示视图。我所有调整应用程序大小的辛勤工作都白费了。所以我添加了一些代码,以便我的 CMainFrame
类可以在用户切换控制栏的显示与隐藏以适应视图时调整自身大小。虽然不是很复杂,但花了我一些时间来弄清楚如何最有效地做到这一点。
详细说明
以下是创建具有自动调整大小的 CMainFrame
类的步骤。另请参阅随附的源代码,以获取完整的演示应用程序和更多解释。
- 创建一个单文档应用程序,其视图派生自
CFormView
。 - 使用类向导,为
MainFrame
添加消息处理程序,用于名为RecalcLayout
的消息。 - 在
CMainFrame
类中添加名为m_nControlBarsWidth
和m_nControlBarsHeight
的private
int
成员,并在构造函数中将它们初始化为0
。 - 在
CMainFrame
类中添加名为m_bCtrlBarsChanged
的private
bool
成员,并在构造函数中将其初始化为false
。 - 将
CMainFrame::RecalcLayout
的实现更改为以下内容void CMainFrame::RecalcLayout(BOOL bNotify) { // RecalcLayout is called by MFC when the frame window is resized. // In this implementation of RecalcLayout, the application // call SetWindowPos, which will resize the frame window. // Therefore, if RecalcLayout is called because // the application is resizing the frame we want to ignore that call. // We use m_bCtrlBarsChanged to keep track if the application are // resizing the frame window because the user toggled the controlbars // on or off or if the user is resizing the window. if (m_bCtrlBarsChanged) { // Ignore call since it's triggered by our call to SetWindowPos. // Otherwise we will end up looping. return; } if (!IsIconic() && !IsZoomed()) { // Calculate size of controlbars (statusbar and toolbars). // We should only do this when the window is not maximized // or minimized. When maximized we should not change // the window's size, it should stay maximized. I don't think this // function can be called when window is minimized, // but if so then window size // should not be changed either. int nControlBarsHeightCurrent = 0; int nControlBarsWidthCurrent = 0; // Calculate the current total height and width of our controlbars. // We only care of a controlbar's height or width // depending on how it is docked. // We will use CWnd::RepositionBars() with // the reposQuery parameter to get // measurements of our controlbars. // A statusbar is always docked to bottom so only // height of statusbar is interesting. CRect rectControlBar; RepositionBars(AFX_IDW_STATUS_BAR, AFX_IDW_STATUS_BAR, AFX_IDW_PANE_FIRST, reposQuery, &rectControlBar, NULL, FALSE); nControlBarsHeightCurrent += rectControlBar.Height(); // Toolbars can be docked to any side of the frame so // depending on if a toolbar is added to the top or bottom // or if docked to left or right side, we need to // add check its height or width. 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) { // The size of the controlbars have changed. // Lets resize the frame window // to accommodate this change. The goal is to keep // the client window unchanged // and resize the frame instead. // Set m_bCtrlBarsChanged to true so we know // the application is resizing the // dialog, not the user. m_bCtrlBarsChanged = true; // nControlBarsWidthCurrent - m_nControlBarsWidth // is how much the bars have // changed in width and that is how much // the frame's width should be adjusted. // The calculations for height is similar. // Get the current size of the frame window. CRect rectFrame; GetWindowRect(rectFrame); // Adjust the size of the frame window. SetWindowPos(NULL, 0, 0, rectFrame.Width() + nControlBarsWidthCurrent - m_nControlBarsWidth, rectFrame.Height() + nControlBarsHeightCurrent - m_nControlBarsHeight, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); // Save the size of the controlbars for next time // the user toggles the controlbars // on or off so we know how much we should // adjust the size of the frame. m_nControlBarsWidth = nControlBarsWidthCurrent; m_nControlBarsHeight = nControlBarsHeightCurrent; // The application is finished resizing the dialog. m_bCtrlBarsChanged = false; } } CFrameWnd::RecalcLayout(bNotify); }
注释
CWnd::RepositionBars()
用于计算不同栏的 width
和 height
。它需要根据我们的控制栏的停靠方式进行不同的调用。
如果您想添加对 CMainFrame::OnGetMinMaxInfo
(WM_GETMINMAXINFO
的消息处理程序)的支持,则需要做一些额外的编码,因为当用户切换控制栏的显示与隐藏时,最小大小也必须更新。请参阅源代码以了解如何实现这一点。
文章历史
原始文章发布于 2002 年 1 月 28 日。此更新用于修复窗口最大化时的错误并处理多个工具栏。此外,之前未处理侧面停靠。我添加了另一张图片以更好地展示代码的运行情况。