分享
 
 
 

MFC程序员的WTL指南: Part V - 高级对话框用户界面类

王朝c/c++·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

MFC程序员的WTL指南: Part V - 高级对话框用户界面类

原作 :Michael Dunn [英文原文]

翻译 :Orbit(桔皮干了) [www.winmsg.com]

下载演示程序代码

本章内容

第五章介绍

特别的自画和外观定制类

COwnerDraw

CCustomDraw

WTL的新控件

CBitmapButton

CCheckListViewCtrl

CTreeViewCtrlEx 和 CTreeItem

CHyperLink

对话框中控件的UI Updating

DDV

处理DDV验证失败

改变对话框的大小

继续

参考

修改记录 第五章介绍

在上一篇文章我们介绍了一些与对话框和控件有关的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类那样使用这个函数。

WTL的新控件

WTL有几个新控件,它们要么是其他封装类的扩展(像 CTreeViewCtrlEx),要么是提供windows标准控件没有的新功能(像 CHyperLink)。

CBitmapButton

WTL的CBitmapButton类声明在atlctrlx.h中,它比MFC的同名类使用起来要简单的多。WTL的CBitmapButton类使用image list而不是单个的位图资源,你可以将多个按钮的图像放到一个位图文件中,减少GDI资源的占用。这对于使用很多图片并需要在Windows 9X系统上运行的程序很有好处,因为使用太多的单个位图将会很快耗尽GDI资源并导致系统崩溃。

CBitmapButton是一个CWindowImpl派生类,它又很多特色:自动调整控件的大小,自动生成3D边框,支持hot-tracking,每个按钮可以使用多个图像分别表示按钮的不同状态。

在ControlMania2中,我们对前面的例子创建的自画按钮使用CBitmapButton类。现在CMainDlg对话框类中添加CBitmapButton类型的变量m_wndBmpBtn,调用SubclassWindow()函数或使用DDX将其和控件联系起来,将位图装载到image list并告诉按钮使用这个image list,还要告诉按钮每个图像分别对应按钮的什么状态。下面是OnInitDialog()函数中建立和使用这个按钮的代码段:

// Set up the bitmap button

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()不能delete它所创建的image list。下面显示的是新按钮的一般状态,注意控件是如何根据图像的大小来调整自己的大小。

因为CBitmapButton是一个非常有用的类,我想介绍一下它的公有方法。

CBitmapButton methods

CBitmapButtonImpl类包含了实现一个按钮的所有代码,除非你想重载某个方法或消息处理,你可以对控件直接使用CBitmapButton类。

CBitmapButtonImpl constructorCBitmapButtonImpl(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE,HIMAGELIST hImageList = NULL)

构造函数可以指定按钮的扩展样式(这与窗口的样式不冲突)和图像列表,通常使用默认参数就足够了,因为可以使用其他的方法设定这些属性。

SubclassWindow()BOOL SubclassWindow(HWND hWnd)

SubclassWindow()是个重载函数,主要完成控件的子类化和初始化控件类保有的内部数据。

Bitmap button extended stylesDWORD GetBitmapButtonExtendedStyle()

DWORD SetBitmapButtonExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)

CBitmapButton支持一些扩展样式,这些扩展样式会对按钮的外观和操作方式产生影响:

BMPBTN_HOVER

使用hot-tracking,当鼠标移到按钮上时按钮被画成焦点状态。

BMPBTN_AUTO3D_SINGLE, BMPBTN_AUTO3D_DOUBLE

在按钮图像周围自动产生一个三维边框,当按钮拥有焦点时会显示一个表示焦点的虚线矩形框。另外如果你没有指定按钮按下状态的图像,将会自动生成一个。BMPBTN_AUTO3D_DOUBLE样式生成的边框稍微粗一些,其他特征和BMPBTN_AUTO3D_SINGLE一样。

BMPBTN_AUTOSIZE

按钮调整自己的大小以适应图像大小,这是默认样式。

BMPBTN_SHAREIMAGELISTS

如果指定这个样式,按钮不负责销毁按钮使用的image list,如果不使用这个样式,CBitmapButton的析构函数会销毁按钮使用的image list。

BMPBTN_AUTOFIRE

如果设置这个样式,在按钮上按住鼠标左键不放将会产生连续的WM_COMMAND消息。

调用SetBitmapButtonExtendedStyle()时,dwMask参数控制着那个样式将被改变,默认值是0,意味着用新样式完全替换旧的样式。

Image list managementHIMAGELIST GetImageList()

HIMAGELIST SetImageList(HIMAGELIST hImageList)

调用SetImageList()设置按钮使用的image list。

Tooltip managementint GetToolTipTextLength()

bool GetToolTipText(LPTSTR lpstrText, int nLength)

bool SetToolTipText(LPCTSTR lpstrText)

CBitmapButton支持显示工具提示(tooltip),调用SetToolTipText()指定显示的文字。

Setting the images to usevoid SetImages(int nNormal, int nPushed = -1,int nFocusOrHover = -1, int nDisabled = -1)

调用SetImages()函数告诉按钮分别使用image list的拿一个图像表示那个状态。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。

下面是一个定义list view扩展样式属性的例子,加入了一个使用这个样式的新类。(注意,扩展属性必须包含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 SubclassWindow()

当子类化一个已经存在的list view控件时,SubclassWindow()查看CCheckListViewCtrlImplTraits的扩展样式属性并将之应用到控件上。未用到前两个参数(窗口样式和扩展窗口样式)。

SetCheckState() and GetCheckState()

这些方法实际上是在CListViewCtrl中,SetCheckState()使用行的索引和一个布尔类型参数,该布尔参数的值表示是否check这一行。GetCheckState()以行索引未参数,返回改行的checked状态。

CheckSelectedItems()

这个方法使用item的索引作为参数,它翻转这个item的check状态,这个item必须是被选定的,同时还将其他所有被选择的item设置成相应状态(译者加:多选状态下)。你大概不会用到这个方法,因为CCheckListViewCtrl会在check box被单击或用户按下了空格键时设置相应的item的状态。

下面是ControlMania2中的CCheckListViewCtrl的样子:

CTreeViewCtrlEx and CTreeItem

有两个类使得树控件的使用简化了很多:CTreeItem类封装了HTREEITEM,一个CTreeItem对象含有一个HTREEITEM和一个指向包含这个HTREEITEM的树控件的指针,使你不必每次调用都引用树控件;CTreeViewCtrlEx和CTreeViewCtrl一样,只是它的方法操作CTreeItem而不是HTREEITEM。例如,InsertItem()函数返回一个CTreeItem而不是HTREEITEM,你可以使用CTreeItem操作新添加的item。下面是一个例子:

// 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, 100 );

// Using CTreeItems:

CTreeItem ti, ti2;

ti = m_wndTreeEx.InsertItem ( "foo", TVI_ROOT, TVI_LAST );

ti2 = ti.AddTail ( "bar", 0 );

ti2.SetData ( 100 );

CTreeViewCtrl对HTREEITEM的每一个操作,CTreeItem都有与之对应的方法,正像每一个关于HWND的API都有一个CWindow方法与之对应一样。查看ControlMania2的代码可以看到更多的CTreeViewCtrlEx和CTreeItem类的方法的演示。

CHyperLink

CHyperLink是一个CWindowImpl派生类,它子类化一个static text控件,使之变成可点击的超链接。CHyperLink根据用户的IE使用的颜色画链接对象,还支持键盘导航。CHyperLink类的构造函数没有参数,下面是其它的公有方法。

CHyperLink methods

CHyperLinkImpl类内含实现一个超链接的全部代码,如果不需要重载它的方法或处理消息的话,你可以直接使用CHyperLink类。

SubclassWindow()BOOL SubclassWindow(HWND hWnd)

重载函数SubclassWindow()完成控件子类化,然后初始化该类保有的内部数据。

Text label managementbool GetLabel(LPTSTR lpstrBuffer, int nLength)

bool SetLabel(LPCTSTR lpstrLabel)

获得或设置控件显示的文字,如果不指定显示文字,控件会显示资源编辑器指定给控件的静态字符串。

Hyperlink managementbool GetHyperLink(LPTSTR lpstrBuffer, int nLength)

bool SetHyperLink(LPCTSTR lpstrLink)

获得或设置控件关联超链接的URL,如果不指定超链接URL,控件会使用显示的文字字符串作为URL。

Navigationbool Navigate()

导航到当前超链接的URL,该URL或者是由SetHyperLink()函数指定的URL,或者就是控件的窗口文字。

Tooltip management

没有公开的方法设置工具提示,所以需要直接使用CToolTipCtrl成员m_tip。

下图显示的就是ControlMania2对话框中的超链接控件:

在OnInitDialog()函数中设置URL:

m_wndLink.SetHyperLink ( _T("http://www.codeproject.com/") );

对话框中控件的UI Updating

对话框中的的UI updating控制比MFC中简单得多,在MFC中,你需要响应未公开的WM_KICKIDLE消息,处理这个消息并触发控件的updating,在WTL中,没有这个诡计,不过向导存在一个BUG,需要手工添加一行代码解决这个问题。

首先需要记住的是对话框必须是无模式的,因为CUpdateUI需要在程序的消息循环控制下工作。如果对话框是模式的,系统处理消息循环,我们程序的空闲处理函数就不会被调用,由于CUpdateUI是在空闲时间工作的,所以没有空闲处理就没有UI updating。

ControlMania2的对话框是非模式的,类定义的开始部分很像是一个框架窗口类:

class CMainDlg : public CDialogImpl<CMainDlg>, public CUpdateUI<CMainDlg>,

public CMessageFilter, public CIdleHandler

{

public:

enum { IDD = IDD_MAINDLG };

virtual BOOL PreTranslateMessage(MSG* pMsg);

virtual 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派生并含有一个update UI链。OnInitDialog()做了这些工作,这和前面介绍的框架窗口中的代码很相似:

// register object for message filtering and idle updates

CMessageLoop* pLoop = _Module.GetMessageLoop();

ATLASSERT(pLoop != NULL);

pLoop->AddMessageFilter(this);

pLoop->AddIdleHandler(this);

UIAddChildWindowContainer(m_hWnd);

只是这次我们不是调用UIAddToolbar()或UIAddStatusBar(),而是调用UIAddChildWindowContainer(),它告诉CUpdateUI我们的对话框含有需要updating的字窗口,只要看看OnIdle(),你会怀疑少了写什么:

BOOL CMainDlg::OnIdle()

{

return FALSE;

}

你可能猜想这里应该调用另一个CUpdateUI的方法做一些实在的updating工作,你是对的,应该是这样的,向导在OnIdle()中漏掉了一行代码,现在加上:

BOOL CMainDlg::OnIdle()

{

UIUpdateChildWindows();

return FALSE;

}

为了演示UI updating,我们设定鼠标点击左边的位图按钮,使得右边的按钮变得可用或禁用。先在update 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 )

{

static bool s_bBtnEnabled = true;

s_bBtnEnabled = !s_bBtnEnabled;

UIEnable ( IDC_ALYSON_BMPBTN, s_bBtnEnabled );

}

DDV

WTL的对话框数据验证(DDV)比MFC简单一些,在MFC中你需要分别使用DDX(对话框数据交换)宏和DDV(对话框数据验证)宏,在WTL中只需一个宏就可以了,WTL包含基本的数据验证支持,在DDV链中可以使用三个宏:

DDX_TEXT_LEN

和DDX_TEXT一样,只是还要验证字符串的长度(不包含结尾的空字符)小于或等于限制长度。

DDX_INT_RANGE and DDX_UINT_RANGE

和DDX_INT,DDX_UINT一样,还加了对数字的最大最小值的验证。

DDX_FLOAT_RANGE

除了像DDX_FLOAT一样完成数据交换之外,还验证数字的最大最小值。

ControlMania2有一个ID是IDC_FAV_SEASON的edit box,它和成员变量m_nSeason相关联。

由于有效的值是1到7,所以使用这样的数据验证宏:

BEGIN_DDX_MAP(CMainDlg)

//...

DDX_INT_RANGE(IDC_FAV_SEASON, m_nSeason, 1, 7)

END_DDX_MAP()

OnOK()调用DoDataExchange()获得season的数值,并验证是在1到7之间。

处理DDV验证失败

如果控件的数据验证失败,CWinDataExchange会调用重载函数OnDataValidateError(),默认到处理是驱动PC喇叭发出声音,你可能想给出更友好的错误指示。OnDataValidateError()的函数原型是:

void OnDataValidateError ( UINT nCtrlID, BOOL bSave, _XData& data );

_XData是一个WTL的内部数据结构,CWinDataExchange根据输入的数据和允许的数据范围填充这个数据结构。下面是这个数据结构的定义:

struct _XData

{

_XDataType nDataType;

union

{

_XTextData textData;

_XIntData intData;

_XFloatData floatData;

};

};

nDataType指示联合中的三个成员那个是有意义的,nDataType 的取值可以是:

enum _XDataType

{

ddxDataNull = 0,

ddxDataText = 1,

ddxDataInt = 2,

ddxDataFloat = 3,

ddxDataDouble = 4

};

在我们的例子中,nDataType的值是ddxDataInt,这表示_XData中的_XIntData成员是有效的,_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 );

::SetFocus ( GetDlgItem(nCtrlID) );

}

_XData中的另外两个结构_XTextData和_XFloatData的定义在atlddx.h中,感兴趣的话可以打开这个文件查看一下。

改变对话框的大小

WTL引起我的注意的第一件事是对可调整大小对话框的内建的支持。在这之前我曾写过一篇关于这个主题的文章,详情请参考这篇文章。简单的说就是将CDialogResize类添加到对话框的集成列表,在OnInitDialog()中调用DlgResize_Init(),然后将消息链入CDialogResize。

继续

下一章,我将介绍如何在对话框中使用ActiveX控件和如何处理控件触发的事件。

参考

Using WTL''s Built-in Dialog Resizing Class - Michael Dunn

Using DDX and DDV with WTL - Less Wright

修改记录

2003年4月28日,本文第一次发表。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有