(转)WTL入门(5)--- 高级的对话框UI类

源代码:http://download.csdn.net/source/3522809 

上一篇文章中,讲述了一些WTL的关于对话框和控件的特性,本章中将讲述的新的WTL类实现了一些高级UI特性:所有者绘制、自定义绘制、新的WTL控件、UI更新和DDV(对话框数据有效性)。

Specialized Owner Draw and Custom Draw Classes

因为所有绘制和自定义绘制控件在GUI项目中非常常见,WTL提供了一些混合类来处理这些工作。首先通过AppWizard创建一个非模式对话框的WTL项目。这是为了使UI更新功能能正确的工作。

COwnerDraw

所有者绘制涉及四个消息:WM_MEASUREITEMWM_DRAWITEMWM_COMPAREITEM, 和WM_DELETEITEM。COwnerDraw 类,在atlframe.h中定义,为我们简化了代码,因为在此类中我们不需要为这些消息路由,而是链接消息到COwnerDraw 并且在自己的实现类中重载消息处理函数。
如何链接消息依赖于是否反射消息到控件里。下面是COwnerDraw的消息路由:

[cpp]  view plain copy
  1. template <class T> class COwnerDraw  
  2. {  
  3. public:  
  4.   BEGIN_MSG_MAP(COwnerDraw<T>)  
  5.     MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem)  
  6.     MESSAGE_HANDLER(WM_MEASUREITEM, OnMeasureItem)  
  7.     MESSAGE_HANDLER(WM_COMPAREITEM, OnCompareItem)  
  8.     MESSAGE_HANDLER(WM_DELETEITEM, OnDeleteItem)  
  9.   ALT_MSG_MAP(1)  
  10.     MESSAGE_HANDLER(OCM_DRAWITEM, OnDrawItem)  
  11.     MESSAGE_HANDLER(OCM_MEASUREITEM, OnMeasureItem)  
  12.     MESSAGE_HANDLER(OCM_COMPAREITEM, OnCompareItem)  
  13.     MESSAGE_HANDLER(OCM_DELETEITEM, OnDeleteItem)  
  14.   END_MSG_MAP()  
  15. };  

可以看出,消息路由的主片段处理消息WM_*;然而ALT_MSG_MAP(1)里的消息路由处理消息的反射版本OCM_*。所有者绘制控件的消息通知,像WM_NOTIFY,可以在它们的副控件中处理,也可以反射到控件本身,如果选择前者,消息链接直接到COwnerDraw

[cpp]  view plain copy
  1. // C++ class for a dialog that contains owner-drawn controls  
  2. class CSomeDlg : public CDialogImpl<CSomeDlg>,  
  3.                  public COwnerDraw<CSomeDlg>, ...  
  4. {  
  5.   BEGIN_MSG_MAP(CSomeDlg)  
  6.     //...  
  7.    <strong> CHAIN_MSG_MAP(COwnerDraw<CSomeDlg>)</strong>  
  8.   END_MSG_MAP()  
  9.    
  10.   void DrawItem ( LPDRAWITEMSTRUCT lpdis );  
  11. };  

然而,如果你想要使控件处理消息,就需要使用CHAIN_MSG_MAP_ALT 宏链接消息到ALT_MSG_MAP(1)段:

[cpp]  view plain copy
  1. // C++ class that implements an owner-drawn button  
  2. class CMyButton : public CWindowImpl<CMyButton, CButton>,  
  3.                   public COwnerDraw<CMyButton>, ...  
  4. {  
  5.   BEGIN_MSG_MAP(CMyButton)  
  6.     //...  
  7.    <strong> CHAIN_MSG_MAP_ALT(COwnerDraw<CMyButton>, 1)  
  8.     DEFAULT_REFLECTION_HANDLER()</strong>  
  9.   END_MSG_MAP()  
  10.    
  11.   void DrawItem ( LPDRAWITEMSTRUCT lpdis );  
  12. };  

COwnerDraw 解包消息参数,调用实现函数。我们可重载的消息处理方法有:

[cpp]  view plain copy
  1. void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);  
  2. void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);  
  3. int  CompareItem(LPCOMPAREITEMSTRUCT lpCompareItemStruct);  
  4. void DeleteItem(LPDELETEITEMSTRUCT lpDeleteItemStruct);  

如果因某原因不想在重载中处理消息,可以调用SetMsgHandled(false),然后消息将会传递到之后的消息路由中的其他处理中。
例子中,我们创建一个所有者绘制的button,并且在Button的实现类中处理反射的消息WM_DRAWITEM ,下面是资源编辑器下的界面:

下面是Button的实现类:

[cpp]  view plain copy
  1. class CODButtonImpl : public CWindowImpl<CODButtonImpl, CButton>,  
  2.                       public COwnerDraw<CODButtonImpl>  
  3. {  
  4. public:  
  5.     BEGIN_MSG_MAP_EX(CODButtonImpl)  
  6.        <strong> CHAIN_MSG_MAP_ALT(COwnerDraw<CODButtonImpl>, 1)  
  7.         DEFAULT_REFLECTION_HANDLER()</strong>  
  8.     END_MSG_MAP()  
  9.    
  10.     <strong>void DrawItem ( LPDRAWITEMSTRUCT lpdis );</strong>  
  11. };  

DrawItem()调用GDI命令,为button绘制一张图:

[cpp]  view plain copy
  1. void CODButtonImpl::DrawItem ( LPDRAWITEMSTRUCT lpdis )  
  2. {  
  3. // NOTE: m_bmp is a CBitmap init'ed in the constructor.  
  4. CDCHandle dc = lpdis->hDC;  
  5. CDC dcMem;  
  6.    
  7.     dcMem.CreateCompatibleDC ( dc );  
  8.     dc.SaveDC();  
  9.     dcMem.SaveDC();  
  10.    
  11.     // Draw the button's background, red if it has the focus, blue if not.  
  12.     if ( lpdis->itemState & ODS_FOCUS )   
  13.         dc.FillSolidRect ( &lpdis->rcItem, RGB(255,0,0) );  
  14.     else  
  15.         dc.FillSolidRect ( &lpdis->rcItem, RGB(0,0,255) );  
  16.    
  17.     // Draw the bitmap in the top-left, or offset by 1 pixel if the button  
  18.     // is clicked.  
  19.     dcMem.SelectBitmap ( m_bmp );  
  20.    
  21.     if ( lpdis->itemState & ODS_SELECTED )   
  22.         dc.BitBlt ( 1, 1, 80, 80, dcMem, 0, 0, SRCCOPY );  
  23.     else  
  24.         dc.BitBlt ( 0, 0, 80, 80, dcMem, 0, 0, SRCCOPY );  
  25.    
  26.     dcMem.RestoreDC(-1);  
  27.     dc.RestoreDC(-1);  
  28. }  

下面是button的表现形式:

CCustomDraw

CCustomDraw的工作方式与COwnDraw相似,它处理NM_CUSTOMDRAW消息并链接它们。CCustomDraw在自定义绘制的每一个阶段都有一个可重载的函数:

[cpp]  view plain copy
  1. DWORD OnPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD);  
  2. DWORD OnPostPaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD);  
  3. DWORD OnPreErase(int idCtrl, LPNMCUSTOMDRAW lpNMCD);  
  4. DWORD OnPostErase(int idCtrl, LPNMCUSTOMDRAW lpNMCD);  
  5.    
  6. DWORD OnItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD);  
  7. DWORD OnItemPostPaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD);  
  8. DWORD OnItemPreErase(int idCtrl, LPNMCUSTOMDRAW lpNMCD);  
  9. DWORD OnItemPostEraset(int idCtrl, LPNMCUSTOMDRAW lpNMCD);  
  10.    
  11. DWORD OnSubItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD);  

其中,所有函数的默认处理都是返回CDRF_DODEFAULT,因此如果你需要执行自定义的绘制或者需要不同的返回值,你需要重载对应的方法。
在上张截图中,树形视图中“Drawn”的颜色是绿色的,这是通过使用一个继承于CTreeCtrl,链接消息到CCustomDraw并且重载了OnPrePaint()OnItemPrePaint()的新类CBuffyTreeCtrl。当树被填充时,“Drawn”节点的数据被设为1,OnItemPrePaint()检测这个值并改变文字颜色。

[cpp]  view plain copy
  1. DWORD CBuffyTreeCtrl::OnPrePaint(  
  2.     int idCtrl, LPNMCUSTOMDRAW lpNMCD)  
  3. {  
  4.     return CDRF_NOTIFYITEMDRAW;  
  5. }  
  6.    
  7. DWORD CBuffyTreeCtrl::OnItemPrePaint(  
  8.     int idCtrl, LPNMCUSTOMDRAW lpNMCD)  
  9. {  
  10.     if ( 1 == lpNMCD->lItemlParam )  
  11.         pnmtv->clrText = RGB(0,128,0);  
  12.    
  13.     return CDRF_DODEFAULT;  
  14. }  

就如COwnerDraw,也可在自定义绘制类的消息处理中调用SetMsgHandled(false),把消息传递到其他的消息路由中。

New WTL Controls

WTL拥有一些新的控件,或是其他封装的改进版(如CTreeViewCtrlEx)或是非内置控件的新功能(如CHyperLink)。

CBitmapButton

WTL的CBitmapButton,在atlctrlx.h中定义,比MFC中的更易用。这个WTL类使用一个图像表而不是四个单独的位图资源,这意味着我们可以把多个按钮图片放在一个位图中,从而降低GDI的使用。如果你的程序运行在win9x并且有大量的图形,这种做法是特别好的,因为使用大量的孤立的图形将会迅速耗尽GDI资源并当掉系统。
CBitmapButton 派生于CWindowImpl,包含很多特性:控件自缩放,自动生成3D边框,热跟踪支持,以及根据控件的状态,一个按钮有几张图像。
在本例中,我们使用CBitmapButton放置在上章创建的所有者绘制的Button旁边,首先添加CBitmapButton对象m_wndBmpBtn作为CMainDlg的成员。然后关联控件和成员变量。加载一个位图到ImageList中,告诉按钮使用这个ImageList,同时告诉按钮哪张图片对应控件的哪个状态。下面是OnInitDialog() 中设置按钮的代码片段:

[cpp]  view plain copy
  1. // Set up the bitmap button  
  2. CImageList iml;  
  3.   
  4. iml.CreateFromImage ( IDB_ALYSON_IMGLIST, 81, 1, CLR_NONE,  
  5.                       IMAGE_BITMAP, LR_CREATEDIBSECTION );  
  6.   
  7. m_wndBmpBtn.SubclassWindow ( GetDlgItem(IDC_ALYSON_BMPBTN) );  
  8. m_wndBmpBtn.SetToolTipText ( _T("Alyson") );  
  9. m_wndBmpBtn.SetImageList ( iml );  
  10. m_wndBmpBtn.SetImages ( 0, 1, 2, 3 );  

默认下,这个按钮占用image list的所有权,因此OnInitDialog()不能释放它创建的imagelist。

CBitmapButton是个非常有用的类,下面是它的方法:

CBitmapButton methods

CBitmapButtonImpl 类包含了按钮的所有实现,除非需要重载方法和消息处理函数,否则就可以直接使用CBitmapButton。

[cpp]  view plain copy
  1. // 构造函数,设置Button的扩展风格(不要与窗口风格混淆),并可指定一个imagelist。// 通常默认的情况就足够了,我们可以用其他方法设置这两个属性  
  2. CBitmapButtonImpl(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE,  
  3.                   HIMAGELIST hImageList = NULL)  
  4. // 重载,执行子类化并初始化内部数据   
  5. BOOL SubclassWindow(HWND hWnd)  
  6.   
  7. // 获取和设置位图按钮的扩展风格DWORD GetBitmapButtonExtendedStyle()  
  8. DWORD SetBitmapButtonExtendedStyle(DWORD dwExtendedStyle,  
  9.                                    DWORD dwMask = 0)  
BMPBTN_HOVER :激活热跟踪,当鼠标停在按钮上时,以焦点状态绘制 BMPBTN_AUTO3D_SINGLEBMPBTN_AUTO3D_DOUBLE :在图像边缘自动生成3D边框,以及鼠标得到焦点时的焦点矩形。如果你没有提供按下状态的图像,它会给你生成一个。 BMPBTN_AUTO3D_DOUBLE 提供一个稍厚的边框。 BMPBTN_AUTOSIZE :使按钮自动调整大小以适应图像的大小。
BMPBTN_SHAREIMAGELISTS :如果设置,按钮对象不会销毁imagelist;否则,在CImageButton的析构函数中销毁imagelist。 BMPBTN_AUTOFIRE : 如果设置,单击该按钮和按住该按钮生成重复的 WM_COMMAND消息。
当调用SetBitmapButtonExtendedStyle()
时,参数dwMask用于控制哪些风格生效,使用默认值0,表示用新的风格完全代替旧的。
[cpp]  view plain copy
  1. HIMAGELIST GetImageList()  
  2. HIMAGELIST SetImageList(HIMAGELIST hImageList)  
使用 GetImageList() 和  SetImageList() 关联imagelist和按钮,获取关联到按钮的当前的imagelist。
[cpp]  view plain copy
  1. int  GetToolTipTextLength()  
  2. bool GetToolTipText(LPTSTR lpstrText, int nLength)  
  3. bool SetToolTipText(LPCTSTR lpstrText)  
CBitmapButton支持当鼠标悬停在按钮上时,显示一个ToolTip。调用GetToolTipText() 和 SetToolTipText() 获取和设置tooltip的文本。
[cpp]  view plain copy
  1. void SetImages(int nNormal, int nPushed = -1, int nFocusOrHover = -1, int nDisabled = -1)  
调用SetImages告诉按钮,imagelist中的哪个图像对应哪个状态。参数是imagelist中的0起始的图像索引。nNormal是必须的,-1表示没有与之对应的图片。

CCheckListViewCtrl

CCheckListViewCtrl,在atlctrlx.h中定义,派生于CWindowImpl,实现了带复选框的list view控件。这与MFC中的CCheckListBox不同,CCheckListBox使用的是List Box,不是List View。
CCheckListViewCtrl 相当简单,添加了很少的功能,但是它引入了一个新的帮助器类CCheckListViewCtrlImplTraits,它像CWinTraits 但是第三个模板参数控件的List View风格。如果不定义自己的帮助器类CCheckListViewCtrlImplTraits。将会使用默认的值:LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT.
[cpp]  view plain copy
  1. // 注意:必须包括<code>LVS_EX_CHECKBOXES,否则会有断言失败</code>  
  2. typedef CCheckListViewCtrlImplTraits<  
  3.     WS_CHILD | WS_VISIBLE | LVS_REPORT,   
  4.     WS_EX_CLIENTEDGE,  
  5.    <strong> LVS_EX_CHECKBOXES</strong> | LVS_EX_GRIDLINES | LVS_EX_UNDERLINEHOT |  
  6.       LVS_EX_ONECLICKACTIVATE> CMyCheckListTraits;  
  7.    
  8. class CMyCheckListCtrl :  
  9.     public CCheckListViewCtrlImpl<CMyCheckListCtrl, CListViewCtrl,   
  10.                                   CMyCheckListTraits>  
  11. {  
  12. private:  
  13.     typedef CCheckListViewCtrlImpl<CMyCheckListCtrl, CListViewCtrl,   
  14.                                    CMyCheckListTraits> baseClass;  
  15. public:  
  16.     BEGIN_MSG_MAP(CMyCheckListCtrl)  
  17.         CHAIN_MSG_MAP(baseClass)  
  18.     END_MSG_MAP()  
  19. };  
CCheckListViewCtrl methods
[cpp]  view plain copy
  1. BOOL SubclassWindow(HWND hWnd)  
当你子类化一个已存在的Listview控件, SubclassWindow()查看关联的CCheckListViewCtrlImplTraits中的扩展的Listview的风格并应用到控件中。CCheckListViewCtrlImplTraits 的前两个模板参数(windows styles and extended window styles)不使用。
[cpp]  view plain copy
  1. BOOL GetCheckState(int nIndex)  
  2. BOOL SetCheckState(int nItem, BOOL bCheck)  
获取和设置指定索引的条目的复选框状态。
[cpp]  view plain copy
  1. void CheckSelectedItems(int nCurrItem)  
此方法使用一个条目索引,切换它的状态(该条目必须是已选择的)并且改变其他被选则的条目的复选框状态。你可能不会使用这个方法,当用户点击复选框或按空格键 CCheckListViewCtrl会自动切换状态。

CTreeViewCtrlEx and CTreeItem

这两个类通过封装 CTreeItem 使我们更易于使用tree control。CTreeItem 对象保存一个CTreeItem以及对应的树控件的指针。仅仅通过使用CTreeItem就可以执行针对某个item的操作。
CTreeViewCtrlEx 像CTreeViewCtrl,但是前者处理的是 CTreeItem 对象,而后者处理的是HTREEITEM 对象。
[cpp]  view plain copy
  1. // Using plain HTREEITEMs:  
  2. HTREEITEM hti, hti2;  
  3.   
  4. hti = m_wndTree.InsertItem ( "foo", TVI_ROOT, TVI_LAST );  
  5. hti2 = m_wndTree.InsertItem ( "bar", hti, TVI_LAST );  
  6. m_wndTree.SetItemData ( hti2, 37 );  
  7.   
  8. // Using CTreeItems:  
  9. CTreeItem ti, ti2;  
  10.   
  11. ti = m_wndTreeEx.InsertItem ( "baz", TVI_ROOT, TVI_LAST );  
  12. ti2 = ti.AddTail ( "yen", 0 );  
  13. ti2.SetData ( 42 );  

CHyperLink

CHyperLink,派生于CWindowImpl,子类化静态文本控件并使之成为可以点击的超链接。CHyperLink自动处理link的绘制(根据IE颜色选项)并支持键盘导航。它的基类CHyperLinkImpl 包含实现link的所有代码,除非你需要重载方法和消息处理函数,你可以直接使用CHyperLink。
CHyperLink 的默认行为是点击链接时在默认的IE浏览器中运行URL。如果子类化的静态控件包含 WS_TABSTOP 状态,可以tab到该控件,然后按空格或回车键相当于点击该链接。默认的 CHyperLink 使用静态控件的文本作为URL和toolTip的默认文本。
CHyperLink methods
下面仅介绍常用的方法,对于其他的,比如计算控件大小,解析链接文本等均在atlctrlx.h中
[cpp]  view plain copy
  1. CHyperLinkImpl ( DWORD dwExtendedStyle = HLINK_UNDERLINED )  
  2. CHyperLink()  
CHyperLinkImpl 的构造函数提供一个应用到控件上得扩展风格。但是CHyperLink并没有与之对应的构造函数,不过可使用 SetHyperLinkExtendedStyle() 设置该属性。
[cpp]  view plain copy
  1. BOOL SubclassWindow(HWND hWnd)  
子类化,初始化内部数据。如果使用DDX关联一个CHyperLink对象与一个静态文本控件,此函数会自动被执行,或者也可以手工调用去子类化控件。
[cpp]  view plain copy
  1. DWORD GetHyperLinkExtendedStyle()  
  2. DWORD SetHyperLinkExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)  
获取和设置控件的扩展风格。你必须在SubclassWindow或 Create()之前设置扩展风格,以告诉控件如何绘制文本。
[cpp]  view plain copy
  1. bool GetLabel(LPTSTR lpstrBuffer, int nLength)  
  2. bool SetLabel(LPCTSTR lpstrLabel)  
获取和设置控件文本,如果不设置,将使用静态控件的初始文本。
[cpp]  view plain copy
  1. bool GetHyperLink(LPTSTR lpstrBuffer, int nLength)  
  2. bool SetHyperLink(LPCTSTR lpstrLink)  
获取和设置控件关联的URL文本。如果不设置,将使用静态控件的初始文本
[cpp]  view plain copy
  1. bool GetToolTipText(LPTSTR lpstrBuffer, int nLength)  
  2. bool SetToolTipText(LPCTSTR lpstrToolTipText)  
获取和设置tooltip文本。这两个函数自由在使用了 HLINK_COMMANDBUTTON 或  HLINK_NOTIFYBUTTON 风格的控件中才能使用。


CHyperLink extended styles
HLINK_UNDERLINED
超链接文本带下划线,默认行为。
HLINK_NOTUNDERLINED
超链接文本不带下划线 
HLINK_UNDERLINEHOVER
当鼠标悬停在控件上时,文本显示带下划线 
HLINK_COMMANDBUTTON
当点击超链接时,控件触发  WM_COMMAND 消息 到父窗口( BN_CLICKED) 。
HLINK_NOTIFYBUTTON
当点击超链接是,控件触发 WM_NOTIFY 消息 ( NM_CLICK) 到父窗口
HLINK_USETAGS
控件仅把<a>中的文本被认为是超链接,其他文本不变。
HLINK_USETAGSBOLD
同上,但是 <a> 中的文本为黑体。当此被设置时,链接文本将永不带下划线。
HLINK_NOTOOLTIP
控件不显示tooltip。

如果不设置 HLINK_COMMANDBUTTON 或  HLINK_NOTIFYBUTTON ,当点击链接时,CHyperLink调用它的 Navigate(),Navigate() 调用ShellExecuteEx() 在默认的浏览器中打开URL。如果你想再点击链接后执行其他的一些行为,设置HLINK_COMMANDBUTTON 或HLINK_NOTIFYBUTTON并处理消息。
Other CHyperLink details
我们可以为静态文本控件设置 SS_CENTER 或  SS_RIGHT ,使静态文本中对齐或右对齐,但是,如果控件设置了 HLINK_USETAGS 或 HLINK_USETAGSBOLD ,文本只能左对齐。
如果你使用CHyperLink 打开一个URL(没有设置set  HLINK_COMMANDBUTTON 或  HLINK_NOTIFYBUTTON),你不可以使用 SetToolTipText()更改ToolTip文本。但是你可以直接处理CHyperLink 的tooltip控件成员m_tip,并使用AddTool()设置文本。
[cpp]  view plain copy
  1. m_wndLink.m_tip.AddTool ( m_wndLink, _T("Clickety!"), &m_wndLink.m_rcLink, 1 );  
注意,自WTL7.0,这里有一个重大更改,WTL7.1中 CHyperLink使用tooltip的ID为1,而WTL7.0中,这个ID是窗口句柄且通过使用m_tip.UpdateTipText()更新文本。
由于一些绘制问题, HLINK_USETAGS 和  HLINK_USETAGSBOLD 是最好用的,当超链接文本是在一行文本中时。绘制代码查找 <a> 并将文本分割成三部分。但是如果某部分需要断字,它将会不正确地自动换行。

你应该确保 HLINK_UNDERLINEHOVER 不和 HLINK_USETAGSBOLD一起设置。因为这会导致链接文本后出现一些空格,如上第一个所示。

UI Updating Dialog Controls

WTL中比MFC更容易更新UI。MFC中,你必须了解WM_KICKIDLE消息并且处理此消息触发UI更新。在WTL中,不需这样做,虽然在AppWizard中有一个缺陷:我们需要手工添加一行代码。
首先要做的第一件事情就是对话框必须是非模态的。这是因为CUpdateUI 要工作,你的程序需要控制消息循环。如果使对话框为模态的,系统控制了消息循环,导致空闲处理无法触发。CUpdateUI 是在空闲处理时间内工作的,因此没有空闲处理就没有UI更新。
对话框类的定义如下,与框架窗口类类似:

[cpp]  view plain copy
  1. class CMainDlg : public CDialogImpl<CMainDlg>, public CUpdateUI<CMainDlg>,  
  2.                  public CMessageFilter, public CIdleHandler  
  3. {  
  4. public:  
  5.     enum { IDD = IDD_MAINDLG };  
  6.    
  7.     BOOL PreTranslateMessage(MSG* pMsg);  
  8.     BOOL OnIdle();  
  9.    
  10.     BEGIN_MSG_MAP_EX(CMainDlg)  
  11.         MSG_WM_INITDIALOG(OnInitDialog)  
  12.         COMMAND_ID_HANDLER_EX(IDOK, OnOK)  
  13.         COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel)  
  14.         COMMAND_ID_HANDLER_EX(IDC_ALYSON_BTN, OnAlysonODBtn)  
  15.     END_MSG_MAP()  
  16.    
  17.     BEGIN_UPDATE_UI_MAP(CMainDlg)  
  18.     END_UPDATE_UI_MAP()  
  19. //...  
  20. };  

注意:CMainDlg派生于CUpdateUI,并且有一个UI更新路由。OnInitDialog()添加消息循环和空闲处理,这与之前的框架窗口的例子相似:

[cpp]  view plain copy
  1. // register object for message filtering and idle updates  
  2. CMessageLoop* pLoop = _Module.GetMessageLoop();  
  3. ATLASSERT(pLoop != NULL);  
  4. pLoop->AddMessageFilter(this);  
  5. pLoop->AddIdleHandler(this);  
  6.   
  7. UIAddChildWindowContainer(m_hWnd);  

注意,这里调用UIAddChildWindowContainer(),而不是框架窗口例子中的UIAddToolbar() 或UIAddStatusBar()。这告诉CUpdateUI包含需要更新的子窗口。
如果此时关注OnIdle()的代码,会发现这里少了一行代码,AppWizard没有生成,我们需要手工添加:

[cpp]  view plain copy
  1. BOOL CMainDlg::OnIdle()  
  2. {  
  3.     UIUpdateChildWindows();  
  4.     return FALSE;  
  5. }  

为了演示UI更新,当点击左侧的位图按钮时,右侧的按钮激活或禁止。首先,在UI更新路由中添加一个条目,使用UPDUI_CHILDWINDOW 标识这个条目是指示子窗口的:

[cpp]  view plain copy
  1. BEGIN_UPDATE_UI_MAP(CMainDlg)  
  2.     <strong>UPDATE_ELEMENT(IDC_ALYSON_BMPBTN, UPDUI_CHILDWINDOW)</strong>  
  3. END_UPDATE_UI_MAP()  

然后在左侧按钮的处理中,调用UIEnable() 开启或关闭右侧按钮的活动状态:

[cpp]  view plain copy
  1. void CMainDlg::OnAlysonODBtn ( UINT uCode, int nID, HWND hwndCtrl )  
  2. {  
  3.    <strong> UIEnable ( IDC_ALYSON_BMPBTN, !m_wndBmpBtn.IsWindowEnabled() );</strong>  
  4. }  

DDV

WTL的对话框数据有效性检查(Dialog Data Validation)比MFC要简单一些。在MFC中,你需要为DDX和DDV创建独立的宏,而在WTL,一个宏同时支持两个功能。在WTL中,下面的宏在DDX路由中包含基本的DDV支持:

DDX_TEXT_LEN
像DDX_TEXT 执行DDX并验证字符串的长度(不计入空终结符)不大于限值。
DDX_INT_RANGE and  DDX_UINT_RANGE
DDX_INT 和  DDX_UINT 执行DDX,附加验证值是否在给定的最小值和最大值范围内。
DDX_FLOAT_RANGE
像DDX_FLOAT 执行DDX,并验证值是否在给定的最小值和最大值范围内。
DDX_FLOAT_P_RANGE (new in WTL 7.1)
DDX_FLOAT_P 执行DDX, 并验证值是否在给定的最小值和最大值范围内。

这些宏的参数与不带有效性检查的宏相比,多了一到两个指定数值接受的范围,DDX_TEXT_LEN多了一个参数,指定字符串最大可允许长度。

比如,上面IDC_FAV_SEASON的范围为[1,7],DDV宏的使用如下:

[cpp]  view plain copy
  1. BEGIN_DDX_MAP(CMainDlg)  
  2.     //...  
  3.     <strong>DDX_INT_RANGE(IDC_FAV_SEASON, m_nSeason, 1, 7)</strong>  
  4. END_DDX_MAP()  

OnOK调用DoDataExchange()时将检查IDC_FAV_SEASON数值的有效性,同时将数据写入m_nSeason。

Handling DDV failures

如果对话框数据有效性检查失败,CWinDataExchange 将会调用可重载的方法OnDataValidateError()并且DoDataExchange()返回false。OnDataValidateError() 的默认处理是扬声器发声,我们应该提供一个更友好的错误处理。OnDataValidateError()的函数原型:

[cpp]  view plain copy
  1. void OnDataValidateError ( UINT nCtrlID, BOOL bSave, _XData& data );  

_XData是一个数据结构,它由CWinDataExchange填充,包含当前填入的数据和可允许的范围等。

[cpp]  view plain copy
  1. struct _XData  
  2. {  
  3.     _XDataType nDataType;  
  4.     union  
  5.     {  
  6.         _XTextData textData;  
  7.         _XIntData intData;  
  8.         _XFloatData floatData;  
  9.     };  
  10. };  

nDataType 指示联合结构体中那个有效。它的可能值为:

[cpp]  view plain copy
  1. enum _XDataType  
  2. {  
  3.     ddxDataNull = 0,  
  4.     ddxDataText = 1,  
  5.     ddxDataInt = 2,  
  6.     ddxDataFloat = 3,  
  7.     ddxDataDouble = 4  
  8. };  

在我们的例子中,nDataType的值为ddxDataInt,这意味着_XData 中的_XIntData将会被填充数据。

[cpp]  view plain copy
  1. struct _XIntData  
  2. {  
  3.     long nVal;  
  4.     long nMin;  
  5.     long nMax;  
  6. };  

我们要重载OnDataValidateError()告诉用户可允许的范围:

[cpp]  view plain copy
  1. void CMainDlg::OnDataValidateError ( UINT nCtrlID, BOOL bSave, _XData& data )  
  2. {  
  3.     CString sMsg;  
  4.    
  5.     sMsg.Format ( _T("Enter a number between %d and %d"),  
  6.                   data.intData.nMin, data.intData.nMax );  
  7.    
  8.     MessageBox ( sMsg, _T("ControlMania2"), MB_ICONEXCLAMATION );  
  9.    
  10.     GotoDlgCtrl ( GetDlgItem(nCtrlID) );  
  11. }  

Resizing Dialogs

在对话框类的继承列表中添加CDialogResize 并在OnInitDialog()中调用DlgResize_Init(),然后链接消息到CDialogResize。


原文:WTL for MFC Programmers, Part V - Advanced Dialog UI Classes


转自:http://blog.csdn.net/wcyoot/article/details/6688356

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值