📄 wtl for mfc programmers, part ix - gdi classes, common dialogs, and utility classes - wtl.htm
字号:
<html>
<head>
<title>WTL for MFC Programmers, Part III - Toolbars and Status Bars</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body bgcolor="#33CCCC" text="#000000">
<p align="center"><b><font style="FONT-SIZE: 16pt" size="4" color="#0033CC">WTL
for MFC Programmers, Part IX - GDI Classes, Common Dialogs, and Utility Classes</font></b><br>
<br>
</p>
<p align="left">原作 :<b><font color="#CC3366">Michael Dunn</font></b> [<a href="http://www.codeproject.com/wtl/wtl4mfc9.asp">英文原文</a>]<br>
翻译 :<a href="mailto:inte2000@163.com">Orbit(桔皮干了)</a> [<a href="http://www.winmsg.com/cn/orbit.htm">http://www.winmsg.com/cn/orbit.htm</a>]</p>
<p align="left"><a href="demo/WTL4MFC9_demo.zip">下载演示程序代码</a></p>
<font face="Arial, Helvetica, sans-serif"></font>
<H2><font color="#FFFF66">本章内容</font></H2>
<UL>
<LI><A
href="#intro">介绍</A>
<LI><A
href="#gdiwrappers">GDI 封装类(wrapper
classes)</A>
<UL>
<LI><A
href="#wrapperscommon">封装类的通用函数(Common
functions)</A>
<LI><A
href="#usingcdct">使用 CDCT</A>
<LI><A
href="#mfcdifferences">与MFC封装类的不同之处</A>
</LI>
</UL>
<LI><A
href="#resourceloading">资源装载(Resource-Loading)函数</A>
<LI><A
href="#usingcommdlg">使用公共对话框</A>
<UL>
<LI><A
href="#usingcfiledialog">CFileDialog</A>
<LI><A
href="#usingcfolderdialog">CFolderDialog</A>
</LI>
</UL>
<LI><A
href="#miscstuff">其它有用的类和全局函数</A>
<UL>
<LI><A
href="#structwrappers">结构封装</A>
<LI><A
href="#dualtypedargs">双类型参数的自适应类</A>
<LI><A
href="#otherclasses">其它工具类</A>
<LI><A
href="#globalfuncs">全局函数</A>
<LI><A
href="#macros">宏</A> </LI>
</UL>
<LI><A href="#samplecode">例子项目</A>
<LI><A
href="#copying">版权和许可</A>
<LI><A
href="#revisionhistory">修订历史</A> </LI>
</UL>
<br>
<H2><A name=intro></A><font color="#FFFF66">对第九部分的介绍</font></H2>
<P>WTL还有很多封装类和工具类在本系列文章前八篇中并没有介绍,例如CString和CDC,WTL还提供了对GDI对象的良好封装,还包括一些有用的资源装载函数以及WIN32通用对话框的封装类,使得通用对话框更加容易使用。现在,在本文的第九篇中,我将介绍一些最常用的工具类。</P>
<P>本文将讨论以下内容:</P>
<OL>
<LI>GDI 封装类
<LI>资源装载(Resource-loading)函数
<LI>使用打开文件和选择文件夹的通用对话框
<LI>其它有用的类和全局函数</LI>
</OL>
<H2><A name=gdiwrappers></A><font color="#FFFF66">GDI 封装类</font></H2>
<P>WTL 使用了与MFC截然不同方式封装GDI对象,WTL的方法是为每种GDI对象设计一个模板类,每个模板都有一个名为t_bManaged的bool类型模板参数,这个参数控制着这些类是否“管理”(或拥有)它所封装的GDI对象。如果t_bManaged是false,表示这个C++对象并不管理GDI对象的生命周期,它只是围绕着这个GDI对象句柄的简单封装;如果t_bManaged是true,就产生了两点不同之处:</P>
<OL>
<LI><CODE>如果封装的句柄不为NULL,析构会调用DeleteObject()函数释放资源</CODE><code>。</code>
<LI><CODE>如果封装的句柄不为NULL,Attach()</CODE> 会在捆绑其它新句柄之前调用<CODE>DeleteObject()释放当前封装的句柄</CODE></LI>
</OL>
<P>这种设计与ATL窗口类的封装风格是一致的,ATL的CWindow就是对HWND句柄的一个简单的封装类,而CWindowImpl则负责管理窗口的整个生命周期。</P>
<P>GDI的封装类都定义在<I><font color="#0000FF">atlgdi.h</font></I>中(译者注:注意是“定义在”而不是通常说的“声明在”头文件中,这是因为ATL/WTL使用的是包含编译模式,所有的代码都在头文件中),
只有<CODE>CMenuT</CODE>例外 ,它定义在<font color="#0000CC"><I>atluser.h</I></font>中。你不需要直接包含这些头文件,因为它们已经包含在<I>atlapp.h</I>
中了。每种模板类都由很容易记忆的名字:</P>
<P>
<TABLE width="100%" border=1>
<TBODY>
<TR>
<TH width="25%"> <P>封装 GDI 对象</P></TH>
<TH width="25%"> <P>模板类</P></TH>
<TH width="25%"> <P>可管理对象封装</P></TH>
<TH width="25%"> <P>简单的句柄封装</P></TH>
</TR>
<TR>
<TD width="25%"> <P>Pen </P></TD>
<TD width="25%"> <P><CODE>CPenT</CODE> </P></TD>
<TD width="25%"> <P><CODE>CPen</CODE> </P></TD>
<TD width="25%"> <P><CODE>CPenHandle</CODE> </P></TD>
</TR>
<TR>
<TD width="25%"> <P>Brush </P></TD>
<TD width="25%"> <P><CODE>CBrushT</CODE> </P></TD>
<TD width="25%"> <P><CODE>CBrush</CODE> </P></TD>
<TD width="25%"> <P><CODE>CBrushHandle</CODE> </P></TD>
</TR>
<TR>
<TD width="25%"> <P>Font </P></TD>
<TD width="25%"> <P><CODE>CFontT</CODE> </P></TD>
<TD width="25%"> <P><CODE>CFont</CODE> </P></TD>
<TD width="25%"> <P><CODE>CFontHandle</CODE> </P></TD>
</TR>
<TR>
<TD width="25%"> <P>Bitmap </P></TD>
<TD width="25%"> <P><CODE>CBitmapT</CODE> </P></TD>
<TD width="25%"> <P><CODE>CBitmap</CODE> </P></TD>
<TD width="25%"> <P><CODE>CBitmapHandle</CODE> </P></TD>
</TR>
<TR>
<TD width="25%"> <P>Palette </P></TD>
<TD width="25%"> <P><CODE>CPaletteT</CODE> </P></TD>
<TD width="25%"> <P><CODE>CPalette</CODE> </P></TD>
<TD width="25%"> <P><CODE>CPaletteHandle</CODE> </P></TD>
</TR>
<TR>
<TD width="25%"> <P>Region </P></TD>
<TD width="25%"> <P><CODE>CRgnT</CODE> </P></TD>
<TD width="25%"> <P><CODE>CRgn</CODE> </P></TD>
<TD width="25%"> <P><CODE>CRgnHandle</CODE> </P></TD>
</TR>
<TR>
<TD width="25%"> <P>Device context </P></TD>
<TD width="25%"> <P><CODE>CDCT</CODE> </P></TD>
<TD width="25%"> <P><CODE>CDC</CODE> </P></TD>
<TD width="25%"> <P><CODE>CDCHandle</CODE> </P></TD>
</TR>
<TR>
<TD width="25%"> <P>Menu </P></TD>
<TD width="25%"> <P><CODE>CMenuT</CODE> </P></TD>
<TD width="25%"> <P><CODE>CMenu</CODE> </P></TD>
<TD width="25%"> <P><CODE>CMenuHandle</CODE> </P></TD>
</TR>
</TBODY>
</TABLE>
<p></P>
<P>相对于MFC围绕着对象指针封装的方法,我更喜欢WTL的方法:你不用总是担心得到一个NULL指针(封装的句柄也可能是NULL,但这是另一回事), 也不用总是注意操作临时对象这种特殊情况(译者注:MFC有一个回收机制,总是试图释放使用FromHandle得到的临时对象),还有一点就是使用这种形式封装的类实例只占用很少的资源,因为类只有一个成员变量,就是其封装的句柄。象这种类似CWindow的封装形式,你可以在不同的线程之间传递封装类对象而不用担心线程安全问题,正是因为这样,WTL也不需要象MFC那样的线程特殊性映射。</P>
<P>下面的设备上下文封装类用于一些特殊的绘制场景:</P>
<UL>
<LI><CODE>CClientDC</CODE>: 封装了GetDC()和ReleaseDC(),用于Windows客户区的绘制<code>。</code>
<LI><CODE>CWindowDC</CODE>: <CODE>封装了GetWindowDC()</CODE> <CODE>和ReleaseDC(),用于在整个Windows上下文中绘制。</CODE>
<LI><CODE>CPaintDC</CODE>: <CODE>封装了BeginPaint()</CODE> <CODE>和 EndPaint()</CODE>,用户<CODE>WM_PAINT</CODE>
消息响应函数。 </LI>
</UL>
<P>每个类的构造函数都有一个HWND(窗口句柄)参数,类的行为和MFC的同名类相似。三个类都是CDC的派生类,它们自己管理设备上下文。</P>
<H2><A name=wrapperscommon></A><font color="#FFFF66">封装类的通用函数</font></H2>
<P>所有的GDI封装类使用的是相同的设计理念,所以它们的使用方法都大同小异,为了简单起见,这里只介绍一下CBitmapT类。</P>
<DL>
<DT>封装 GDI 对象句柄
<DD>每个类都由一个公有成员变量,也就是C++对象封装的GDI对象句柄,CBitmapT有一个HBITMAP类型成员,名为<CODE>m_hBitmap。</CODE>
<DT>构造函数
<DD>构造函数有一个HBITMAP类型的参数,默认值是NULL,<CODE>m_hBitmap</CODE>将被初始化位这个值。
<DT>析构函数
<DD>如果<CODE>t_bManaged</CODE> 是true,并且<CODE>m_hBitmap</CODE>不是NULL<CODE>,那么析构函数会调用DeleteObject()释放这个bitmap。</CODE>
<DT><CODE>Attach()</CODE> <CODE>和 <SPAN
class=cpp-keyword>operator</SPAN> =</CODE>
<DD>这两个函数都有一个HBITMAP类型的参数,<CODE>如果t_bManaged是true,并且</CODE><CODE>m_hBitmap</CODE>不为NULL,它们会调用<CODE>DeleteObject()释放这个</CODE><CODE>CBitmapT</CODE>对象管理的bitmap,然后将<CODE>m_hBitmap</CODE>的值设为作为参数传递进来的那个HBITMAP。
<DT><CODE>Detach()</CODE>
<DD><CODE>Detach()</CODE> <CODE>将m_hBitmap的值设为</CODE>NULL,然后返回<CODE>m_hBitmap的值,</CODE><CODE>Detach()</CODE>调用以后,<CODE>CBitmapT</CODE>对象就不再关联GDI
bitmap了。
<DT>创建 GDI <code>对象的函数</code>
<DD><CODE>CBitmapT</CODE> 封装了几个<CODE>用来创建位图的WIN32 API :LoadBitmap(),</CODE><CODE>LoadMappedBitmap()、</CODE><CODE>CreateBitmap()、</CODE><CODE>CreateBitmapIndirect()、</CODE><CODE>CreateCompatibleBitmap()、</CODE><CODE>CreateDiscardableBitmap()、</CODE><CODE>CreateDIBitmap()、</CODE>
<code>、</code> <CODE>CreateDIBSection()。</CODE> 这些方法<CODE>将保证m_hBitmap</CODE>不是NULL然后返回一个,<code>如果要将这个</code><CODE>CBitmapT对象用于其它GDI
bitmap,需要首先调用</CODE><CODE>DeleteObject()</CODE> <CODE>或Detach()</CODE> 函数。
<DT><CODE>DeleteObject()</CODE>
<DD><CODE>DeleteObject()</CODE> 销毁GDI bitmap对象,将<CODE>m_hBitmap</CODE>设为NULL,调用这个函数时<CODE>m_hBitmap</CODE>应该不为NULL,否则将会出现断言错误。
<DT><CODE>IsNull()</CODE>
<DD><CODE>如果m_hBitmap不为NULL,IsNull()</CODE> 返回true,否则返回false。使用这个函数可以测试CBitmapT对象是否关联了一个GDI
bitmap。
<DT><CODE><SPAN class=cpp-keyword>operator</SPAN> HBITMAP</CODE>
<DD>这个转换操作符返回<CODE>m_hBitmap,这样你就可以将</CODE><CODE>CBitmapT</CODE> 对象传递给那些使用HBIMAP句柄作为参数的函数或Win32
API。 这个转换操作符还可用在测试这个对象合法性的布尔表达式中,其值与<CODE>IsNull()刚好相反。例如</CODE>,下面两个if语句时等价的:
<DD> <font color="#0000FF">
<PRE>CBitmapHandle bmp = <SPAN class=cpp-comment>/* some HBITMAP value */</SPAN>;
<SPAN class=cpp-keyword>if</SPAN> ( !bmp.IsNull() ) { <SPAN class=cpp-keyword>do</SPAN> something... }
<SPAN class=cpp-keyword>if</SPAN> ( bmp ) { <SPAN class=cpp-keyword>do</SPAN> something more... }</PRE>
<DT><CODE></CODE> </font>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -