65.9K
CodeProject 正在变化。 阅读更多。
Home

MFC/C++ 窗口大小调整辅助类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (138投票s)

2010年11月5日

CPOL

25分钟阅读

viewsIcon

750915

downloadIcon

18313

在调整窗口大小时为您提供完全控制

引言

在MFC中,调整控件大小或重新定位控件可能相当麻烦。如果您熟悉.NET平台,使用Control类的AnchorDock属性以及用于向容器控件添加子控件的设计时支持,事情会简化得多。我尝试模仿.NET的某些功能,但采用C++的方式。

背景

网上有其他类似的解决方案(也在CodeProject.com上)。我认为我的解决方案在设计、简洁性和功能丰富性方面脱颖而出。

此解决方案允许您执行以下操作

  • 锚定对话框控件
  • 创建包含其他面板或UI控件的面板
  • 通过使用最小尺寸和最大尺寸属性来限制对话框控件或面板的大小
  • 通过使用最小尺寸和最大尺寸属性来限制对话框的大小
  • 创建水平或垂直分隔容器
  • 冻结分隔容器,以便用户无法使用鼠标调整面板大小
  • 设置分隔容器的固定面板,以便在对话框调整大小时,固定面板将保持不变
  • 设置对话框控件或面板的父级(与Win32 SetParent API无关)
  • 使用Visual Style在对话框的右下角显示一个调整大小的抓手(如果应用程序支持)
  • 为分隔容器的分隔器显示调整大小的抓手
  • 将对话框控件停靠在其父面板内
  • 创建流式布局面板

让我们简要看一下.NET是如何调整子控件大小的

  • Anchor - 它允许子控件锚定到左、上、右或底部边缘,或这些边缘的任何组合。
  • Dock - 它允许子控件停靠在左、上、右和底部边缘。
  • Visual Studio 设计器支持 - 如果您将框架控件放在Windows窗体上,然后将按钮控件拖到框架上,该按钮将成为框架的实际子控件,以及Windows窗体的孙子控件。但在MFC的资源编辑器中,如果您将框架放在对话框模板上,然后将按钮拖到框架控件上,该按钮实际上是对话框模板的直接子控件,而不是框架的子控件。这意味着如果您移动框架,按钮将不会移动。
  • SplitContainer - 自该控件发明以来,创建分隔器窗口从未如此简单。它有两个面板,可以容纳其他控件。
  • FlowLayout - 表示一个动态地水平或垂直排列其内容的面板。

因此,在.NET中,所有控件都是Form的子控件或孙子控件;这创建了一个控件的层次结构。当父控件的大小被调整或重新定位时,其所有子控件会根据它们的AnchorDock属性设置进行调整大小或重新定位。

在我的解决方案中,我创建了一个矩形(CRect)的层次结构。我实现了AnchorPanelSplitContainerDockFlowLayout的概念。

此解决方案中有几个类,但CWndResizer是开发人员唯一会使用的类。

通常,您会在Visual Studio资源编辑器中设计对话框模板,然后在对话框类的实现中,您会有一个类似这样的成员变量

private:
    CWndResizer m_resizer;

本文随附的示例使用CDialog类来演示该类的许多功能。但该类可以与任何派生自CWnd的类一起使用(CDialogCPropertyPageCPropertySheetCFrmWndCFormView等)。

在此类执行任何操作之前,您必须调用Hook方法,如下所示

BOOL CExample1Dlg::OnInitDialog()
{
  CDialog::OnInitDialog();
  BOOL bOk = m_resizer.Hook(this);
  ASSERT(bOk == TRUE);
}

在本文中,我将把这个窗口(传递给Hook方法)称为“钩接窗口”。

通过调用此方法,它会将一个窗口过程钩子放在WndProc链中。

当您调用Hook方法时,它会将钩接窗口的客户端区域存储在一个名为CPanel的结构中。面板主要是指在钩接窗口的客户端坐标中给定的矩形区域。一个面板可以有零个或多个子面板。在创建面板时,您为面板指定一个唯一的名称。该名称用于稍后引用面板或查找面板。钩接窗口的客户端区域是层次结构的根,其名称为_root

每个面板都有AnchorMinSizeMaxSize属性(以及其他一些属性)。但您不能直接设置或获取面板的属性;相反,您将使用CWndResizer类的成员方法。

其思想是,当CPanel的大小被调整或重新定位时,它的所有子控件也会相对地调整大小和重新定位。

现在,让我们看一些代码片段。

以下代码会将对话框的“确定”和“取消”按钮锚定到右下角

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);
}

如果您想创建一个面板并将其Anchor属性设置为ANCHOR_HORIZONTALLY,您可以这样做

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);
}

关于剪裁(重要)

此解决方案无法剪裁对话框控件的任何部分或面板的任何部分。CPanel对象通常不关联任何窗口(HWND)。如果子面板比其父面板大,则子面板在父面板外部的区域将可见,因为父面板和子面板都不是实际的窗口。为了规避此问题,我们应该通过调用SetMinimumSize方法设置父面板的合理最小尺寸,以便父面板永远不会小于子面板的最小尺寸。您还应该考虑设置钩接窗口的最小尺寸,以便它能够包含其客户端区域内的所有面板。

Using the Code

在VS2008中创建的示例应用程序演示了该类的许多功能。

CWndResizer类具有以下成员

以下是详细信息

方法

BOOL CreateFlowLayoutPanel(LPCTSTR panelName, const CRect * prcPanel);

BOOL CreateFlowLayoutPanel(LPCTSTR panelName, const CUIntArray * parrID, BOOL setAsChildren = FALSE);

描述 在钩接窗口的客户端区域创建流式布局面板(.NET类比)。
参数 panelName 此面板的唯一名称。此名称用于稍后引用此面板。
prcPanel 在钩接窗口的客户端坐标中给出的矩形。
parrID 对话框控件ID的数组。此方法将创建面板,其区域是指定数组中的对话框控件的组合区域。
setAsChildren 如果为TRUE,则此方法将为parrID中指定的每个对话框控件创建面板,并使其成为其自身的子控件。
返回值 TRUE 成功。
FALSE 失败。

尚未调用CWndResizer::Hook方法。

-或-

panelName不是唯一的或为NULL

-或-

parrID包含无效ID。

备注

FlowLayout面板根据水平或垂直流方向排列其内容。其内容可以从一行换到下一行,或从一列换到下一列。

您可以通过调用SetFlowDirection方法的返回值来指定流方向。

其他面板可以是FlowLayout面板的子面板。通过此功能,您可以构建复杂的布局,使其在运行时适应您的对话框尺寸。

方法

BOOL CreatePanel(LPCTSTR panelName, const CRect * prcPanel);

BOOL CreatePanel(LPCTSTR panelName, const CUIntArray * parrID, BOOL setAsChildren = FALSE);

BOOL CreatePanel(UINT panelID);

描述 在钩接窗口的客户端区域创建面板。
参数 panelName 此面板的唯一名称。此名称用于稍后引用此面板。
prcPanel 在钩接窗口的客户端坐标中给出的矩形。
parrID 对话框控件ID的数组。此方法将创建面板,其区域是指定数组中的对话框控件的组合区域。
setAsChildren 如果为TRUE,则此方法将为parrID中指定的每个对话框控件创建面板,并使其成为其自身的子控件。
panelID 对话框控件的ID。
返回值 TRUE 成功。
FALSE 失败。

尚未调用CWndResizer::Hook方法。

-或-

panelName不是唯一的或为NULL

-或-

parrID包含无效ID。

-或-

panelID无效。

备注

创建面板后,默认情况下,它将具有以下初始设置

  • 父面板名称:_T("_root")
  • 锚定:ANCHOR_LEFT | ANCHOR_TOP
  • 停靠:DOCK_NONE
  • 最小尺寸:cx = 10, cy = 10
  • 最大尺寸:cx = 100000, cy = 100000

关于BOOL CreatePanel(UINT panelID)

您通常会为ActiveX或OLE控件使用此重载方法。通过此重载方法创建的面板使用MoveWindow API进行调整大小/重新定位。对于其他面板,CWndResizer使用BeginDeferWindowPosDeferWindowPosEndDeferWindowPos来调整UI控件的大小/重新定位。

注意:当对话框控件首次被引用时,CWndResizer会在内部为它们创建面板。

关于BOOL CreatePanel(LPCTSTR panelName, const CUIntArray * parrID, BOOL setAsChildren = FALSE)

此方法可用作某些技术的快捷方式。假设您有一个非常简单的对话框,只有一个“确定”和“取消”按钮,并且您想将这两个按钮锚定到对话框的右下角,那么您可以执行以下三种技术中的任何一种来实现此目的

技术 1

m_resizer.SetAnchor(IDOK, ANCHOR_RIGHT | ANCHOR_BOTTOM);
m_resizer.SetAnchor(IDCANCEL, ANCHOR_RIGHT | ANCHOR_BOTTOM);

技术 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);

技术 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);

技术2和技术3产生完全相同的面板层次结构。但技术3比技术2短得多。

方法

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);

描述 创建分隔容器(.NET类比)。
参数 splitContainerName 此分隔容器的唯一名称。此名称用于稍后引用此面板。
panelNameA 通过先前调用CWndResizer::CreatePanelCWndResizer::CreateSplitContainer创建的面板的名称。

如果结果面板是水平分隔容器,则此参数指向分隔容器的左面板。否则,它是分隔容器的顶面板。

panelIDA 钩接窗口的子控件(窗口)的ID。

如果结果面板是水平分隔容器,则此参数指向分隔容器的左面板。否则,它是分隔容器的顶面板。

panelNameB

通过先前调用CWndResizer::CreatePanelCWndResizer::CreateSplitContainer创建的面板的名称。

如果结果面板是水平分隔容器,则此参数指向分隔容器的右面板。否则,它是分隔容器的底面板。

panelIDB

钩接窗口的子控件(窗口)的ID。

如果结果面板是水平分隔容器,则此参数指向分隔容器的右面板。否则,它是分隔容器的底面板。

返回值 TRUE 成功。
FALSE

失败。

尚未调用CWndResizer::Hook方法。

-或-

splitContainerNamepanelNameApanelIDApanelNameBpanelIDB不是唯一的或为NULL

-或-

指定的面板(矩形)发生重叠。

-或-

一个或所有指定的面板已被用于创建分隔容器。

备注

CWndResizer确定分隔容器应该是水平的还是垂直的。

如果第一个矩形(第二个参数)的right成员小于第二个矩形(第三个参数)的left成员,则此方法创建一个水平分隔容器。否则,如果第一个矩形(第二个参数)的bottom成员小于第二个矩形(第三个参数)的top成员,则此方法创建一个垂直分隔容器。

CSplitContainer的定义

CSplitContainer由两个CSplitPanel和一个CSplitterPanel组成。CSplitterPanel包含一个CSplitterGripperPanel

如果您将鼠标移到CSplitContainer的两个面板(CSplitPanel)之间的空间(CSpitterPanel)上,鼠标指针会改变。然后您可以按下鼠标左键并拖动以调整面板大小。如果面板的父级被调整大小,则分隔器面板的两个面板会成比例地调整大小。

方法

BOOL GetAnchor(LPCTSTR panelName, UINT & anchor);

BOOL GetAnchor(UINT panelID, UINT & anchor);

描述 获取指定面板的锚定(.NET类比)。
参数 panelName 通过先前调用CWndResizer::CreatePanel创建的面板的名称。
panelID 钩接窗口的子控件(窗口)的ID。
anchor 接收锚定值。
返回值 TRUE 成功。anchor包含有效值。
FALSE

失败。应忽略anchor值。

尚未调用CWndResizer::Hook方法。

-或-

panelNamepanelID无效。

备注 检索面板的锚定值。有关详细信息,请参阅SetAnchor
方法

BOOL GetAutoHandlePaint();

描述 获取CWndResizer是否自动处理绘制。
返回值 TRUE 成功。anchor包含有效值。
FALSE

失败。应忽略anchor值。

尚未调用CWndResizer::Hook方法。

-或-

panelNamepanelID无效。

备注 请参阅SetAutoHandlePaint以获取详细信息。
方法

BOOL GetDock(LPCTSTR panelName, UINT & uDock);

BOOL GetDock(UINT panelID, UINT & uDock);

描述 获取指定面板的锚定(.NET类比)。
参数 panelName 通过先前调用CWndResizer::CreatePanel创建的面板的名称。
panelID 钩接窗口的子控件(窗口)的ID。
uDock 接收停靠值。
返回值 TRUE 成功。anchor包含有效值。
FALSE

失败。应忽略anchor值。

尚未调用CWndResizer::Hook方法。

-或-

panelNamepanelID无效。

备注 检索面板的停靠值。有关详细信息,请参阅SetDock
方法 BOOL GetFixedPanel(LPCTSTR splitContainer, short & panel);
描述 获取固定面板的名称(如果有)(.NET类比)。
参数 splitContainerName 通过先前调用CWndResizer::CreateSplitContainer创建的分隔容器的名称。
panel 接收固定面板的ID。如果包含1,则左侧面板(或垂直分隔容器的顶部面板)是固定的,如果包含2,则右侧面板(或垂直分隔容器的底部面板)是固定的。否则,splitContainer没有固定面板。
返回值 TRUE 成功。panel包含有效值。
FALSE

失败。应忽略panel值。

尚未调用CWndResizer::Hook方法。

-或-

splitContainerName无效。

备注 检索分隔容器的固定面板ID。有关详细信息,请参阅SetSetFixedPanel
方法 BOOL GetFlowDirection(LPCTSTR flowPanelName, short & direction);
描述 获取FlowPanel的流方向(.NET类比)。
参数 flowPanelName 通过先前调用CWndResizer::CreateFlowLayoutPanel创建的分隔容器的名称。
direction 接收FlowPanel的流方向。
返回值 TRUE 成功。direction包含有效值。
FALSE

失败。应忽略direction值。

尚未调用CWndResizer::Hook方法。

-或-

flowPanelName无效。

备注 请参阅SetFlowDirection以获取详细信息。
方法 BOOL GetFlowItemSpacingX(LPCTSTR flowPanelName, int & nSpace);
描述 获取FlowPanel中x方向的项之间的间距。
参数 flowPanelName 通过先前调用CWndResizer::CreateFlowLayoutPanel创建的分隔容器的名称。
nSpace 接收以像素为单位的间距。
返回值 TRUE 成功。panel包含有效值。
FALSE

失败。应忽略nSpace值。

尚未调用CWndResizer::Hook方法。

-或-

flowPanelName无效。

备注 请参阅SetFlowItemSpacingX以获取详细信息。
方法 BOOL GetFlowItemSpacingY(LPCTSTR flowPanelName, int & nSpace);
描述 获取FlowPanel中y方向的项之间的间距。
参数 flowPanelName 通过先前调用CWndResizer::CreateFlowLayoutPanel创建的分隔容器的名称。
nSpace 接收以像素为单位的间距。
返回值 TRUE 成功。panel包含有效值。
FALSE

失败。应忽略nSpace值。

尚未调用CWndResizer::Hook方法。

-或-

flowPanelName无效。

备注 请参阅SetFlowItemSpacingY以获取详细信息。
方法 BOOL GetIsSplitterFixed(LPCTSTR splitContainerName , BOOL &fixed);
描述 获取分隔容器的分隔器是否设置为固定(.NET类比)。
参数 splitContainerName 通过先前调用CWndResizer::CreateSplitContainer创建的分隔容器的名称。
fixed 接收一个布尔值。如果为TRUE,则分隔器是固定的;否则,分隔器可以自由地通过鼠标移动。
返回值 TRUE 成功。fixed包含有效值。
FALSE

失败。应忽略fixed值。

尚未调用CWndResizer::Hook方法。

-或-

splitContainerName无效。

备注 检索一个标志,该标志指示分隔容器的分隔器是否可以自由地通过鼠标移动。有关详细信息,请参阅SetIsSplitterFixed
方法

BOOL GetMaximumSize(LPCTSTR panelName, CSize & size) ;

BOOL GetMaximumSize(UINT panelID, CSize & size) ;

描述 获取指定面板的最大尺寸(.NET类比)。
参数 panelName 通过先前调用CWndResizer::CreatePanel创建的面板的名称。
panelID 钩接窗口的子控件(窗口)的ID。
返回值 TRUE 成功。size包含有效值。
FALSE

失败。应忽略size值。

尚未调用CWndResizer::Hook方法。

-或-

panelNamepanelID无效。

备注 如果panelNamepanelID引用的是水平分隔容器的面板,则应忽略CSize::cy成员。如果panelNamepanelID引用的是垂直分隔容器的面板,则应忽略CSize::cx成员。
方法

BOOL GetMinimumSize(LPCTSTR panelName, CSize & size) ;

BOOL GetMinimumSize(UINT panelID, CSize & size) ;

描述 获取指定面板的最小尺寸(.NET类比)。
参数 panelName 通过先前调用CWndResizer::CreatePanel创建的面板的名称。
panelID 钩接窗口的子控件(窗口)的ID。
返回值 TRUE 成功。size包含有效值。
FALSE 失败。应忽略size值。

尚未调用CWndResizer::Hook方法。

-或-

panelNamepanelID无效。

备注 如果panelNamepanelID引用的是水平分隔容器的面板,则应忽略CSize::cy成员。如果panelNamepanelID引用的是垂直分隔容器的面板,则应忽略CSize::cx成员。
方法

BOOL GetParent(LPCTSTR panelName, CString & parentName);

BOOL GetParent(UINT panelID, CString & parentName);

描述 获取指定面板的父级名称。
参数 panelName 通过先前调用CWndResizer::CreatePanel创建的面板的名称。
panelID 钩接窗口的子控件(窗口)的ID。
parentName 接收父级的名称。
返回值 TRUE 成功。parentName包含有效值。
FALSE

失败。应忽略size值。

尚未调用CWndResizer::Hook方法。

-或-

panelNamepanelID无效。

备注 如果parentName_root,则表示该面板是钩接窗口的直接子级。
方法 BOOL GetShowResizeGrip();
描述 获取一个标志,指示是否在钩接窗口的右下角绘制调整大小的抓手。
参数 N/A 无参数。
返回值 TRUE 它将在对话框/窗口的右下角绘制一个调整大小的抓手。
FALSE 它将在对话框/窗口的右下角绘制调整大小的抓手。
备注 此方法从不失败。
方法 BOOL GetShowSplitterGrip(LPCTSTR splitContainerName, BOOL & bShow);
描述 获取分隔容器的分隔器抓手是否可见。
参数 splitContainerName 通过先前调用CWndResizer::CreateSplitContainer创建的分隔容器的名称。
bShow 接收一个布尔值。如果为TRUE,则分隔器抓手可见;否则,分隔器抓手隐藏。
返回值 TRUE 成功。
FALSE

失败。应忽略bShow值。

尚未调用CWndResizer::Hook方法。

-或-

splitContainerName无效。

备注 N/A
方法 BOOL Hook(CWnd * pWnd);
描述 将钩子放入WndProc链中。
参数 pWnd 指向您要调整大小的CWnd及其子控件的指针。
返回值 TRUE 成功。
FALSE

失败。

pWnd无效。

-或-

此方法已调用一次。

备注

在调用任何其他方法之前,您必须调用此方法。

pWnd指向的窗口称为“钩接窗口”。

方法 BOOL InvokeOnResized();
描述 模拟钩接窗口的大小调整。
参数 N/A 无参数。
返回值 TRUE 成功。
FALSE

失败。

尚未调用CWndResizer::Hook方法。

备注

此方法向钩接窗口发送WM_SIZE消息。

在创建了必要的面板并设置了锚定之后,您可能希望调用此方法来初始应用设置。

方法

BOOL SetAnchor(LPCTSTR panelName, UINT & anchor);

BOOL SetAnchor(UINT panelID, UINT & anchor);

描述 设置指定面板的锚定(.NET类比)。
参数 panelName 通过先前调用CWndResizer::CreatePanel创建的面板的名称。
panelID 钩接窗口的子控件(窗口)的ID。
anchor 锚定值。请参阅备注。
返回值 TRUE 成功。anchor包含有效值。
FALSE

失败。

尚未调用CWndResizer::Hook方法。

-或-

panelNamepanelID无效。

备注

anchor可以是一个或多个以下值

锚定值 描述
ANCHOR_LEFT 将面板的左边缘锚定到其父级。这是面板的默认值。
ANCHOR_TOP 将面板的顶边缘锚定到其父级。这是面板的默认值。
ANCHOR_RIGHT 将面板的右边缘锚定到其父级。
ANCHOR_BOTTOM 将面板的底边缘锚定到其父级。
ANCHOR_CENTER_HORIZONTALLY 面板在父矩形中水平居中。它的优先级高于ANCHOR_ALL.NET不支持类似功能。
ANCHOR_CENTER_VERTICALLY 面板在父矩形中垂直居中。它的优先级高于ANCHOR_ALL.NET不支持类似功能。
ANCHOR_PRIORITY_RIGHT 这仅在使用ANCHOR_HORIZONTALLY时才有用。如果面板的宽度大于允许的最大宽度,默认情况下,面板的左边缘将被锚定,右边缘将被释放以调整到最大尺寸。如果指定了此项,则右边缘将被锚定,左边缘将被释放以调整到最大尺寸。.NET不支持类似功能。
ANCHOR_PRIORITY_BOTTOM 这仅在使用ANCHOR_VERTICALLY时才有用。如果面板的高度大于允许的最大高度,默认情况下,面板的顶边缘将被锚定,底边缘将被释放以调整到最大尺寸。如果指定了此项,则底边缘将被锚定,顶边缘将被释放以调整到最大尺寸。.NET不支持类似功能。
ANCHOR_VERTICALLY (ANCHOR_TOP | ANCHOR_BOTTOM)
ANCHOR_HORIZONTALLY (ANCHOR_LEFT | ANCHOR_RIGHT)
ANCHOR_ALL (ANCHOR_VERTICALLY | ANCHOR_HORIZONTALLY)

注意:面板永远不会小于其允许的最小尺寸,也不会大于其允许的最大尺寸。

方法

BOOL SetAutoHandlePaint(BOOL bHandle);

描述 设置CWndResizer是否自动处理绘制。
参数 bHandle 如果CWndResizer要自动处理WM_PAINT消息,则为TRUE;否则为FALSE
返回值 TRUE 始终返回TRUE
FALSE 从不返回FALSE。
备注 这是一个例子。
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
}
 
方法

BOOL SetDock(LPCTSTR panelName, UINT & uDock);

BOOL SetDock(UINT panelID, UINT & uDock);

描述 设置指定面板的停靠(.NET类比)。
参数 panelName 通过先前调用CWndResizer::CreatePanel创建的面板的名称。
panelID 钩接窗口的子控件(窗口)的ID。
uDock 停靠值。请参阅备注。
返回值 TRUE 成功。
FALSE

失败。

尚未调用CWndResizer::Hook方法。

-或-

panelNamepanelID无效。

备注

uDock可以是以下之一

停靠值 描述
DOCK_NONE 面板将不会被停靠。这是面板的默认值。
DOCK_LEFT 面板将停靠在其父级的左边缘。
DOCK_TOP 面板将停靠在其父级的顶边缘。
DOCK_RIGHT 面板将停靠在其父级的右边缘。
DOCK_BOTTOM 面板将停靠在其父级的底边缘。
方法 BOOL SetFixedPanel(LPCTSTR splitContainerName, short panel);
描述 设置分隔容器的一个面板为固定(.NET类比)。
参数 splitContainerName 通过先前调用CWndResizer::CreateSplitContainer创建的分隔容器的名称。
panel 固定面板的ID。如果包含1,则左侧面板(或垂直分隔容器的顶部面板)是固定的,如果包含2,则右侧面板(或垂直分隔容器的底部面板)是固定的。否则,splitContainer没有固定面板。
返回值 TRUE 成功。
FALSE 失败。

尚未调用CWndResizer::Hook方法。

备注

随着钩接窗口的大小被调整,固定面板保持不变,而另一个面板会调整大小。

注意:面板永远不会小于其允许的最小尺寸,也不会大于其允许的最大尺寸。

方法 BOOL SetFlowDirection(LPCTSTR flowPanelName, short direction);
描述 设置FlowPanel的流方向(.NET类比)。
参数 flowPanelName 通过先前调用CWndResizer::CreateFlowLayoutPanel创建的分隔容器的名称。
direction FlowPanel的流方向。
返回值 TRUE 成功。
FALSE

失败。

尚未调用CWndResizer::Hook方法。

-或-

flowPanelName无效。

备注 direction设置为1表示从左到右的方向,任何其他值都表示从上到下的方向。
方法 BOOL SetFlowItemSpacingX(LPCTSTR flowPanelName, int nSpace);
描述 设置FlowPanel中x方向项之间的间距。
参数 flowPanelName 通过先前调用CWndResizer::CreateFlowLayoutPanel创建的分隔容器的名称。
nSpace 以像素为单位的间距。
返回值 TRUE 成功。
FALSE

失败。应忽略nSpace值。

尚未调用CWndResizer::Hook方法。

-或-

flowPanelName无效。

备注 如果流方向是从左到右,则nSpace表示x方向上两个项之间的间距,否则表示项的两个列之间的间距。
方法 BOOL SetFlowItemSpacingY(LPCTSTR flowPanelName, int & nSpace);
描述 获取FlowPanel中y方向的项之间的间距。
参数 flowPanelName 通过先前调用CWndResizer::CreateFlowLayoutPanel创建的分隔容器的名称。
nSpace 以像素为单位的间距。
返回值 TRUE 成功。
FALSE

失败。应忽略nSpace值。

尚未调用CWndResizer::Hook方法。

-或-

flowPanelName无效。

备注 如果流方向是从左到右,则nSpace表示y方向上两个项之间的间距,否则表示项的两个行之间的间距。
方法 BOOL SetIsSplitterFixed(LPCTSTR splitContainerName , BOOL fixed);
描述 设置分隔容器的分隔器是否设置为固定(.NET类比)。
参数 splitContainerName 通过先前调用CWndResizer::CreateSplitContainer创建的分隔容器的名称。
fixed 一个布尔值。如果为TRUE,则分隔器是固定的;否则,分隔器可以自由地通过鼠标移动。
返回值 TRUE 成功。
FALSE

失败。

尚未调用CWndResizer::Hook方法。

备注 如果分隔器设置为固定,用户将无法使用鼠标移动分隔器。
方法

BOOL SetMaximumSize(LPCTSTR panelName, CSize & size) ;

BOOL SetMaximumSize(UINT panelID, CSize & size) ;

描述 设置指定面板的最大尺寸(.NET类比)。
参数 panelName 通过先前调用CWndResizer::CreatePanel创建的面板的名称。
panelID 钩接窗口的子控件(窗口)的ID。
size 面板的最小尺寸。
返回值 TRUE 成功。
FALSE

失败。

尚未调用CWndResizer::Hook方法。

-或-

size小于CWndResizer::GetMinimumSize

备注 如果panelNamepanelID引用的是水平分隔容器的面板,则应忽略sizecy成员。如果panelNamepanelID引用的是垂直分隔容器的面板,则应忽略sizecx成员。
方法

BOOL SetMinimumSize(LPCTSTR panelName, CSize & size);

BOOL SetMinimumSize(UINT panelID, CSize & size) ;

描述 设置指定面板的最小尺寸(.NET类比)。
参数 panelName 通过先前调用CWndResizer::CreatePanel创建的面板的名称。
panelID 钩接窗口的子控件(窗口)的ID。
返回值 TRUE 成功。
FALSE

失败。

尚未调用CWndResizer::Hook方法。

-或-

panelNamepanelID无效。

备注 如果panelNamepanelID引用的是水平分隔容器的面板,则应忽略sizecy成员。如果panelNamepanelID引用的是垂直分隔容器的面板,则应忽略sizecx成员。
方法

BOOL SetParent(LPCTSTR panelName, LPCTSTR parentName);

BOOL SetParent(UINT panelID, LPCTSTR parentName);

BOOL SetParent(LPCTSTR panelName, UINT parentID);

BOOL SetParent(UINT panelID, UINT parentID);

描述 设置指定面板的父级名称。
参数 panelName 通过先前调用CWndResizer::CreatePanel创建的面板的名称。
panelID 钩接窗口的子控件(窗口)的ID。
parentID 父级的名称。
parentName 父级的名称。
返回值 TRUE 成功。parentName包含有效值。
FALSE 失败。应忽略size值。

尚未调用CWndResizer::Hook方法。

-或-

panelNamepanelIDparentNameparentName无效。

备注

这不应被视为Win32 SetParent API。

parentName设置为_root以表示该面板是钩接窗口的直接子级。

方法 void SetShowResizeGrip(BOOL show = TRUE);
描述 设置一个标志,指示是否在钩接窗口的右下角绘制调整大小的抓手。
参数 show 如果应绘制调整大小的抓手,则为TRUE;否则为FALSE
返回值 N/A 无返回值。
备注 此方法从不失败。
方法 BOOL SetShowSplitterGrip(LPCTSTR splitContainerName, BOOL bShow);
描述 设置一个标志,指示是否显示分隔容器分隔器的抓手。
参数 splitContainerName 通过先前调用CWndResizer::CreateSplitContainer创建的分隔容器的名称。
bShow 如果应绘制调整大小的抓手,则为TRUE;否则隐藏。
返回值 TRUE 成功。
FALSE 失败。

尚未调用CWndResizer::Hook方法。

-或-

splitContainerName无效。

备注 此方法从不失败。
方法 BOOL Unhook();
参数 N/A 无参数。
返回值 TRUE 成功。
FALSE

失败。

尚未调用CWndResizer::Hook方法。

备注

您不必调用此方法。

当钩接窗口收到WM_DESTROY消息时,此方法会被原子地调用。

关注点

此解决方案不需要您自己计算任何数字。此解决方案将所有功能封装在CWndResizer中。

可以对其进行重新设计,使其暴露其他对象(CPanelCSplitContainer等)。我这样设计的原因是我不希望用户了解太多的对象,并简化内存的分配和释放。

另一件事是它依赖于MFC类(CWnd等);同样,您可以删除所有对MFC类的引用,并使用纯Win32 API。

历史

  • 2011年1月13日:文章已更新,包含新功能DockingFlowLayoutPanel
  • 2011年2月25日:根据成员 2272507 的评论,更改了CWndResizer::UnHook方法
  • 2011年3月16日:更新了演示和源代码
  • 2011年5月23日:更新了演示和源代码
  • 2011年8月1日:更新了演示和源代码
  • 2011年8月17日:更新了演示和源代码 - 现在支持CFormView上的滚动
  • 2011年12月7日:更新了演示和源代码
  • 2013年1月25日:添加了新示例(示例11)并修复了一个错误
  • 2013年6月28日:更新了源代码
  • 2013年11月9日:在用户报告后,修复了示例9中的错误
  • 2014年10月20日:添加了三个新方法:Draw(CPaintDC * pDC)GetAutoHandlePaint()SetAutoHandlePaint(BOOL bHandle)。这些在您自定义绘制时很有帮助。有关详细信息,请参阅上面的文档。
  • 2014年10月20日:修复了报告的错误
© . All rights reserved.