在软件开发中,分割窗口类是一个常见的需求,尤其是在需要在同一个窗口中展示多个视图或控件时。尽管有许多现成的分割窗口类,但它们往往有各自的限制。本文将介绍一种新的分割窗口类CSimpleSplitterWnd,它允许使用简单的CWnd派生窗口,同时具有原始CSplitterWnd的专业外观。
现有的分割窗口类可以分为两类:一类是基于原始MFC的CSplitterWnd派生的,它们外观美观,为CSplitterWnd提供了许多优势,但限制了只能使用CView派生的窗口;另一类则可以在各种窗口中使用,但在编译和测试过程中,它们的外观并不如CSplitterWnd那样美观。因此,本文提供了一种新的类,它不派生自CSplitterWnd,允许使用简单的CWnd派生窗口,同时保持了CSplitterWnd的专业外观。
CSimpleSplitterWnd的设计目标是模拟CSplitterWnd的基本功能。使用这个类,可以创建多个排列成水平或垂直的窗格。它不提供自动分割、共享滚动条和交叉跟踪器等功能。虽然这些功能可能对某些人很有用,但作者认为它们并不是非常必要。CSimpleSplitter与CSplitterWnd相比,窗格窗口是相互独立的,因此它们也负责自己的滚动和边框。
分割器的布局在构造函数中设置:
CSimpleSplitter(int nPanes, UINT nOrientation = SSP_HORZ, int nMinSize = 30, int nBarThickness = 3);
其中nPanes是窗格的数量,nOrientation应该是SSP_HORZ或SSP_VERT,nMinSize是任何窗格的最小高度(或宽度),在调整布局算法时很重要,nBarThickness是窗格之间条的厚度。所有这些属性在分割器实例的生命周期内都是固定的。创建分割器及其窗格的过程是直接的:
BOOL Create(CWnd* pParent, UINT nID = AFX_IDW_PANE_FIRST);
BOOL CreatePane(int nIndex, CWnd* pPaneWnd, DWORD dwStyle, DWORD dwExStyle, LPCTSTR lpszClassName = NULL);
窗格从0索引到nPanes - 1。参数dwStyle、dwExStyle和lpszClassName传递给pPaneWnd->CreateEx()。例如,可以在此处指定窗格的边框。在演示应用程序中,“大”窗格有WS_EX_CLIENTEDGE扩展样式,而三个“平面”窗格有WS_EX_STATICEDGE扩展样式。分割条本身只是一个灰色矩形,因此如果不指定任何边缘并将灰色背景设置为窗格窗口,也可以在对话框中使用分割器。
以下五个方法与CSplitterWnd方法类似,因此不需要特殊文档:
int GetPaneCount() const;
void SetPane(int nIndex, CWnd* pPaneWnd);
CWnd* GetPane(int nIndex) const;
virtual void SetActivePane(int nIndex);
CWnd* GetActivePane(int* pIndex) const;
但是,设置窗格宽度或高度使用不同的技术:
void SetPaneSizes(const int* sizes);
传递一个nPanes整数数组。它们指定窗格的相对大小。可以使用任何比例尺,窗格按数组成员的总和成比例调整大小。当调整整个分割器大小时,窗格的大小会按它们实际大小成比例变化。如果需要,它们会调整到m_nMinSize。
使用这些函数,可以检索分割器客户端坐标中窗格的位置。条从1索引到nPanes - 1。
void GetPaneRect(int nIndex, CRect& rcPane) const;
void GetBarRect(int nIndex, CRect& rcBar) const;
这就是全部。查看SimpleSplitterApp演示,特别是CMainFrame::OnCreate代码,将了解一切!
在编写分割器时,作者对框架如何重绘调整大小的窗口感到困惑。如果调整顶部或左侧边框,框架首先只移动窗口内容到相应的方向,然后调用OnPaint()。因此,窗口被重绘了两次,在分割器中看起来很难。幸运的是,有一个消息处理程序CWnd::OnWindowPosChanging。如果看到SWP_NOCOPYBITS,可以避免内容的初始移动,如在CSimpleSplitter和CChildWnd代码中所见。认为这在许多其他情况下的分割器中也很有用。