WTL 分割器和窗格容器






4.94/5 (18投票s)
2002年6月10日
4分钟阅读

220232

4509
使用 WTL 的 CSplitterWindow 和 CPaneContainer 控件创建多窗格应用程序的基础知识。
引言
本文介绍了 WTL 的 CSplitterWindow
作为应用程序窗口分隔符以及 CPaneContainer
作为子窗口宿主的基本用法。包含的演示项目是一个 3 窗格 SDI 应用程序,使用了垂直和水平分割器。每个分割器窗格都容纳了一个窗格容器,其中一个窗格容器容纳了一个编辑控件。
分割器
分割器可以是垂直的或水平的,并被用于流行的多窗格应用程序(如 Microsoft Outlook)中,以将应用程序的主窗口划分为功能区域。分割器窗口模板位于 atlsplit.h
中。此头文件提供了两个现成的分割器实现。CSplitterWindow
是标准的垂直分割器,而 CHorSplitterWindow
提供水平分割器。
3 窗格布局
Microsoft Outlook 风格的 3 窗格布局需要一个垂直分割器来将屏幕划分为左侧和右侧区域,以及一个水平分割器来将垂直分割器的右侧区域划分为顶部和底部。应用程序的主框架是垂直分割器的父窗口,而垂直分割器是水平分割器的父窗口。使用 m_vSplit
和 m_hzSplit
成员变量的基本设置代码如下:
// client rect for vertical splitter CRect rcVert; GetClientRect(&rcVert); // create the vertical splitter m_vSplit.Create(m_hWnd, rcVert, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); // client rect for horizontal splitter CRect rcHorz; GetClientRect(&rcHorz); // create the horizontal splitter. Note that vSplit is parent of hzSplit m_hzSplit.Create(m_vSplit.m_hWnd, rcHorz, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); // add the horizontal splitter to the right pane (1) of vertical splitter m_vSplit.SetSplitterPane(1, m_hzSplit);
此外,还为两个分割器设置了多个选项。下面的代码片段显示了如何设置最小分割尺寸为 35 像素。这限制了分割器可以移动到主窗口左侧或右侧边缘的接近程度。此外,初始分割器位置设置在距离左侧 85 像素处,并启用了“幽灵条”效果。在幽灵条模式下,拖动分割器时会显示深灰色条。在正常模式下,会拖动整个分割器和窗格内容。
// set the vertical splitter parameters m_vSplit.m_cxyMin = 35; // minimum size m_vSplit.SetSplitterPos(85); // from left m_vSplit.m_bFullDrag = false; // ghost bar enabled
此时,3 窗格应用程序的框架已就绪。本文下一节将介绍如何向分割器添加内容。分割器窗格可以包含控件,例如树形列表或列表视图,或者子窗口,例如对话框或窗格容器。在我们的示例项目中,我们在应用程序的每个区域添加一个窗格容器,设置各种选项,然后将一个编辑控件添加到其中一个窗格容器中。
窗格容器
窗格容器提供了一个可以托管子窗口和控件的区域。它们还提供了一个带有标题和关闭按钮的标题栏。窗格容器的常见用途是提供有用的标题和对相关程序元素的逻辑分组。CPaneContainer
类位于 atlctrlx.h
头文件中。
初始化窗格容器
使用窗格容器需要三个基本步骤。首先,使用适当的分割器句柄作为父窗口来创建容器。其次,将容器添加到适当的分割器区域。第三,设置容器标题。这些步骤在下面的代码中展示。此代码设置了垂直分割的左侧区域的窗格容器。
// create the left container m_lPane.Create(m_vSplit.m_hWnd); // add container to left pane (0) of vertical splitter m_vSplit.SetSplitterPane(0, m_lPane); // set the left pane title m_lPane.SetTitle("Left Pane");
如果需要,可以在创建窗格容器时通过在窗格容器创建语句的第二个参数中提供标题文本或标题字符串的资源 ID 来设置标题。
在创建并分配容器到分割器后,您可以设置扩展选项。扩展选项是 PANECNT_NOCLOSEBUTTON
和 PANECNT_VERTICAL
。第一个选项控制容器标题栏是否显示关闭按钮,第二个选项控制标题栏是水平排列在容器顶部,还是垂直排列在容器左侧。请注意,当标题栏设置为垂直方向时,标题不会显示。扩展选项的设置如下:
// remove the close button from the top container m_tPane.SetPaneContainerExtendedStyle(PANECNT_NOCLOSEBUTTON);
添加容器子项
如前所述,窗格容器可以托管子窗口或子控件,例如示例程序中的编辑控件。将子控件添加到窗格容器的步骤如下:
- 按常规方式创建控件
- 根据您的需求配置控件
- 将控件添加到窗格容器
您将使用类似的步骤将任何类型的窗口添加到容器中。此代码显示了如何将编辑控件添加到示例项目的底部窗格容器中。
// create and configure an edit control. Note that m_bPane is the parent m_edit.Create(m_bPane.m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, WS_EX_CLIENTEDGE); m_edit.SetFont((HFONT)GetStockObject(DEFAULT_GUI_FONT), TRUE); m_edit.SetWindowText(" Bottom Pane -- with vertical header and edit as child"); // assign the edit to the bottom container m_bPane.SetClient(m_edit.m_hWnd);
处理关闭按钮
当用户单击窗格容器的关闭按钮(标记为 X)时,该按钮会发送一个 ID_PANE_CLOSE
通知。示例项目在主框架的消息映射中捕获该通知,并使用以下例程来处理它:
LRESULT OnPaneClose(WORD, WORD, HWND hWndCtl, BOOL&) { // hide the container whose Close button was clicked ::ShowWindow(hWndCtl, SW_HIDE); // find the container's parent splitter HWND hWnd = ::GetParent(hWndCtl); CSplitterWindow* pWnd; pWnd = (CSplitterWindow*)::GetWindowLong(hWnd, GWL_ID); // take the container that was Closed out of the splitter int nCount = pWnd->m_nPanesCount; for(int nPane = 0; nPane < nCount; nPane++) { if (hWndCtl == pWnd->m_hWndPane[nPane]) { pWnd->SetSinglePaneMode(nCount - nPane - 1); break; } } return 0; }
如果您想完全移除容器而不是仅仅隐藏它,请使用 DestroyWindow(hWndCtl)
而不是 ShowWindow
。如果您想保持多窗格模式而不是切换到单窗格模式,您可能还想使用 SetSplitterPane(nPane, NULL)
而不是 SetSinglePaneMode
。
此外,您可能想用另一个子窗口或控件替换现有的子窗口或控件。在移除旧的之后,通过创建新的并将其添加到容器中来实现。如果您实现了此功能,您可能还想重写容器的 DrawButtonImage
方法以提供适当的按钮图像。
使用条款
本文提供的示例项目是免费的。您可以随意使用此代码。
本软件按“现状”分发,不提供任何形式的担保。