钢琴曲欢乐颂改编:WM_MEASUREITEM
来源:百度文库 编辑:中财网 时间:2024/05/07 02:06:25
CWnd::OnMeasureItem
afx_msg void OnMeasureItem( int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct );
参数:
说明:
当控件被创建的时候,框架为自画按钮、组合框、列表框或菜单项调用这个成员函数。
重载这个函数并填充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的结构定义如下:
- {
UINT CtlType; UINT CtlID; UINT itemID; UINT itemWidth; UINT itemHeight; DWORD itemData - }
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()实现如下:
- void
CUi7Dlg::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) - {
if(nIDCtl == IDC_COLOR_PICKER) { //设定高度为30 lpMeasureItemStruct->itemHeight = 30; return; } CDialog::OnMeasureItem(nIDCtl, lpMeasureItemStruct); - }
同样别忘了指定列表框的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结构如下:
- {
HWND hwndFrom; UINT idFrom; UINT code; - }
NMHDR;
其中:
hwndFrom 发送方控件的窗口句柄
idFrom 发送方控件的ID
code 通知代码
对于某些控件来说,pNMHDR则会解释成其它内容更丰富的结构对象的指针,如:对于列表控件来说,pNMHDR常常指向一个NMCUSTOMDRAW对象,NMCUSTOMDRAW结构如下:
- {
NMHDR hdr; DWORD dwDrawStage; HDC hdc; RECT rc; DWORD dwItemSpec; UINT uItemState; LPARAM lItemlParam; - }
NMCUSTOMDRAW, FAR * LPNMCUSTOMDRAW;
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消息美化界面
对应代码如下:
- {
//类型安全转换 NMLVCUSTOMDRAW* pLVCD = reinterpret_cast (pNMHDR); *pResult = 0; //指定列表项绘制前后发送消息 if(CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage) { *pResult = CDRF_NOTIFYITEMDRAW; } else if(CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage) { //奇数行 if(pLVCD->nmcd.dwItemSpec % 2) pLVCD->clrTextBk = RGB(255, 255, 128); //偶数行 else pLVCD->clrTextBk = RGB(128, 255, 255); //继续 *pResult = CDRF_DODEFAULT; } - }
注意到上例采取了3.1所推荐的第2种实现方法,派生了一个新类CCoolList。
3.4 使用MFC类的虚函数机制
修改Windows界面,除了从Windows消息机制下功夫,也可以从MFC类下功夫,这应该得益于类的虚函数机制。为了防止诸如“面向对象技术”等术语在此泛滥,以下仅举一段代码作为例子:
- {
// standard paint routine CPaintDC dc(this); OnPrepareDC(&dc); OnDraw(&dc); - }
这是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画出来的。限于篇幅,在此不再附以例程。