Chinaunix首页 | 论坛 | 博客
  • 博客访问: 14523641
  • 博文数量: 5645
  • 博客积分: 9880
  • 博客等级: 中将
  • 技术积分: 68081
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-28 13:35
文章分类

全部博文(5645)

文章存档

2008年(5645)

我的朋友

分类:

2008-04-28 21:28:19

下载本文示例代码
  在上一篇文章我们介绍了一些与对话框和控件有关的WTL的特性,它们和MFC的相应的类作用相同。本文将介绍一些新类实现高级界面特性新类:控件自画和自定外观控件,新的WTL控件,UI updating和对话框数据验证(DDV)。   特别的自画和外观定制类   由于自画和定制外观控件在图形用户界面中是很常用的手段,所以WTL提供了几个嵌入类来完成这些令人厌烦的工作。我接着就会介绍它们,事实上我们在上一个例子工程ControlMania2的结尾部分已经这么做了。如果你正随着我的讲解用应用程序生成向导创建新工程,请不要忘了使用无模式对话框,为了使正常工作必须使用无模式对话框,我会在对话框中控件的UI Updating部分详细解释为什么这样作。  COwnerDraw   控件的自画需要响应四个消息:WM_MEASUREITEM, WM_DRAWITEM, WM_COMPAREITEM, 和WM_DELETEITEM,在atlframe.h头文件中定义的COwnerDraw类可以简化这些工作,使用这个类就不需要处理这四个消息,你只需将消息链入COwnerDraw,它会调用你的类中的重载函数。  如何将消息链入COwnerDraw取决与你是否将消息反射给控件,两种方法有些不同。下面是COwnerDraw类的消息映射链,它使得两种方法的差别更加明显: template <class T> class COwnerDraw{ public:  BEGIN_MSG_MAP(COwnerDraw<T>)   MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem)   MESSAGE_HANDLER(WM_MEASUREITEM, OnMeasureItem)   MESSAGE_HANDLER(WM_COMPAREITEM, OnCompareItem)   MESSAGE_HANDLER(WM_DELETEITEM, OnDeleteItem)   ALT_MSG_MAP(1)   MESSAGE_HANDLER(OCM_DRAWITEM, OnDrawItem)   MESSAGE_HANDLER(OCM_MEASUREITEM, OnMeasureItem)   MESSAGE_HANDLER(OCM_COMPAREITEM, OnCompareItem)   MESSAGE_HANDLER(OCM_DELETEITEM, OnDeleteItem)  END_MSG_MAP()};  注意,消息映射链的主要部分处理WM_*消息,而ATL部分处理反射的消息,OCM_*。自画的通知消息就像WM_NOTIFY消息一样,你可以在父窗口处理它们,也可以将它们反射会控件,如果你使用前一种方法,消息被直接链入COwnerDraw: class CSomeDlg : public COwnerDraw<CSomeDlg>, ...{ BEGIN_MSG_MAP(CSomeDlg)  //...  CHAIN_MSG_MAP(COwnerDraw<CSomeDlg>) END_MSG_MAP() void DrawItem ( LPDRAWITEMSTRUCT lpdis );};  当然,如果你想要控件自己处理这些消息,你需要使用CHAIN_MSG_MAP_ALT宏将消息链入ALT_MSG_MAP(1)部分: class CSomeButtonImpl : public COwnerDraw<CSomeButtonImpl>, ...{ BEGIN_MSG_MAP(CSomeButtonImpl)  //...  CHAIN_MSG_MAP_ALT(COwnerDraw<CSomeButtonImpl>, 1)  DEFAULT_REFLECTION_HANDLER() END_MSG_MAP() void DrawItem ( LPDRAWITEMSTRUCT lpdis );};  COwnerDraw类将对消息传递的参数展开,然后调用你的类中的实现函数。上面的例子中,我们自己的类实现DrawItem()函数,当有WM_DRAWITEM或OCM_DRAWITEM消息被链入COwnerDraw时,这个函数就会被调用。你可以重载的方法有: void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);int CompareItem(LPCOMPAREITEMSTRUCT lpCompareItemStruct);void DeleteItem(LPDELETEITEMSTRUCT lpDeleteItemStruct);  如果你不想处理某个消息,你可以调用SetMsgHandled(false),消息会被传递给消息映射链中的其他响应者。SetMsgHandled()事实上是COwnerDraw类的成员函数,但是它的作用和在BEGIN_MSG_MAP_EX()中使用SetMsgHandled()一样。  对于ControlMania2,它从ControlMania1中的树控件开始,添加了自画按钮处理反射的WM_DRAWITEM消息,下面是资源编辑器中的新按钮:   现在我们需要一个新类实现自画按钮: class CODButtonImpl : public CWindowImpl<CODButtonImpl, CButton>,public COwnerDraw<CODButtonImpl>{ public:  BEGIN_MSG_MAP_EX(CODButtonImpl)   CHAIN_MSG_MAP_ALT(COwnerDraw<CODButtonImpl>, 1)   DEFAULT_REFLECTION_HANDLER()  END_MSG_MAP()  void DrawItem ( LPDRAWITEMSTRUCT lpdis );};  DrawItem()使用了像BitBlt()这样的GDI函数向按钮的表面画位图,代码应该很容易理解,因为WTL使用的类名和函数名都和MFC类似。 void CODButtonImpl::DrawItem ( LPDRAWITEMSTRUCT lpdis ){ // NOTE: m_bmp is a CBitmap init''ed in the constructor. CDCHandle dc = lpdis->hDC; CDC dcMem; dcMem.CreateCompatibleDC ( dc ); dc.SaveDC(); dcMem.SaveDC(); // Draw the button''s background, red if it has the focus, blue if not. if ( lpdis->itemState & ODS_FOCUS )   dc.FillSolidRect ( &lpdis->rcItem, RGB(255,0,0) ); else  dc.FillSolidRect ( &lpdis->rcItem, RGB(0,0,255) ); // Draw the bitmap in the top-left, or offset by 1 pixel if the button // is clicked. dcMem.SelectBitmap ( m_bmp ); if ( lpdis->itemState & ODS_SELECTED )   dc.BitBlt ( 1, 1, 80, 80, dcMem, 0, 0, SRCCOPY ); else  dc.BitBlt ( 0, 0, 80, 80, dcMem, 0, 0, SRCCOPY ); dcMem.RestoreDC(-1); dc.RestoreDC(-1);}  我们的按钮看起来是这个样子:   CCustomDraw   CCustomDraw类使用和COwnerDraw类相同的方法处理NM_CUSTOMDRAW消息,对于自定绘制的每个阶段都有相应的重载函数: DWORD OnPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD);DWORD OnPostPaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD);DWORD OnPreErase(int idCtrl, LPNMCUSTOMDRAW lpNMCD);DWORD OnPostErase(int idCtrl, LPNMCUSTOMDRAW lpNMCD);DWORD OnItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD);DWORD OnItemPostPaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD);DWORD OnItemPreErase(int idCtrl, LPNMCUSTOMDRAW lpNMCD);DWORD OnItemPostEraset(int idCtrl, LPNMCUSTOMDRAW lpNMCD);DWORD OnSubItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD);  这些函数默认都是返回CDRF_DODEFAULT,如果想自画控件或返回一个不同的值,就需要重载这些函数:  你可能注意到上面的屏幕截图将“道恩”(Dawn:女名)显示成绿色,这是因为CBuffyTreeCtrl将消息链入CCustomDraw并重载了OnPrePaint()和OnItemPrePaint()方法。向树控件中添加节点时,节点的item data字段被设置成1,OnItemPrePaint()检查这个值,然后改变文字的颜色。 DWORD CBuffyTreeCtrl::OnPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD){ return CDRF_NOTIFYITEMDRAW;}DWORD CBuffyTreeCtrl::OnItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD){ if ( 1 == lpNMCD->lItemlParam )  pnmtv->clrText = RGB(0,128,0); return CDRF_DODEFAULT;}  CCustomDraw类也有SetMsgHandled()函数,你可以像在COwnerDraw类那样使用这个函数。共5页。 1 2 3 4 5 :   在上一篇文章我们介绍了一些与对话框和控件有关的WTL的特性,它们和MFC的相应的类作用相同。本文将介绍一些新类实现高级界面特性新类:控件自画和自定外观控件,新的WTL控件,UI updating和对话框数据验证(DDV)。   特别的自画和外观定制类   由于自画和定制外观控件在图形用户界面中是很常用的手段,所以WTL提供了几个嵌入类来完成这些令人厌烦的工作。我接着就会介绍它们,事实上我们在上一个例子工程ControlMania2的结尾部分已经这么做了。如果你正随着我的讲解用应用程序生成向导创建新工程,请不要忘了使用无模式对话框,为了使正常工作必须使用无模式对话框,我会在对话框中控件的UI Updating部分详细解释为什么这样作。  COwnerDraw   控件的自画需要响应四个消息:WM_MEASUREITEM, WM_DRAWITEM, WM_COMPAREITEM, 和WM_DELETEITEM,在atlframe.h头文件中定义的COwnerDraw类可以简化这些工作,使用这个类就不需要处理这四个消息,你只需将消息链入COwnerDraw,它会调用你的类中的重载函数。  如何将消息链入COwnerDraw取决与你是否将消息反射给控件,两种方法有些不同。下面是COwnerDraw类的消息映射链,它使得两种方法的差别更加明显: template <class T> class COwnerDraw{ public:  BEGIN_MSG_MAP(COwnerDraw<T>)   MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem)   MESSAGE_HANDLER(WM_MEASUREITEM, OnMeasureItem)   MESSAGE_HANDLER(WM_COMPAREITEM, OnCompareItem)   MESSAGE_HANDLER(WM_DELETEITEM, OnDeleteItem)   ALT_MSG_MAP(1)   MESSAGE_HANDLER(OCM_DRAWITEM, OnDrawItem)   MESSAGE_HANDLER(OCM_MEASUREITEM, OnMeasureItem)   MESSAGE_HANDLER(OCM_COMPAREITEM, OnCompareItem)   MESSAGE_HANDLER(OCM_DELETEITEM, OnDeleteItem)  END_MSG_MAP()};  注意,消息映射链的主要部分处理WM_*消息,而ATL部分处理反射的消息,OCM_*。自画的通知消息就像WM_NOTIFY消息一样,你可以在父窗口处理它们,也可以将它们反射会控件,如果你使用前一种方法,消息被直接链入COwnerDraw: class CSomeDlg : public COwnerDraw<CSomeDlg>, ...{ BEGIN_MSG_MAP(CSomeDlg)  //...  CHAIN_MSG_MAP(COwnerDraw<CSomeDlg>) END_MSG_MAP() void DrawItem ( LPDRAWITEMSTRUCT lpdis );};  当然,如果你想要控件自己处理这些消息,你需要使用CHAIN_MSG_MAP_ALT宏将消息链入ALT_MSG_MAP(1)部分: class CSomeButtonImpl : public COwnerDraw<CSomeButtonImpl>, ...{ BEGIN_MSG_MAP(CSomeButtonImpl)  //...  CHAIN_MSG_MAP_ALT(COwnerDraw<CSomeButtonImpl>, 1)  DEFAULT_REFLECTION_HANDLER() END_MSG_MAP() void DrawItem ( LPDRAWITEMSTRUCT lpdis );};  COwnerDraw类将对消息传递的参数展开,然后调用你的类中的实现函数。上面的例子中,我们自己的类实现DrawItem()函数,当有WM_DRAWITEM或OCM_DRAWITEM消息被链入COwnerDraw时,这个函数就会被调用。你可以重载的方法有: void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);int CompareItem(LPCOMPAREITEMSTRUCT lpCompareItemStruct);void DeleteItem(LPDELETEITEMSTRUCT lpDeleteItemStruct);  如果你不想处理某个消息,你可以调用SetMsgHandled(false),消息会被传递给消息映射链中的其他响应者。SetMsgHandled()事实上是COwnerDraw类的成员函数,但是它的作用和在BEGIN_MSG_MAP_EX()中使用SetMsgHandled()一样。  对于ControlMania2,它从ControlMania1中的树控件开始,添加了自画按钮处理反射的WM_DRAWITEM消息,下面是资源编辑器中的新按钮:   现在我们需要一个新类实现自画按钮: class CODButtonImpl : public CWindowImpl<CODButtonImpl, CButton>,public COwnerDraw<CODButtonImpl>{ public:  BEGIN_MSG_MAP_EX(CODButtonImpl)   CHAIN_MSG_MAP_ALT(COwnerDraw<CODButtonImpl>, 1)   DEFAULT_REFLECTION_HANDLER()  END_MSG_MAP()  void DrawItem ( LPDRAWITEMSTRUCT lpdis );};  DrawItem()使用了像BitBlt()这样的GDI函数向按钮的表面画位图,代码应该很容易理解,因为WTL使用的类名和函数名都和MFC类似。 void CODButtonImpl::DrawItem ( LPDRAWITEMSTRUCT lpdis ){ // NOTE: m_bmp is a CBitmap init''ed in the constructor. CDCHandle dc = lpdis->hDC; CDC dcMem; dcMem.CreateCompatibleDC ( dc ); dc.SaveDC(); dcMem.SaveDC(); // Draw the button''s background, red if it has the focus, blue if not. if ( lpdis->itemState & ODS_FOCUS )   dc.FillSolidRect ( &lpdis->rcItem, RGB(255,0,0) ); else  dc.FillSolidRect ( &lpdis->rcItem, RGB(0,0,255) ); // Draw the bitmap in the top-left, or offset by 1 pixel if the button // is clicked. dcMem.SelectBitmap ( m_bmp ); if ( lpdis->itemState & ODS_SELECTED )   dc.BitBlt ( 1, 1, 80, 80, dcMem, 0, 0, SRCCOPY ); else  dc.BitBlt ( 0, 0, 80, 80, dcMem, 0, 0, SRCCOPY ); dcMem.RestoreDC(-1); dc.RestoreDC(-1);}  我们的按钮看起来是这个样子:   CCustomDraw   CCustomDraw类使用和COwnerDraw类相同的方法处理NM_CUSTOMDRAW消息,对于自定绘制的每个阶段都有相应的重载函数: DWORD OnPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD);DWORD OnPostPaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD);DWORD OnPreErase(int idCtrl, LPNMCUSTOMDRAW lpNMCD);DWORD OnPostErase(int idCtrl, LPNMCUSTOMDRAW lpNMCD);DWORD OnItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD);DWORD OnItemPostPaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD);DWORD OnItemPreErase(int idCtrl, LPNMCUSTOMDRAW lpNMCD);DWORD OnItemPostEraset(int idCtrl, LPNMCUSTOMDRAW lpNMCD);DWORD OnSubItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD);  这些函数默认都是返回CDRF_DODEFAULT,如果想自画控件或返回一个不同的值,就需要重载这些函数:  你可能注意到上面的屏幕截图将“道恩”(Dawn:女名)显示成绿色,这是因为CBuffyTreeCtrl将消息链入CCustomDraw并重载了OnPrePaint()和OnItemPrePaint()方法。向树控件中添加节点时,节点的item data字段被设置成1,OnItemPrePaint()检查这个值,然后改变文字的颜色。 DWORD CBuffyTreeCtrl::OnPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD){ return CDRF_NOTIFYITEMDRAW;}DWORD CBuffyTreeCtrl::OnItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD){ if ( 1 == lpNMCD->lItemlParam )  pnmtv->clrText = RGB(0,128,0); return CDRF_DODEFAULT;}  CCustomDraw类也有SetMsgHandled()函数,你可以像在COwnerDraw类那样使用这个函数。共5页。 1 2 3 4 5 : 下载本文示例代码


MFC程序员的WTL指南之高级界面类MFC程序员的WTL指南之高级界面类MFC程序员的WTL指南之高级界面类MFC程序员的WTL指南之高级界面类MFC程序员的WTL指南之高级界面类MFC程序员的WTL指南之高级界面类MFC程序员的WTL指南之高级界面类MFC程序员的WTL指南之高级界面类MFC程序员的WTL指南之高级界面类MFC程序员的WTL指南之高级界面类MFC程序员的WTL指南之高级界面类MFC程序员的WTL指南之高级界面类MFC程序员的WTL指南之高级界面类MFC程序员的WTL指南之高级界面类MFC程序员的WTL指南之高级界面类
阅读(255) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~