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

可自定义的窗口布局

starIconstarIconstarIconstarIconstarIcon

5.00/5 (15投票s)

2008 年 7 月 28 日

CPOL

6分钟阅读

viewsIcon

41845

downloadIcon

1854

一个类,用于方便用户定义的对话框和窗口控件布局。

positionwnd.example.jpg

描述

有时,为用户提供重新组织对话框或窗口布局的能力可能会很有用。此提交提供了一组 MFC 类,当编译到应用程序中时,可以使应用程序的用户能够重新组织其对话框和窗口中控件的布局。

设计

我们希望在运行时为用户提供重新组织对话框/窗口布局的能力。我们将通过使用窗口子类化机制来实现这一点。窗口子类化本质上允许我们在将窗口的所有消息分派给原始窗口消息处理程序之前捕获并处理它们。我们将使用窗口子类化来捕获和处理窗口的鼠标和绘制消息。截获的鼠标消息将用于将控件/窗口切换进出布局编辑模式,并提供一种实际编辑控件布局的方法。在布局编辑模式下,控件/窗口将以橙色高亮显示,并具有六个橙色控件点,以允许用户配置控件的布局。一个控件点将用于移动控件。另一个控件点将用于调整控件大小。其余四个控件点将用于指定当父窗口调整大小时,控件应如何重新定位和调整大小。

我们希望将新的布局编辑机制集成到现有代码中非常容易。包含新机制的最简单方法是将一个对象添加到对话框或窗口中,并通过调用该对象上的一个方法(最好是在 OnInitDialogOnInitializeView 处理程序中)让该对象“自动”包含该对话框或窗口中的所有控件。一旦调用了子类化控件,其余的就取决于用户了。

如果我们无法持久化用户的布局,那么允许用户自定义对话框或窗口布局有什么用呢?因此,我们将添加方法将布局保存和加载到 XML 格式的字符串中。开发人员需要决定如何以及在何处存储 XML 格式的布局。

CPositionWnd

CPositionWnd 是用于子类化和操作对话框或窗口控件布局的主要类。CPositionWnd 类仅公开了几个方法。理想情况下,您可以在对话框或窗口中包含一个 CPositionWnd 对象,并通过一次调用 SubclassWindowEx 让它递归地子类化对话框或窗口中的所有控件/窗口。通过调用 LoadConfigFromStringSaveConfigToString 可以实现布局持久化。感兴趣的方法包括:

  • void SubclassWindowEx(CWnd* pWindow = NULL, BOOL bIncludeChildren = TRUE) - 此方法允许开发人员为可自定义布局设置窗口。
  • void LoadConfigFromString(LPCSTR lpszConfig) - 此方法允许开发人员加载子类化窗口的布局。
  • CString SaveConfigToString(void) - 此方法允许开发人员保存子类化窗口的布局。

CPositionWndContainer

CPositionWndContainer 提供了一种更灵活的方式来仅配置特定的控件/窗口进行布局编辑。您可以创建 CPositionWndContainer 对象并将特定的控件/窗口附加到该对象。然后,它将为开发人员处理持久化和子类化布局。感兴趣的方法包括:

  • BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL) - 此方法创建一个充当容器窗口的窗口。开发人员可以然后将可自定义的控件/窗口附加到该对象。当此窗口调整大小时,它将更新所有附加的窗口。
  • void LoadConfigFromString(LPCSTR lpszConfig) - 此方法允许开发人员加载子类化窗口的布局。
  • CString SaveConfigToString(void) - 此方法允许开发人员保存子类化窗口的布局。
  • void AttachWindow(CWnd* pWnd, BOOL bIncludeChildren = TRUE) - 此方法允许开发人员将各种控件/窗口分配给容器。

CPositionWndXMLConfigParser

CPositionWndXMLConfigParser 是一个辅助类,用于帮助 CPositionWndCPositionWndContainer 解析 XML 格式的布局。开发人员实际上不需要担心这个类,因为它由 CPositionWndCPositionWndContainer 在内部使用。

注意:开发人员需要在其项目中包含 xmlfile.cpp/.h 才能使用 CPositionWndXMLConfigParser 类。

开发人员使用示例

使用 CPositionWnd 类非常简单。在对话框或窗口类中添加一个 CPositionWnd 对象,并在 OnInitDialogOnInitializeView 方法中让该对象子类化您的对话框或窗口。

BOOL CMyTestDialog::OnInitDialog()
{
    CDialog::OnInitDialog();

    // Attach the position window code to the dialog...
    m_posWnd.SubclassWindowEx(this);

    // Load the persisted lyaout...
    m_posWnd.LoadConfigFromString(m_strLayoutConfiguration);

    // Restore the dialog's layout size...
    int nWidth = m_posWnd.GetLayoutWidth();
    int nHeight = m_posWnd.GetLayoutHeight();
    SetWindowPos(NULL, 0, 0, nWidth, nHeight, SWP_NOMOVE | SWP_NOZORDER);

    return TRUE;
}

用户使用示例

用户可以通过按住 Ctrl 键并单击鼠标右键来编辑任何子类化控件。进入布局编辑模式后,控件将以橙色高亮显示,并带有六个橙色控件点。

positionwnd.controlineditmode.jpg

控件左上角的控件点是移动控件点。如果用户将鼠标悬停在移动控件点上,光标将变为移动光标。用户可以单击并按住控件点,然后将其移动到另一个位置。

positionwnd.movingcontrol.jpg

控件右下角的控件点是调整控件大小的控件点。如果用户将鼠标悬停在尺寸调整控件点上,光标将变为尺寸调整光标。用户可以单击并按住控件点来更改控件的大小。

positionwnd.sizecontrol.jpg

移动控件点右侧和下方的控件点决定了当父窗口调整大小时控件的行为。当选中时,它们本质上会将控件的顶部和/或左侧“锚定/停靠”到用户相对于父窗口顶部和左侧放置控件的位置。

尺寸调整控件点左侧和上方的控件点决定了当父窗口调整大小时控件的行为。当选中时,它们本质上会将控件的底部和/或右侧“锚定/停靠”到调整尺寸后的控件相对于父窗口底部和右侧的位置。

通过使用锚定/停靠控件点的不同组合,用户可以在一定程度上控制控件在父窗口大小变化时如何重新定位和调整大小。

当控件处于布局模式时,用户可以通过右键单击控件来访问一个可以帮助用户进行布局的菜单。例如,某些控件有会遮挡父控件的子控件。一旦切换了子控件以进行布局编辑,右键单击该控件并使用“选择父级”菜单选项可以将父控件置于布局编辑模式。

注意事项

有时,处于布局模式的控件在移动时可能会留下“痕迹”。重绘父窗口将清除这些痕迹,但这仍是一个需要解决的问题。

有可能将控件移动到父窗口可见范围之外,从而失去对移动控件点的访问。这是另一个需要解决的问题。

某些控件可能有边框、框架、滚动条等,这些会影响控件返回的尺寸,并且可能使定位这些控件变得具有挑战性。

根据用户的不同,掌握设置布局所需的时间可能会很长。

© . All rights reserved.