源代码: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_MEASUREITEM
, WM_DRAWITEM
, WM_COMPAREITEM
, 和WM_DELETEITEM。COwnerDraw
类,在atlframe.h中定义,为我们简化了代码,因为在此类中我们不需要为这些消息路由,而是链接消息到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_*;然而ALT_MSG_MAP(1)里的消息路由处理消息的反射版本OCM_*。所有者绘制控件的消息通知,像WM_NOTIFY,可以在它们的副控件中处理,也可以反射到控件本身,如果选择前者,消息链接直接到COwnerDraw
-
- class CSomeDlg : public CDialogImpl<CSomeDlg>,
- public COwnerDraw<CSomeDlg>, ...
- {
- BEGIN_MSG_MAP(CSomeDlg)
-
- <strong> CHAIN_MSG_MAP(COwnerDraw<CSomeDlg>)</strong>
- END_MSG_MAP()
-
- void DrawItem ( LPDRAWITEMSTRUCT lpdis );
- };
然而,如果你想要使控件处理消息,就需要使用CHAIN_MSG_MAP_ALT
宏链接消息到ALT_MSG_MAP(1)段:
-
- class CMyButton : public CWindowImpl<CMyButton, CButton>,
- public COwnerDraw<CMyButton>, ...
- {
- BEGIN_MSG_MAP(CMyButton)
-
- <strong> CHAIN_MSG_MAP_ALT(COwnerDraw<CMyButton>, 1)
- DEFAULT_REFLECTION_HANDLER()</strong>
- END_MSG_MAP()
-
- void DrawItem ( LPDRAWITEMSTRUCT lpdis );
- };
COwnerDraw
解包消息参数,调用实现函数。我们可重载的消息处理方法有:
- void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
- void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
- int CompareItem(LPCOMPAREITEMSTRUCT lpCompareItemStruct);
- void DeleteItem(LPDELETEITEMSTRUCT lpDeleteItemStruct);
如果因某原因不想在重载中处理消息,可以调用SetMsgHandled(false),然后消息将会传递到之后的消息路由中的其他处理中。
例子中,我们创建一个所有者绘制的button,并且在Button的实现类中处理反射的消息WM_DRAWITEM
,下面是资源编辑器下的界面:
下面是Button的实现类:
- class CODButtonImpl : public CWindowImpl<CODButtonImpl, CButton>,
- public COwnerDraw<CODButtonImpl>
- {
- public:
- BEGIN_MSG_MAP_EX(CODButtonImpl)
- <strong> CHAIN_MSG_MAP_ALT(COwnerDraw<CODButtonImpl>, 1)
- DEFAULT_REFLECTION_HANDLER()</strong>
- END_MSG_MAP()
-
- <strong>void DrawItem ( LPDRAWITEMSTRUCT lpdis );</strong>
- };
DrawItem()调用GDI命令,为button绘制一张图:
- void CODButtonImpl::DrawItem ( LPDRAWITEMSTRUCT lpdis )
- {
-
- CDCHandle dc = lpdis->hDC;
- CDC dcMem;
-
- dcMem.CreateCompatibleDC ( dc );
- dc.SaveDC();
- dcMem.SaveDC();
-
-
- if ( lpdis->itemState & ODS_FOCUS )
- dc.FillSolidRect ( &lpdis->rcItem, RGB(255,0,0) );
- else
- dc.FillSolidRect ( &lpdis->rcItem, RGB(0,0,255) );
-
-
-
- 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);
- }
下面是button的表现形式:
CCustomDraw
CCustomDraw的工作方式与COwnDraw相似,它处理NM_CUSTOMDRAW消息并链接它们。CCustomDraw在自定义绘制的每一个阶段都有一个可重载的函数:
- 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,因此如果你需要执行自定义的绘制或者需要不同的返回值,你需要重载对应的方法。
在上张截图中,树形视图中“Drawn”的颜色是绿色的,这是通过使用一个继承于CTreeCtrl,链接消息到CCustomDraw并且重载了OnPrePaint()
和OnItemPrePaint()的新类CBuffyTreeCtrl。当树被填充时,“Drawn”节点的数据被设为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;
- }
就如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()
中设置按钮的代码片段:
-
- CImageList iml;
-
- iml.CreateFromImage ( IDB_ALYSON_IMGLIST, 81, 1, CLR_NONE,
- IMAGE_BITMAP, LR_CREATEDIBSECTION );
-
- m_wndBmpBtn.SubclassWindow ( GetDlgItem(IDC_ALYSON_BMPBTN) );
- m_wndBmpBtn.SetToolTipText ( _T("Alyson") );
- m_wndBmpBtn.SetImageList ( iml );
- m_wndBmpBtn.SetImages ( 0, 1, 2, 3 );
默认下,这个按钮占用image list的所有权,因此OnInitDialog()不能释放它创建的imagelist。
CBitmapButton是个非常有用的类,下面是它的方法:
CBitmapButton methods
CBitmapButtonImpl
类包含了按钮的所有实现,除非需要重载方法和消息处理函数,否则就可以直接使用CBitmapButton。
-
- CBitmapButtonImpl(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE,
- HIMAGELIST hImageList = NULL)
-
- BOOL SubclassWindow(HWND hWnd)
-
-
- DWORD SetBitmapButtonExtendedStyle(DWORD dwExtendedStyle,
- DWORD dwMask = 0)
BMPBTN_HOVER
:激活热跟踪,当鼠标停在按钮上时,以焦点状态绘制
BMPBTN_AUTO3D_SINGLE
,
BMPBTN_AUTO3D_DOUBLE
:在图像边缘自动生成3D边框,以及鼠标得到焦点时的焦点矩形。如果你没有提供按下状态的图像,它会给你生成一个。
BMPBTN_AUTO3D_DOUBLE
提供一个稍厚的边框。
BMPBTN_AUTOSIZE
:使按钮自动调整大小以适应图像的大小。
BMPBTN_SHAREIMAGELISTS :如果设置,按钮对象不会销毁imagelist;否则,在CImageButton的析构函数中销毁imagelist。
BMPBTN_AUTOFIRE
: 如果设置,单击该按钮和按住该按钮生成重复的
WM_COMMAND消息。
当调用SetBitmapButtonExtendedStyle()
时,参数dwMask用于控制哪些风格生效,使用默认值0,表示用新的风格完全代替旧的。
- HIMAGELIST GetImageList()
- HIMAGELIST SetImageList(HIMAGELIST hImageList)
使用
GetImageList()
和
SetImageList()
关联imagelist和按钮,获取关联到按钮的当前的imagelist。
- int GetToolTipTextLength()
- bool GetToolTipText(LPTSTR lpstrText, int nLength)
- bool SetToolTipText(LPCTSTR lpstrText)
CBitmapButton支持当鼠标悬停在按钮上时,显示一个ToolTip。调用GetToolTipText() 和 SetToolTipText() 获取和设置tooltip的文本。
- 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
.
-
- typedef CCheckListViewCtrlImplTraits<
- WS_CHILD | WS_VISIBLE | LVS_REPORT,
- WS_EX_CLIENTEDGE,
- <strong> LVS_EX_CHECKBOXES</strong> | LVS_EX_GRIDLINES | LVS_EX_UNDERLINEHOT |
- LVS_EX_ONECLICKACTIVATE> CMyCheckListTraits;
-
- class CMyCheckListCtrl :
- public CCheckListViewCtrlImpl<CMyCheckListCtrl, CListViewCtrl,
- CMyCheckListTraits>
- {
- private:
- typedef CCheckListViewCtrlImpl<CMyCheckListCtrl, CListViewCtrl,
- CMyCheckListTraits> baseClass;
- public:
- BEGIN_MSG_MAP(CMyCheckListCtrl)
- CHAIN_MSG_MAP(baseClass)
- END_MSG_MAP()
- };
CCheckListViewCtrl methods
- BOOL SubclassWindow(HWND hWnd)
当你子类化一个已存在的Listview控件,
SubclassWindow()查看关联的CCheckListViewCtrlImplTraits中的扩展的Listview的风格并应用到控件中。CCheckListViewCtrlImplTraits
的前两个模板参数(windows styles and extended window styles)不使用。
- BOOL GetCheckState(int nIndex)
- BOOL SetCheckState(int nItem, BOOL bCheck)
获取和设置指定索引的条目的复选框状态。
- void CheckSelectedItems(int nCurrItem)
此方法使用一个条目索引,切换它的状态(该条目必须是已选择的)并且改变其他被选则的条目的复选框状态。你可能不会使用这个方法,当用户点击复选框或按空格键
CCheckListViewCtrl会自动切换状态。
CTreeViewCtrlEx and CTreeItem
这两个类通过封装
CTreeItem
使我们更易于使用tree control。CTreeItem
对象保存一个CTreeItem以及对应的树控件的指针。仅仅通过使用CTreeItem就可以执行针对某个item的操作。
CTreeViewCtrlEx
像CTreeViewCtrl,但是前者处理的是
CTreeItem
对象,而后者处理的是HTREEITEM 对象。
-
- HTREEITEM hti, hti2;
-
- hti = m_wndTree.InsertItem ( "foo", TVI_ROOT, TVI_LAST );
- hti2 = m_wndTree.InsertItem ( "bar", hti, TVI_LAST );
- m_wndTree.SetItemData ( hti2, 37 );
-
-
- CTreeItem ti, ti2;
-
- ti = m_wndTreeEx.InsertItem ( "baz", TVI_ROOT, TVI_LAST );
- ti2 = ti.AddTail ( "yen", 0 );
- 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中
- CHyperLinkImpl ( DWORD dwExtendedStyle = HLINK_UNDERLINED )
- CHyperLink()
CHyperLinkImpl
的构造函数提供一个应用到控件上得扩展风格。但是CHyperLink并没有与之对应的构造函数,不过可使用
SetHyperLinkExtendedStyle()
设置该属性。
- BOOL SubclassWindow(HWND hWnd)
子类化,初始化内部数据。如果使用DDX关联一个CHyperLink对象与一个静态文本控件,此函数会自动被执行,或者也可以手工调用去子类化控件。
- DWORD GetHyperLinkExtendedStyle()
- DWORD SetHyperLinkExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
获取和设置控件的扩展风格。你必须在SubclassWindow或
Create()之前设置扩展风格,以告诉控件如何绘制文本。
- bool GetLabel(LPTSTR lpstrBuffer, int nLength)
- bool SetLabel(LPCTSTR lpstrLabel)
获取和设置控件文本,如果不设置,将使用静态控件的初始文本。
- bool GetHyperLink(LPTSTR lpstrBuffer, int nLength)
- bool SetHyperLink(LPCTSTR lpstrLink)
获取和设置控件关联的URL文本。如果不设置,将使用静态控件的初始文本
- bool GetToolTipText(LPTSTR lpstrBuffer, int nLength)
- 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()设置文本。
- 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更新。
对话框类的定义如下,与框架窗口类类似:
- class CMainDlg : public CDialogImpl<CMainDlg>, public CUpdateUI<CMainDlg>,
- public CMessageFilter, public CIdleHandler
- {
- public:
- enum { IDD = IDD_MAINDLG };
-
- BOOL PreTranslateMessage(MSG* pMsg);
- BOOL OnIdle();
-
- BEGIN_MSG_MAP_EX(CMainDlg)
- MSG_WM_INITDIALOG(OnInitDialog)
- COMMAND_ID_HANDLER_EX(IDOK, OnOK)
- COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel)
- COMMAND_ID_HANDLER_EX(IDC_ALYSON_BTN, OnAlysonODBtn)
- END_MSG_MAP()
-
- BEGIN_UPDATE_UI_MAP(CMainDlg)
- END_UPDATE_UI_MAP()
-
- };
注意:CMainDlg派生于CUpdateUI,并且有一个UI更新路由。OnInitDialog()添加消息循环和空闲处理,这与之前的框架窗口的例子相似:
-
- CMessageLoop* pLoop = _Module.GetMessageLoop();
- ATLASSERT(pLoop != NULL);
- pLoop->AddMessageFilter(this);
- pLoop->AddIdleHandler(this);
-
- UIAddChildWindowContainer(m_hWnd);
注意,这里调用UIAddChildWindowContainer(),而不是框架窗口例子中的UIAddToolbar()
或UIAddStatusBar()。这告诉CUpdateUI包含需要更新的子窗口。
如果此时关注OnIdle()的代码,会发现这里少了一行代码,AppWizard没有生成,我们需要手工添加:
- BOOL CMainDlg::OnIdle()
- {
- UIUpdateChildWindows();
- return FALSE;
- }
为了演示UI更新,当点击左侧的位图按钮时,右侧的按钮激活或禁止。首先,在UI更新路由中添加一个条目,使用UPDUI_CHILDWINDOW
标识这个条目是指示子窗口的:
- BEGIN_UPDATE_UI_MAP(CMainDlg)
- <strong>UPDATE_ELEMENT(IDC_ALYSON_BMPBTN, UPDUI_CHILDWINDOW)</strong>
- END_UPDATE_UI_MAP()
然后在左侧按钮的处理中,调用UIEnable()
开启或关闭右侧按钮的活动状态:
- void CMainDlg::OnAlysonODBtn ( UINT uCode, int nID, HWND hwndCtrl )
- {
- <strong> UIEnable ( IDC_ALYSON_BMPBTN, !m_wndBmpBtn.IsWindowEnabled() );</strong>
- }
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宏的使用如下:
- BEGIN_DDX_MAP(CMainDlg)
-
- <strong>DDX_INT_RANGE(IDC_FAV_SEASON, m_nSeason, 1, 7)</strong>
- END_DDX_MAP()
OnOK调用DoDataExchange()时将检查IDC_FAV_SEASON数值的有效性,同时将数据写入m_nSeason。
Handling DDV failures
如果对话框数据有效性检查失败,CWinDataExchange
将会调用可重载的方法OnDataValidateError()并且DoDataExchange()返回false。OnDataValidateError()
的默认处理是扬声器发声,我们应该提供一个更友好的错误处理。OnDataValidateError()
的函数原型:
- void OnDataValidateError ( UINT nCtrlID, BOOL bSave, _XData& data );
_XData是一个数据结构,它由CWinDataExchange填充,包含当前填入的数据和可允许的范围等。
- struct _XData
- {
- _XDataType nDataType;
- union
- {
- _XTextData textData;
- _XIntData intData;
- _XFloatData floatData;
- };
- };
nDataType
指示联合结构体中那个有效。它的可能值为:
- enum _XDataType
- {
- ddxDataNull = 0,
- ddxDataText = 1,
- ddxDataInt = 2,
- ddxDataFloat = 3,
- ddxDataDouble = 4
- };
在我们的例子中,nDataType的值为ddxDataInt,这意味着_XData
中的_XIntData将会被填充数据。
- struct _XIntData
- {
- long nVal;
- long nMin;
- long nMax;
- };
我们要重载OnDataValidateError()告诉用户可允许的范围:
- void CMainDlg::OnDataValidateError ( UINT nCtrlID, BOOL bSave, _XData& data )
- {
- CString sMsg;
-
- sMsg.Format ( _T("Enter a number between %d and %d"),
- data.intData.nMin, data.intData.nMax );
-
- MessageBox ( sMsg, _T("ControlMania2"), MB_ICONEXCLAMATION );
-
- GotoDlgCtrl ( GetDlgItem(nCtrlID) );
- }
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