ATLWTL第五部分.docx
- 文档编号:11410450
- 上传时间:2023-02-28
- 格式:DOCX
- 页数:23
- 大小:148.74KB
ATLWTL第五部分.docx
《ATLWTL第五部分.docx》由会员分享,可在线阅读,更多相关《ATLWTL第五部分.docx(23页珍藏版)》请在冰豆网上搜索。
ATLWTL第五部分
第五部分-高级对话框UI类
内容
∙第五部分简介
∙属主绘制(OwnerDraw)以及定制绘制(CustomDraw)的专用类
∙COwnerDraw
∙CCustomDraw
∙新的WTL控件
∙CBitmapButton
∙CCheckListViewCtrl
∙CTreeViewCtrlEx和CTreeItem
∙CHyperLink
∙对话框控件的UI更新
∙DDV
∙处理DDV失败
∙改变对话框的大小
∙下一步
∙参考资料
∙修订历史
第五部分简介
在上一部分里,我们了解了一些关于对话框和控件的WTL特性,其工作方式与MFC中的对应类很相似。
在本部分里,我们会介绍几个新的WTL类,它们实现了一些更加高级的UI特性:
属主绘制(Ownerdraw)和定制绘制(Customdraw),新的WTL控件,UI更新,以及对话框数据验证(DDV)。
属主绘制以及定制绘制的专用类
由于属主绘制和定制绘制在GUI工作中已经变得相当的普遍,于是WTL提供几个嵌入类来处理这些烦人的事。
接下来我们会逐一介绍它们,作为我们的上一个示例工程的续集,现在我们从ControlMania2开始。
如果你是随着AppWizard来创建工程,就要确保你的对话框是非模态的。
为了使UI更新能正常工作,这是必须的。
在 UI更新一节中,我会给出更多的细节。
COwnerDraw
属主绘制包括对四个消息的处理:
WM_MEASUREITEM、WM_DRAWITEM、WM_COMPAREITEM 和 WM_DELETEITEM。
而在atlframe.h中定义的 COwnerDraw类则可以简化你的代码。
这是因为你不必再需要为这些消息写处理器了,而只需把消息串联到 COwnerDraw,后者就会调用在你的类中实现的覆盖函数。
如何串联消息取决于你是否把消息反射回了控件。
下面是 COwnerDraw 的消息映射,它清楚地显示出了其中的差异:
template
{
public:
BEGIN_MSG_MAP(COwnerDraw
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++classforadialogthatcontainsowner-drawncontrols
classCSomeDlg:
publicCDialogImpl
publicCOwnerDraw
{
BEGIN_MSG_MAP(CSomeDlg)
//...
CHAIN_MSG_MAP(COwnerDraw
END_MSG_MAP()
voidDrawItem(LPDRAWITEMSTRUCTlpdis);
};
不过如果你希望控件处理消息,你就要使用 CHAIN_MSG_MAP_ALT 宏把消息串联到 ALT_MSG_MAP
(1) 节:
//C++classthatimplementsanowner-drawnbutton
classCMyButton:
publicCWindowImpl
publicCOwnerDraw
{
BEGIN_MSG_MAP(CMyButton)
//...
CHAIN_MSG_MAP_ALT(COwnerDraw
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
voidDrawItem(LPDRAWITEMSTRUCTlpdis);
};
COwnerDraw 拆解随消息发送来的参数,然后调用你的类中的实现函数。
在上例中,该类实现了 DrawItem(),那么它在 WM_DRAWITEM 或者OCM_DRAWITEM 串联到 COwnerDraw 的时候就会被调用到。
你可以覆盖的方法有:
voidDrawItem(LPDRAWITEMSTRUCTlpDrawItemStruct);
voidMeasureItem(LPMEASUREITEMSTRUCTlpMeasureItemStruct);
intCompareItem(LPCOMPAREITEMSTRUCTlpCompareItemStruct);
voidDeleteItem(LPDELETEITEMSTRUCTlpDeleteItemStruct);
如果出于某些原因你不想处理某个消息,你可以调用 SetMsgHandled(false) 从而消息会传递下去,到达其后的消息映射中可能存在的某个处理器。
对于ControlMania2,我们将从ControlMania1中的树控件开始,并添加一个属主绘制按钮,然后在按钮类中处理反射的 WM_DRAWITEM。
下面就是资源编辑器中的新按钮:
现在,我们还需要一个实现此按钮的类:
classCODButtonImpl:
publicCWindowImpl
publicCOwnerDraw
{
public:
BEGIN_MSG_MAP_EX(CODButtonImpl)
CHAIN_MSG_MAP_ALT(COwnerDraw
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
voidDrawItem(LPDRAWITEMSTRUCTlpdis);
};
DrawItem() 使用诸如 BitBlt() 这样的GDI调用来在按钮表面上绘制一个图片。
代码应该很容易理解,因为WTL的类名和方法又一次类似于MFC。
voidCODButtonImpl:
:
DrawItem(LPDRAWITEMSTRUCTlpdis)
{
//NOTE:
m_bmpisaCBitmapinit'edintheconstructor.
CDCHandledc=lpdis->hDC;
CDCdcMem;
dcMem.CreateCompatibleDC(dc);
dc.SaveDC();
dcMem.SaveDC();
//Drawthebutton'sbackground,redifithasthefocus,blueifnot.
if(lpdis->itemState&ODS_FOCUS)
dc.FillSolidRect(&lpdis->rcItem,RGB(255,0,0));
else
dc.FillSolidRect(&lpdis->rcItem,RGB(0,0,255));
//Drawthebitmapinthetop-left,oroffsetby1pixelifthebutton
//isclicked.
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 消息的。
CCustomDraw 针对每个定制绘制的步骤都有一个可覆盖的方法:
DWORDOnPrePaint(intidCtrl,LPNMCUSTOMDRAWlpNMCD);
DWORDOnPostPaint(intidCtrl,LPNMCUSTOMDRAWlpNMCD);
DWORDOnPreErase(intidCtrl,LPNMCUSTOMDRAWlpNMCD);
DWORDOnPostErase(intidCtrl,LPNMCUSTOMDRAWlpNMCD);
DWORDOnItemPrePaint(intidCtrl,LPNMCUSTOMDRAWlpNMCD);
DWORDOnItemPostPaint(intidCtrl,LPNMCUSTOMDRAWlpNMCD);
DWORDOnItemPreErase(intidCtrl,LPNMCUSTOMDRAWlpNMCD);
DWORDOnItemPostEraset(intidCtrl,LPNMCUSTOMDRAWlpNMCD);
DWORDOnSubItemPrePaint(intidCtrl,LPNMCUSTOMDRAWlpNMCD);
它们的缺省处理全都返回 CDRF_DODEFAULT,因此,如果你需要进行自己的绘制或者是返回其他不同的值,你覆盖其一即可。
你可能在最后一个截图上已经注意到了,“Dawn”是用绿色显示的。
这是由于使用了一个从 CTreeCtrl 派生来的新类(源代码中名为CBuffyTreeCtrl)把消息串联到了 CCustomDraw 并覆盖了 OnPrePaint() 和 OnItemPrePaint() 方法。
在树填充之后,该节点的附加数据被设置成了1,而 OnItemPrePaint() 会检查此值,并且一旦发现就会改变文本的颜色。
DWORDCBuffyTreeCtrl:
:
OnPrePaint(
intidCtrl,LPNMCUSTOMDRAWlpNMCD)
{
returnCDRF_NOTIFYITEMDRAW;
}
DWORDCBuffyTreeCtrl:
:
OnItemPrePaint(
intidCtrl,LPNMCUSTOMDRAWlpNMCD)
{
if(1==lpNMCD->lItemlParam)
pnmtv->clrText=RGB(0,128,0);
returnCDRF_DODEFAULT;
}
和在 COwnerDraw 中的一样,在定制绘制的消息处理器中你也可以调用 SetMsgHandled(false) 函数以使得消息可以传递到消息映射中的其它处理器那儿。
新的WTL控件
WTL有它自己的几个新控件,有的是对其他封装类的增强(比如 CTreeViewCtrlEx),有的则是提供了内建控件所没有的新功能(比如CHyperLink)。
CBitmapButton
WTL的 CBitmapButton,声明于atlctrlx.h中,较之于MFC版本更加易用。
WTL版本使用了图像列表控件而非四个独立的位图资源,这就意味着你可以在一个位图中保存多个按钮的图像,从而多少降低了一些GDI资源占用。
如果你的应用有很多的图像而又正好运行于Windows9x,那这一点就尤为注目,因为使用大量的独立图像会很快耗尽GDI资源并使系统完蛋。
CBitmapButton 是一个 CWindowImpl 派生类,拥有众多特性:
自动调整控件大小,自动生成三维边框,热追踪(hot-tracking)支持,每个按钮可以使用多个图像用以区别按钮所处的不同状态。
在ControlMania2中,我们将在前面所创建的属主绘制按钮旁边创建一个 CBitmapButton。
首先,我们向 CMainDlg 添加一个名为m_wndBmpBtn 的 CBitmapButton 成员。
然后再用常用的方法把它连接到新按钮上,既可以调用 SubclassWindow() 也可以使用DDX。
把一幅位图加载到图像列表中,再告诉按钮将要使用此列表。
然后告诉按钮列表中的那个图像对应哪一种控件状态。
下面是从 OnInitDialog() 中取来的一段设置按钮的代码:
//Setupthebitmapbutton
CImageListiml;
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);
缺省情况下,按钮会假定它拥有该图像列表,因此 OnInitDialog() 千万不能删除它所创建的图像列表。
下面是缺省状态下的新按钮。
可以看到该控件的大小已经改变,正好适合图像的大小。
因为 CBitmapButton 是一个非常有用的类,所以我在这儿会介绍它的公用方法。
CBitmapButton的方法
CBitmapButtonImpl 类包含了实现一个按钮的所有代码,不过除非你要覆盖某个方法或者消息处理器,你可以为你的控件使用 CBitmapButton类。
CBitmapButtonImpl构造函数
CBitmapButtonImpl(DWORDdwExtendedStyle=BMPBTN_AUTOSIZE,
HIMAGELISThImageList=NULL)
构造函数设置按钮的扩展风格(不要与他的窗口风格混淆),而且还可以关联一个图像列表。
通常使用缺省值就足够了,因为你可以用其他的方法来设置这些属性。
BOOLSubclassWindow(HWNDhWnd)
SubclassWindow() 是一个覆盖版本的方法,用来执行子类化并初始化本类拥有的内部数据。
DWORDGetBitmapButtonExtendedStyle()
DWORDSetBitmapButtonExtendedStyle(DWORDdwExtendedStyle,
DWORDdwMask=0)
CBitmapButton 支持一些影响其外观或者操作的扩展风格:
BMPBTN_HOVER
启用热追踪。
当鼠标光标位于按钮上的时候,按钮会呈现为其聚焦状态。
BMPBTN_AUTO3D_SINGLE, BMPBTN_AUTO3D_DOUBLE
自动围绕图像生成一个三维边框,以及拥有焦点时的聚焦矩形。
另外,如果你没有提供按下状态的图像,则也会为你生成一个。
BMPBTN_AUTO3D_DOUBLE 生成一个略为厚实一点的边框。
BMPBTN_AUTOSIZE
使按钮的大小自动匹配图像的大小。
此风格是缺省风格。
BMPBTN_SHAREIMAGELISTS
如果设置了此风格,按钮对象不会销毁用以容纳按钮图像的图像列表。
如果没有设置,则图像列表会在 CBitmapButton 的析构函数中销毁。
BMPBTN_AUTOFIRE
如果设置了此风格,点击按钮并保持鼠标键为按下状态会连续产生 WM_COMMAND 消息。
当调用 SetBitmapButtonExtendedStyle() 时,dwMask 参数控制了受影响的风格,缺省值0会使新的风格完全取代旧的风格。
HIMAGELISTGetImageList()
HIMAGELISTSetImageList(HIMAGELISThImageList)
调用 SetImageList() 和 GetImageList() 可以把图像列表关联到按钮上,或者取得当前关联于按钮的图像列表。
intGetToolTipTextLength()
boolGetToolTipText(LPTSTRlpstrText,intnLength)
boolSetToolTipText(LPCTSTRlpstrText)
CBitmapButton 支持当鼠标悬停在按钮上时显示一个工具提示。
调用 SetImageList() 和 GetToolTipText() 来指定或者获取工具提示上显示的文字。
voidSetImages(intnNormal,intnPushed=-1,
intnFocusOrHover=-1,intnDisabled=-1)
调用 SetImages() 来告诉按钮图像列表中的哪个图像用于某种按钮状态。
所有参数都是基于0的图像列表中的索引值。
nNormal 是必需的,其他的都是可选的。
传递-1表示对于该状态没有图像。
CCheckListViewCtrl
CCheckListViewCtrl,定义于atlctrlx.h中,是 CWindowImpl 的一个派生类,它实现了带有复选框的列表视图控件。
此类不同于MFC的CCheckListBox,后者使用的是列表框而不是列表视图。
CCheckListViewCtrl 相当简单,因为它只添加了少许属于自己的功能。
不过,它还引入了一个新的辅助类,名为 CCheckListViewCtrlImplTraits,该类与 CWinTraits 相似但有第三个模板参数,该参数表示控件要使用的扩展列表视图风格。
如果你没有定义自己的 CCheckListViewCtrlImplTraits 组合,那么该类将会使用这些缺省值:
LVS_EX_CHECKBOXES|LVS_EX_FULLROWSELECT。
下面是一个特点(traits)定义的示例,使用了不同的扩展视图列表风格,以及一个使用这些特点的新类。
(注意,必须在扩展列表视图风格中包括 LVS_EX_CHECKBOXES,否则会得到一个断言失败的消息)。
typedefCCheckListViewCtrlImplTraits<
WS_CHILD|WS_VISIBLE|LVS_REPORT,
WS_EX_CLIENTEDGE,
LVS_EX_CHECKBOXES|LVS_EX_GRIDLINES|LVS_EX_UNDERLINEHOT|
LVS_EX_ONECLICKACTIVATE>CMyCheckListTraits;
classCMyCheckListCtrl:
publicCCheckListViewCtrlImpl CMyCheckListTraits> { private: typedefCCheckListViewCtrlImpl CMyCheckListTraits>baseClass; public: BEGIN_MSG_MAP(CMyCheckListCtrl) CHAIN_MSG_MAP(baseClass) END_MSG_MAP() }; CCheckListViewCtrl方法 BOOLSubclassWindow(HWNDhWnd) 当你子类化一个现有的列表视图控件时,SubclassWindow() 查看相关的 CCheckListViewCtrlImplTraits 中的扩展列表视图风格,并将之应用到控件上。 前两个模板参数(窗口风格以及扩展窗口风格)并未使用。 BOOLGetCheckState(intnIndex) BOOLSetCheckState(intnItem,BOOLbCheck) 这两个方法实际上在 CListViewCtrl 中。 SetCheckState() 接受一个条目索引以及指示是否勾选该条目的布尔值。 GetCheckState() 只接受一个索引,而后返回该条目的当前勾选状态。 voidCheckSelectedItems(intnCurrItem) 此方法接受一个条目索引。 它切换该条目的勾选(Check)状态,该条目必须已经被选定(Select),并且同时改变其他所有已选定条目的选中状态。 你自己通常不会使用此方法,因为 CCheckListViewCtrl 在复选框被点击或者用户按下空格键时会处理条目的选中事宜。 下面是 CCheckListViewCtrl 在ControlMania2中的样子: CTreeViewCtrlEx和CTreeItem 通过封装 HTREEITEM,这两个类使得使用树控件的功能更为方便。 一个 CTreeItem 对象中保存了一个 HTREEITEM 和一个指向包含此项的树控件的指针,这样你就只需使用 CTreeItem 来操纵该项而不必每次都引用树控件。 CTreeViewCtrlEx 和 CTreeViewCtrl 相像,但它的方法是处理CTreeItem 而非 HTREEITEM。 因此,假如你调用 InsertItem(),则它会返回一个 CTreeItem 而不是 HTREEITEM,然后你就可以使用 CTreeItem 操控此新插入的项。 下面是一个例子: //UsingplainHTREEITEMs: HTREEITEMhti,hti2; hti=m_wndTree.InsertItem("foo",TVI_ROOT,TVI_LAST); hti2=m_wndTree.InsertItem("bar",hti,TVI_LAST); m_wndTree.SetIt
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- ATLWTL 第五 部分
