CWnd::OnMeasureItem
说明: 当控件被创建的时候,框架为自画按钮、组合框、列表框或菜单项调用这个成员函数。 重载这个函数并填充lpMeasureItemStruct指向的MEASUREITEMSTRUCT数据结构,然后返回;这将通知Windows控件的大小,并使Windows能够正确地处理控件的用户交互。 如果列表框或组合框是用LBS_OWNERDRAWVARIABLE或CBS_OWNERDRAWVARIA_BLE风格创建的,则框架为控件中的每一个项调用这个函数;否则这个函数只被调用一次。 在发送WM_INITDIALOG消息之前,Windows为用OWNERDRAWFIXED风格创建的组合框和列表框的拥有者发出对 OnMeasureItem的调用。其结果是,当拥有者接收到这个调用时,Windows还没有确定在控件中使用的字体的高度和宽度;需要这些值的函数调 用和计算应该发生在应用程序或库的主函数中。 如果要测量的的项是CMenu,CListBox或CComboBox对象,则将调用适当的类的虚函数MeasureItem。重载适当的控件类的MeasureItem成员函数以计算并设置每个项的大小。 仅当控件类是在运行时创建,或者它是用LBS_OWNERDRAWVARIABLE或CBS_OWNERDRAWVARIABLE风格创建的时候,OnMeasureItem才会被调用。这是因为WM_MEASUREITEM消息时在控件创建过程的早期被发送的。 如果你使用DDX_Control,SubclassDlgItem或SubclassWindow进行了子类化,则子类化过程通常发生在创建过程之后。 因此,在控件的OnChildNotify函数中无法处理WM_MEASUREITEM消息,这是MFC用来实现 ON_WM_MEASUREITEM_REFLECT的机制。 注意 框架调用这个成员函数以允许你的应用程序处理一个Windows消息。传递给你的成员函数的参数反映了接收到消息时框架接收到的参数。如果你调用了这个函数的基类实现,则该实现将使用最初传递给消息的参数(而不是你提供给这个函数的参数)。 仅仅WM_DRAWITEM还是不够的,对于一些特殊的控件,如ListBox,系统在发送WM_DRAWITEM消息前,还发送WM_MEASUREITEM消息,需要你设置ListBox中每个项目的高度。 WM_DRAWITEM的映射函数原型如下: afx_msg void OnMeasureItem( int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct ); nIDCtl 该控件的ID,如果该元素为菜单,则nIDCtl为0 lpMeasureItemStruct指向MEASUREITEMSTRUCT结构对象的指针,MEASUREITEMSTRUCT的结构定义如下:
CtlType指定了控件的类型,其取值如表6所示: 类型值 ODT_COMBOBOX ODT_LISTBOX ODT_MENU 表6 CtlType的类型值与含义 CtlID 指定自绘控件的ID值,该成员不适用于菜单项 itemID表示菜单项ID,也可以表示可变高度的列表框或组合框中某项的索引值。该成员不适用于固定高度的列表框或组合框。 itemWidth 指定菜单项的宽度 itemHeight指定菜单项或者列表框中某项的的高度,最大值为255 itemData 对于菜单项,该成员的取值为由CMenu::AppendMenu、CMenu::InsertMenu、CMenu::ModifyMenu等函数传递给菜单的值。 对于列表框或这组合框,该成员的取值为由ComboBox::AddString、CComboBox::InsertString、CListBox::AddString或者CListBox::InsertString等函数传递给控件的值。 图示出了OnMeasureItem的效果: 相应的OnMeasureItem()实现如下:
同样别忘了指定列表框的Owner draw属性: 图11 指定下拉框的Owner draw属性 3.3.6 NM_CUSTOMDRAW 大家也许熟悉WM_NOTIFY,控件通过WM_NOTIFY向父窗口发送消息。在WM_NOTIFY消息体中,部分控件会发送NM_CUSTOMDRAW告诉父窗口自己需要绘图。 可以反射NM_CUSTOMDRAW消息,如: ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw) afx_msg void OnCustomDraw(NMHDR *pNMHDR, LRESULT *pResult); 参数: pNMHDR 说到底只是一个指针,大多数情况下它指向一个NMHDR结构对象,NMHDR结构如下:
其中: hwndFrom 发送方控件的窗口句柄 idFrom 发送方控件的ID code 通知代码 对于某些控件来说,pNMHDR则会解释成其它内容更丰富的结构对象的指针,如:对于列表控件来说,pNMHDR常常指向一个NMCUSTOMDRAW对象,NMCUSTOMDRAW结构如下:
hdr NMHDR对象 dwDrawStage 当前绘制状态,其取值如表7所示: 类型值 CDDS_POSTERASE CDDS_POSTPAINT CDDS_PREERASE CDDS_PREPAINT CDDS_ITEM CDDS_ITEMPOSTERASE CDDS_ITEMPOSTPAINT CDDS_ITEMPREERASE CDDS_ITEMPREPAINT CDDS_SUBITEM 表7 dwDrawStage的类型值与含义 hdc指定了绘制操作所使用的设备环境。 rc指定了将被绘制的矩形区域。 dwItemSpec 列表项的索引 uItemState 当前列表项的状态,其取值如表8所示: 类型值 CDIS_CHECKED CDIS_DEFAULT CDIS_DISABLED CDIS_FOCUS CDIS_GRAYED CDIS_SELECTED CDIS_HOTLIGHT CDIS_INDETERMINATE CDIS_MARKED 表8 uItemState的类型值与含义 lItemlParam 当前列表项的绑定数据 pResult 指向状态值的指针,指定系统后续操作,依赖于dwDrawStage: 当dwDrawStage为CDDS_PREPAINT,pResult含义如表9所示: 类型值 CDRF_DODEFAULT CDRF_NOTIFYITEMDRAW CDRF_NOTIFYPOSTERASE CDRF_NOTIFYPOSTPAINT 表9 pResult的类型值与含义(一) 当dwDrawStage为CDDS_ITEMPREPAINT,pResult含义如表10所示: 类型值 CDRF_NEWFONT CDRF_NOTIFYSUBITEMDRAW CDRF_SKIPDEFAULT 表10 pResult的类型值与含义(二) 以下是一个利用NM_CUSTOMDRAW消息绘制出的多色列表框的例子: 图12 利用NM_CUSTOMDRAW消息美化界面 对应代码如下:
注意到上例采取了3.1所推荐的第2种实现方法,派生了一个新类CCoolList。 3.4 使用MFC类的虚函数机制 修改Windows界面,除了从Windows消息机制下功夫,也可以从MFC类下功夫,这应该得益于类的虚函数机制。为了防止诸如“面向对象技术”等术语在此泛滥,以下仅举一段代码作为例子:
这是MFC中viewcore.cpp中的源代码,很多读者总不明白OnDraw()和OnPaint()之间的关系,从以上的代码中很容易看 出,CView的WM_PAINT消息响应函数OnPaint()会自动调用CView::OnDraw()。而作为开发者的用户,可以通过简单的 OnDraw()的重载实现对WM_PAINT的处理。所以说,对MFC类的虚函数的重载是对消息机制的扩展。 以下列出了与界面美化相关的虚函数,参数说明略去: CButton::DrawItem CCheckListBox::DrawItem CComboBox::DrawItem CHeaderCtrl::DrawItem CListBox::DrawItem CMenu::DrawItem CStatusBar::DrawItem CStatusBarCtrl::DrawItem CTabCtrl::DrawItem virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct ); Owner draw元素自绘函数 很显然,位图菜单都是通过这个DrawItem画出来的。限于篇幅,在此不再附以例程。 |
|