TN014:自定义控件

本说明描述了 MFC 对自定义控件和自绘控件的支持。 它还介绍了动态子类分析,并描述了 CWnd 对象与 HWNDs 之间的关系。

MFC 示例应用程序 CTRLTEST 演示如何使用许多自定义控件。 请参阅 MFC 常规示例 CTRLTEST 和联机帮助的源代码。

Owner-Draw 控件/菜单

Windows 使用 Windows 消息为所有者绘制控件和菜单提供支持。 任何控件或菜单的父窗口在响应中接收这些消息和调用函数。 可以重写这些函数,以自定义控件或菜单的外观和行为,使其更符合您设想的样式。

MFC 直接支持自定义绘制功能:

您可以在您的CWnd派生类中重写这些函数,以实现自定义绘图行为。

此方法不会导致可重用的代码。 如果在两个不同的 CWnd 类中有两个类似的控件,则必须在两个位置实现自定义控件行为。 MFC 支持的自绘制控件体系结构解决了此问题。

Self-Draw 控件和菜单

MFC 为标准所有者绘制消息提供默认实现(在 CWndCMenu 类中)。 此默认实现将解码所有者绘制参数,并将所有者绘制消息委托给控件或菜单。 这称为自绘制,因为绘图代码位于控件或菜单的类中,而不是在所有者窗口中。

通过使用自定义绘制控件,您可以创建使用所有者绘制方式来显示控件的可重用控件类。 绘制控件的代码位于控件类中,而不是其父级。 这是一种面向对象的自定义控件编程方法。 将以下函数列表添加到自绘制类:

  • 对于自画按钮:

    CButton::DrawItem(LPDRAWITEMSTRUCT);
    // insert code to draw this button
    
  • 对于自画菜单:

    CMenu::MeasureItem(LPMEASUREITEMSTRUCT);
    // insert code to measure the size of an item in this menu
    CMenu::DrawItem(LPDRAWITEMSTRUCT);
    // insert code to draw an item in this menu
    
  • 对于自绘制列表框:

    CListBox::MeasureItem(LPMEASUREITEMSTRUCT);
    // insert code to measure the size of an item in this list box
    CListBox::DrawItem(LPDRAWITEMSTRUCT);
    // insert code to draw an item in this list box
    
    CListBox::CompareItem(LPCOMPAREITEMSTRUCT);
    // insert code to compare two items in this list box if LBS_SORT
    CListBox::DeleteItem(LPDELETEITEMSTRUCT);
    // insert code to delete an item from this list box
    
  • 对于自画组合框:

    CComboBox::MeasureItem(LPMEASUREITEMSTRUCT);
    // insert code to measure the size of an item in this combo box
    CComboBox::DrawItem(LPDRAWITEMSTRUCT);
    // insert code to draw an item in this combo box
    
    CComboBox::CompareItem(LPCOMPAREITEMSTRUCT);
    // insert code to compare two items in this combo box if CBS_SORT
    CComboBox::DeleteItem(LPDELETEITEMSTRUCT);
    // insert code to delete an item from this combo box
    

有关所有者绘制结构(DRAWITEMSTRUCTMEASUREITEMSTRUCTCOMPAREITEMSTRUCTDELETEITEMSTRUCT)的详细信息,请分别参阅与 CWnd::OnDrawItemCWnd::OnMeasureItemCWnd::OnCompareItemCWnd::OnDeleteItem相关的 MFC 文档。

使用自画控件和菜单

对于自画菜单,必须同时覆盖 OnMeasureItemOnDrawItem 方法。

对于自画列表框和组合框,必须重写 OnMeasureItemOnDrawItem。 必须为列表框指定LBS_OWNERDRAWVARIABLE样式,或为对话框模板中的组合框指定CBS_OWNERDRAWVARIABLE样式。 OWNERDRAWFIXED 样式不适用于自画项目,因为固定项目高度是在自画控件附加到列表框之前确定的。 (可以使用 CListBox::SetItemHeightCComboBox::SetItemHeight 方法来克服此限制。

切换到“OWNERDRAWVARIABLE”样式将强制系统将“NOINTEGRALHEIGHT”样式应用于该控件。 由于控件无法计算具有可变大小项的整型高度,因此忽略 INTEGRALHEIGHT 的默认样式,并且该控件始终为 NOINTEGRALHEIGHT。 如果项目高度固定,可以通过将控件大小指定为项目大小的整数倍来防止显示不完整的项目。

对于具有 LBS_SORT 或 CBS_SORT 样式的自绘列表框和组合框,必须重写 OnCompareItem 方法。

对于自绘列表框和组合框,通常不会覆盖OnDeleteItem。 如果要执行任何特殊处理操作,可以重写 OnDeleteItem 。 一种适用情况是,当其他内存或其他资源随每个列表框或组合框项一起存储时。

Self-Drawing 控件和菜单的示例

MFC 常规示例 CTRLTEST 提供了自画菜单和自画列表框的示例。

自绘制按钮的最典型示例是位图按钮。 位图按钮是显示不同状态的一个、两个或三个位图图像的按钮。 MFC 类 CBitmapButton 中提供了一个示例。

动态子类

有时,需要更改已存在的对象的功能。 前面的示例要求你在创建控件之前自定义控件。 动态子类化使你能够自定义已创建的控件。

子类化是 Windows 中用于将窗口的 WndProc 替换为自定义 WndProc 并调用旧的 WndProc 来实现默认功能的术语。

这不应与C++类的继承混淆。 为了说明,C++术语 基类派生类 类似于 Windows 对象模型中的 超级类子类 。 C++使用 MFC 和 Windows 子类的派生功能相似,但C++不支持动态子类。

CWnd 类提供C++对象(派生自 CWnd)和 Windows 窗口对象(称为 an HWND)之间的连接。

有三种常见的方法与它们相关:

  • CWnd创建HWND. 可以通过创建派生自 CWnd的类来修改派生类中的行为。 当您的应用程序调用 HWND 时,会创建

  • 应用程序将 a CWnd 附加到现有 HWND。 不会修改现有窗口的行为。 这是委派的一种情况,可以通过调用 CWnd::Attach 将现有的HWND对象设为CWnd对象的别名来实现。

  • CWnd 附加到现有 HWND 类,可以修改派生类中的行为。 这称为动态子类化,因为我们在运行时更改 Windows 对象的行为和类。

可以使用 CWnd::SubclassWindowCWnd::SubclassDlgItem 方法实现动态子类化。

这两个例程将对象 CWnd 附加到现有 HWND对象。 SubclassWindow 直接采用 HWNDSubclassDlgItem 是接受控件 ID 和父窗口的辅助函数。 SubclassDlgItem 旨在将C++对象附加到从对话模板创建的对话框控件。

有关何时使用SubclassWindow使用的几个示例,请参阅 SubclassDlgItem 示例。

另请参阅

按编号列出的技术说明
按类别列出的技术说明