?? wtl for mfc programmers, part v - advanced dialog ui classes - wtl.htm
字號:
<html>
<head>
<title>WTL for MFC Programmers, Part V - Advanced Dialog UI Classes</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 V - Advanced Dialog UI Classes</font></b><br>
<br>
</p>
<p align="left">原作 :<b><font color="#CC3366">Michael Dunn</font></b> [<a href="http://www.codeproject.com/wtl/WTL4MFC5.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/WTL4MFC5_demo.zip">下載演示程序代碼</a></p>
<H2><font color="#FFFF66">本章內(nèi)容</font></H2>
<UL>
<LI><A href="#intro">第五章介紹</A>
<LI><A href="#specialized">特別的自畫和外觀定制類</A>
<UL>
<LI><A href="#cownerdraw">COwnerDraw</A>
<LI><A href="#ccustomdraw">CCustomDraw</A></LI>
</UL>
<LI><A href="#newwtlctrls">WTL的新控件</A>
<UL>
<LI><A href="#newcbitmapbutton">CBitmapButton</A>
<LI><A href="#cchecklist">CCheckListViewCtrl</A>
<LI><A href="#treeex">CTreeViewCtrlEx 和 CTreeItem</A>
<LI><A href="#chyperlink">CHyperLink</A></LI>
</UL>
<LI><A href="#uiupdctrl">對話框中控件的UI Updating</A>
<LI><A href="#ddv">DDV</A>
<UL>
<LI><A href="#ddvfail">處理DDV驗證失敗</A> </LI>
</UL>
<LI><A href="#resizing">改變對話框的大小</A>
<LI><A href="#upnext">繼續(xù)</A>
<LI><A href="#references">參考</A>
<LI><A href="#revisionhistory">修改記錄</A> </LI>
</UL>
<H2><A name=intro></A><font color="#FFFF66">第五章介紹</font></H2>
<p>在上一篇文章我們介紹了一些與對話框和控件有關(guān)的WTL的特性,它們和MFC的相應(yīng)的類作用相同。本文將介紹一些新類實現(xiàn)高級界面特性新類:控件自畫和自定外觀控件,新的WTL控件,UI
updating和對話框數(shù)據(jù)驗證(DDV)。</p>
<H2><A name=specialized></A><font color="#FFFF66">特別的自畫和外觀定制類</font></H2>
<P>由于自畫和定制外觀控件在圖形用戶界面中是很常用的手段,所以WTL提供了幾個嵌入類來完成這些令人厭煩的工作。我接著就會介紹它們,事實上我們在上一個例子工程ControlMania2的結(jié)尾部分已經(jīng)這么做了。如果你正隨著我的講解用應(yīng)用程序生成向?qū)?chuàng)建新工程,請不要忘了使用無模式對話框,為了使正常工作必須使用無模式對話框,我會在<a href="#uiupdctrl">對話框中控件的UI
Updating</a>部分詳細解釋為什么這樣作。</P>
<H3><A name=cownerdraw></A><font color="#FFFF66">COwnerDraw</font></H3>
<P>控件的自畫需要響應(yīng)四個消息:WM_MEASUREITEM, WM_DRAWITEM, WM_COMPAREITEM, 和WM_DELETEITEM,在atlframe.h頭文件中定義的COwnerDraw類可以簡化這些工作,使用這個類就不需要處理這四個消息,你只需將消息鏈入COwnerDraw,它會調(diào)用你的類中的重載函數(shù)。</P>
<p>如何將消息鏈入COwnerDraw取決與你是否將消息反射給控件,兩種方法有些不同。下面是COwnerDraw類的消息映射鏈,它使得兩種方法的差別更加明顯:</p>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">template</font></SPAN> <font color="#0033FF"><<SPAN class=cpp-keyword>class</SPAN> T> <SPAN class=cpp-keyword>class</SPAN> COwnerDraw
{
<SPAN class=cpp-keyword>public</SPAN>:
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(<SPAN class=cpp-literal>1</SPAN>)
MESSAGE_HANDLER(OCM_DRAWITEM, OnDrawItem)
MESSAGE_HANDLER(OCM_MEASUREITEM, OnMeasureItem)
MESSAGE_HANDLER(OCM_COMPAREITEM, OnCompareItem)
MESSAGE_HANDLER(OCM_DELETEITEM, OnDeleteItem)
END_MSG_MAP()
};</font></PRE>
<P>注意,消息映射鏈的主要部分處理WM_*消息,而ATL部分處理反射的消息,OCM_*。自畫的通知消息就像WM_NOTIFY消息一樣,你可以在父窗口處理它們,也可以將它們反射會控件,如果你使用前一種方法,消息被直接鏈入COwnerDraw:</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">class</font></SPAN><font color="#0033FF"> CSomeDlg : <SPAN class=cpp-keyword>public</SPAN> COwnerDraw<CSomeDlg>, ...
{
BEGIN_MSG_MAP(CSomeDlg)
<SPAN class=cpp-comment>//...</SPAN>
CHAIN_MSG_MAP(COwnerDraw<CSomeDlg>)
END_MSG_MAP()
<SPAN class=cpp-keyword>void</SPAN> DrawItem ( LPDRAWITEMSTRUCT lpdis );
};</font></PRE>
<P>當然,如果你想要控件自己處理這些消息,你需要使用CHAIN_MSG_MAP_ALT宏將消息鏈入ALT_MSG_MAP(1)部分:</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">class</font></SPAN><font color="#0033FF"> CSomeButtonImpl : <SPAN class=cpp-keyword>public</SPAN> COwnerDraw<CSomeButtonImpl>, ...
{
BEGIN_MSG_MAP(CSomeButtonImpl)
<SPAN class=cpp-comment>//...</SPAN>
CHAIN_MSG_MAP_ALT(COwnerDraw<CSomeButtonImpl>, <SPAN class=cpp-literal>1</SPAN>)
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
<SPAN class=cpp-keyword>void</SPAN> DrawItem ( LPDRAWITEMSTRUCT lpdis );
};</font></PRE>
<P>COwnerDraw類將對消息傳遞的參數(shù)展開,然后調(diào)用你的類中的實現(xiàn)函數(shù)。上面的例子中,我們自己的類實現(xiàn)DrawItem()函數(shù),當有WM_DRAWITEM或OCM_DRAWITEM消息被鏈入COwnerDraw時,這個函數(shù)就會被調(diào)用。你可以重載的方法有:</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">void</font></SPAN><font color="#0033FF"> DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
<SPAN class=cpp-keyword>void</SPAN> MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
<SPAN class=cpp-keyword>int</SPAN> CompareItem(LPCOMPAREITEMSTRUCT lpCompareItemStruct);
<SPAN class=cpp-keyword>void</SPAN> DeleteItem(LPDELETEITEMSTRUCT lpDeleteItemStruct);</font></PRE>
<P>如果你不想處理某個消息,你可以調(diào)用SetMsgHandled(false),消息會被傳遞給消息映射鏈中的其他響應(yīng)者。SetMsgHandled()事實上是COwnerDraw類的成員函數(shù),但是它的作用和在BEGIN_MSG_MAP_EX()中使用SetMsgHandled()一樣。</P>
<p>對于ControlMania2,它從ControlMania1中的樹控件開始,添加了自畫按鈕處理反射的WM_DRAWITEM消息,下面是資源編輯器中的新按鈕:</p>
<P><IMG height=346 alt=" [Owner-drawn button 1 - 7K] "
src="images/cm2_od15.png"
width=325 align=bottom border=0></P>
<P>現(xiàn)在我們需要一個新類實現(xiàn)自畫按鈕:</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">class</font></SPAN><font color="#0033FF"> CODButtonImpl : <SPAN class=cpp-keyword>public</SPAN> CWindowImpl<CODButtonImpl, CButton>,
<SPAN class=cpp-keyword>public</SPAN> COwnerDraw<CODButtonImpl>
{
<SPAN class=cpp-keyword>public</SPAN>:
BEGIN_MSG_MAP_EX(CODButtonImpl)
CHAIN_MSG_MAP_ALT(COwnerDraw<CODButtonImpl>, <SPAN class=cpp-literal>1</SPAN>)
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
<SPAN class=cpp-keyword>void</SPAN> DrawItem ( LPDRAWITEMSTRUCT lpdis );
};</font></PRE>
<P>DrawItem()使用了像BitBlt()這樣的GDI函數(shù)向按鈕的表面畫位圖,代碼應(yīng)該很容易理解,因為WTL使用的類名和函數(shù)名都和MFC類似。</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">void</font></SPAN><font color="#0033FF"> CODButtonImpl::DrawItem ( LPDRAWITEMSTRUCT lpdis )
{
<SPAN class=cpp-comment>// NOTE: m_bmp is a CBitmap init'ed in the constructor.</SPAN>
CDCHandle dc = lpdis->hDC;
CDC dcMem;
dcMem.CreateCompatibleDC ( dc );
dc.SaveDC();
dcMem.SaveDC();
<SPAN class=cpp-comment>// Draw the button's background, red if it has the focus, blue if not.</SPAN>
<SPAN class=cpp-keyword>if</SPAN> ( lpdis->itemState & ODS_FOCUS )
dc.FillSolidRect ( &lpdis->rcItem, RGB(<SPAN class=cpp-literal>255</SPAN>,<SPAN class=cpp-literal>0</SPAN>,<SPAN class=cpp-literal>0</SPAN>) );
<SPAN class=cpp-keyword>else</SPAN>
dc.FillSolidRect ( &lpdis->rcItem, RGB(<SPAN class=cpp-literal>0</SPAN>,<SPAN class=cpp-literal>0</SPAN>,<SPAN class=cpp-literal>255</SPAN>) );
<SPAN class=cpp-comment>// Draw the bitmap in the top-left, or offset by 1 pixel if the button</SPAN>
<SPAN class=cpp-comment>// is clicked.</SPAN>
dcMem.SelectBitmap ( m_bmp );
<SPAN class=cpp-keyword>if</SPAN> ( lpdis->itemState & ODS_SELECTED )
dc.BitBlt ( <SPAN class=cpp-literal>1</SPAN>, <SPAN class=cpp-literal>1</SPAN>, <SPAN class=cpp-literal>80</SPAN>, <SPAN class=cpp-literal>80</SPAN>, dcMem, <SPAN class=cpp-literal>0</SPAN>, <SPAN class=cpp-literal>0</SPAN>, SRCCOPY );
<SPAN class=cpp-keyword>else</SPAN>
dc.BitBlt ( <SPAN class=cpp-literal>0</SPAN>, <SPAN class=cpp-literal>0</SPAN>, <SPAN class=cpp-literal>80</SPAN>, <SPAN class=cpp-literal>80</SPAN>, dcMem, <SPAN class=cpp-literal>0</SPAN>, <SPAN class=cpp-literal>0</SPAN>, SRCCOPY );
dcMem.RestoreDC(-<SPAN class=cpp-literal>1</SPAN>);
dc.RestoreDC(-<SPAN class=cpp-literal>1</SPAN>);
}</font></PRE>
<P>我們的按鈕看起來是這個樣子:</P>
<P><IMG height=324 alt=" [Owner-drawn button - 11K] "
src="images/cm2_od25.png"
width=305 align=bottom border=0></P>
<H3><A name=ccustomdraw></A><font color="#FFFF66">CCustomDraw</font></H3>
<P>CCustomDraw類使用和COwnerDraw類相同的方法處理NM_CUSTOMDRAW消息,對于自定繪制的每個階段都有相應(yīng)的重載函數(shù):</P>
<PRE><font color="#0033FF">DWORD OnPrePaint(<SPAN class=cpp-keyword>int</SPAN> idCtrl, LPNMCUSTOMDRAW lpNMCD);
DWORD OnPostPaint(<SPAN class=cpp-keyword>int</SPAN> idCtrl, LPNMCUSTOMDRAW lpNMCD);
DWORD OnPreErase(<SPAN class=cpp-keyword>int</SPAN> idCtrl, LPNMCUSTOMDRAW lpNMCD);
DWORD OnPostErase(<SPAN class=cpp-keyword>int</SPAN> idCtrl, LPNMCUSTOMDRAW lpNMCD);
DWORD OnItemPrePaint(<SPAN class=cpp-keyword>int</SPAN> idCtrl, LPNMCUSTOMDRAW lpNMCD);
DWORD OnItemPostPaint(<SPAN class=cpp-keyword>int</SPAN> idCtrl, LPNMCUSTOMDRAW lpNMCD);
DWORD OnItemPreErase(<SPAN class=cpp-keyword>int</SPAN> idCtrl, LPNMCUSTOMDRAW lpNMCD);
DWORD OnItemPostEraset(<SPAN class=cpp-keyword>int</SPAN> idCtrl, LPNMCUSTOMDRAW lpNMCD);
DWORD OnSubItemPrePaint(<SPAN class=cpp-keyword>int</SPAN> idCtrl, LPNMCUSTOMDRAW lpNMCD);</font></PRE>
<P>這些函數(shù)默認都是返回CDRF_DODEFAULT,如果想自畫控件或返回一個不同的值,就需要重載這些函數(shù):</P>
<p>你可能注意到上面的屏幕截圖將“道恩”(Dawn:女名)顯示成綠色,這是因為CBuffyTreeCtrl將消息鏈入CCustomDraw并重載了OnPrePaint()和OnItemPrePaint()方法。向樹控件中添加節(jié)點時,節(jié)點的item
data字段被設(shè)置成1,OnItemPrePaint()檢查這個值,然后改變文字的顏色。</p>
<PRE><font color="#0033FF">DWORD CBuffyTreeCtrl::OnPrePaint(<SPAN class=cpp-keyword>int</SPAN> idCtrl,
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -