源代码: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

// C++ class for a dialog that contains owner-drawn controls
class CSomeDlg : public CDialogImpl<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)段:

// C++ class that implements an owner-drawn button
class CMyButton : public CWindowImpl<CMyButton, CButton>,public COwnerDraw<CMyButton>, ...
{BEGIN_MSG_MAP(CMyButton)//... CHAIN_MSG_MAP_ALT(COwnerDraw<CMyButton>, 1)DEFAULT_REFLECTION_HANDLER()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) CHAIN_MSG_MAP_ALT(COwnerDraw<CODButtonImpl>, 1)DEFAULT_REFLECTION_HANDLER()END_MSG_MAP()void DrawItem ( LPDRAWITEMSTRUCT lpdis );
};

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

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) );elsedc.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 );elsedc.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() 中设置按钮的代码片段:

    // Set up the bitmap buttonCImageList 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。

// 构造函数,设置Button的扩展风格(不要与窗口风格混淆),并可指定一个imagelist。// 通常默认的情况就足够了,我们可以用其他方法设置这两个属性
CBitmapButtonImpl(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE,HIMAGELIST hImageList = NULL)
// 重载,执行子类化并初始化内部数据
BOOL SubclassWindow(HWND hWnd)// 获取和设置位图按钮的扩展风格DWORD GetBitmapButtonExtendedStyle()
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.

// 注意:必须包括LVS_EX_CHECKBOXES,否则会有断言失败
typedef CCheckListViewCtrlImplTraits<WS_CHILD | WS_VISIBLE | LVS_REPORT, WS_EX_CLIENTEDGE, LVS_EX_CHECKBOXES | 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的操作。
CTreeViewCtrlExCTreeViewCtrl,但是前者处理的是 CTreeItem 对象,而后者处理的是HTREEITEM 对象。

    // Using plain HTREEITEMs: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 );// Using CTreeItems: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_USETAGSHLINK_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()添加消息循环和空闲处理,这与之前的框架窗口的例子相似:

    // register object for message filtering and idle updatesCMessageLoop* 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)UPDATE_ELEMENT(IDC_ALYSON_BMPBTN, UPDUI_CHILDWINDOW)END_UPDATE_UI_MAP()

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

void CMainDlg::OnAlysonODBtn ( UINT uCode, int nID, HWND hwndCtrl )
{ UIEnable ( IDC_ALYSON_BMPBTN, !m_wndBmpBtn.IsWindowEnabled() );
}

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)//...DDX_INT_RANGE(IDC_FAV_SEASON, m_nSeason, 1, 7)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

WTL入门(5)--- 高级的对话框UI类相关推荐

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

    源代码:http://download.csdn.net/source/3522809 上一篇文章中,讲述了一些WTL的关于对话框和控件的特性,本章中将讲述的新的WTL类实现了一些高级UI特性:所有者 ...

  2. 5.TypeScript入门之TS高级类型(class类)

    上一章节:4.TypeScript入门之TS常用类型(3) Ⅳ.TypeScript高级类型 概述 TS中的高级类型有很多,重点学习以下高级类型: class类 类型兼容性 交叉类型 泛型和 keyo ...

  3. MFC编程入门之十三(对话框:属性页对话框及相关类的介绍)

    前面讲了模态对话框和非模态对话框,本节来将一种特殊的对话框--属性页对话框. 属性页对话框的分类 属性页对话框想必大家并不陌生,XP系统中桌面右键点属性,弹出的就是属性页对话框,它通过标签切换各个页面 ...

  4. ❤️《Vue前端基础框架集合从入门到高级》(小白也可学,建议收藏)❤️

    <Vue前端基础框架集合从入门到高级>,小白也可学 文章目录 <Vue前端基础框架集合从入门到高级>,小白也可学 ❤️一.前端核心分析 ❤️1.1.概述 ❤️1.2.前端三要素 ...

  5. 【Qt】对话框QDialog类,模态对话框和非模态对话框

    QDialog类是所有对话框窗口类的基类.对话框窗口是一个用来完成短小任务或和用户进行简单交互的顶层窗口.按照运行对话框时是否还可以和该程序的其它窗口进行交互,将它分为两类:模态(modal)对话框和 ...

  6. Java 从入门到高级学习路线

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. Java 从入门到高级学习路线 <一>1.Jvm 部分 Jvm 内存模型.Jvm 内存结 ...

  7. 【Golang 快速入门】高级语法:反射 + 并发

    Golang 快速入门 Golang 进阶 反射 变量内置 Pair 结构 reflect 结构体标签 并发知识 基础知识 早期调度器的处理 GMP 模型 调度器的设计策略 并发编程 goroutin ...

  8. 速取,3D建模速成入门到高级教程(附软件安装包)

    同名公号回复"入门资料"获取3D建模速成入门到高级教程 大家好,我是华维导师,从事游戏建模师已有10年,曾参与过腾讯<漫威>,<魂斗罗>.网易<阴阳师 ...

  9. 【摘】Linux运维入门到高级全套常用要点

    Linux运维入门到高级全套常用要点 目 录 1. Linux 入门篇----------------------- 4 1. 1 Linux 操作系统简介-------------------.. ...

最新文章

  1. Android多媒体扫描过程(Android Media Scanner Prosess)
  2. matlab有限差分一维导热,一维导热方程-有限差分法-matlab实现11.docx
  3. c++对那些类型的数据不能使用引用_基于js数据类型浅谈deepClone
  4. 【转自CDDN】随笔:sysobjects.Xtype
  5. Linux怎么确定信号来源,Linux信号来源和捕获处理以及signal函数简介
  6. 2.Knockout.Js(监控属性Observables)
  7. 创建外部快照_快照事件:现在如何仅通过拍照即可创建日历事件
  8. oraclemt 无法启动服务_调整MT后台 解决站点压力问题
  9. 国有资产管理系统web
  10. 淘宝上传图片到淘宝 API 返回值说明(upload_img-上传图片到淘宝)
  11. 杨浦区服务器维修,上海杨浦区dns服务器地址
  12. 如何在5个月内做出月入3万的业余项目
  13. 横向扩展 纵向扩展 数据库_理解数据库扩展模式的指南
  14. win10 + cuda9.0+pytorch安装
  15. 八大排序 详解(下)——指向函数的指针 的使用
  16. 最后一天了,四个关键字回顾程序员小跃的2020
  17. 2019-9-2-程序员壁纸
  18. 10个免费学术论文期刊网站
  19. 大学生旅游网页制作作业5页 西柏坡介绍网页成品源代码下载 河北红色旅游景点网页设计
  20. JDK8新特性-Map遍历比较

热门文章

  1. 学生信息管理系统 Android studio (直接可用)
  2. 无线智能插座的java环境配置安装包下载
  3. Adobe Lightroom2023:完美的数字照片管理和处理软件
  4. 关于vivo输入法的使用评价
  5. Redis数据库密码设置和查看密码
  6. 焕新启航 强者不凡|锦江之星5.0品牌沙龙(苏州站)圆满落幕
  7. IDEA 报错:无效的源发行版
  8. c# image转换为bitmap_WPF将BitmapImage图片对象转换为Bitmap图片对象
  9. MultiDex工作原理
  10. CTF实验吧-上传绕过【0x00截断】