?? wtl for mfc programmers, part vii - splitter windows - wtl.htm
字號:
<html>
<head>
<title>WTL for MFC Programmers, Part VII - Splitter Windows</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 VII - Splitter Windows</font></b><br>
<br>
</p>
<p align="left">原作 :<b><font color="#CC3366">Michael Dunn</font></b> [<a href="http://www.codeproject.com/wtl/WTL4MFC7.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/WTL4MFC7_demo.zip">下載演示程序代碼</a></p>
<H2><font color="#FFFF66">本章內(nèi)容</font></H2>
<UL>
<LI><A href="#intro">介紹</A>
<LI><A href="#wtlsplitters">WTL 的分隔窗口</A>
<UL>
<LI><A href="#splitclasses">相關(guān)的類</A>
<LI><A href="#creatingsplitter">創(chuàng)建分隔窗口</A>
<LI><A href="#splittermethods">基本的方法</A>
<LI><A href="#splitdata">數(shù)據(jù)成員</A> </LI></UL>
<LI><A href="#startsample">開始一個例子工程</A>
<LI><A href="#creatingpanes">創(chuàng)建一個窗格內(nèi)的窗口</A>
<LI><A href="#messagerouting">消息處理</A>
<LI><A href="#panecont">窗格容器</A>
<UL>
<LI><A href="#pcclasses">相關(guān)的類</A>
<LI><A href="#pcmethods">基本方法</A>
<LI><A href="#pcinsplitter">在分隔窗口中使用窗格容器</A>
<LI><A href="#pcclosebtn">關(guān)閉按鈕和消息處理</A> </LI></UL>
<LI><A href="#advancedsplitter">高級功能</A>
<UL>
<LI><A href="#nested">嵌套的分隔窗口</A>
<LI><A href="#axinpane">在窗格中使用ActiveX控件</A>
<LI><A href="#specialdraw">特殊繪制</A> </LI></UL>
<LI><A href="#pcoverrides">窗格容器內(nèi)的特殊繪制</A>
<LI><A href="#bonus">在狀態(tài)欄顯示進度條</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>隨著使用兩個分隔的視圖管理文件系統(tǒng)的資源管理器在Windows 95中第一次出現(xiàn),分隔窗口逐漸成為一種流行的界面元素。MFC也有一個復(fù)雜的功能強大的分隔窗口類,但是要掌握它的用法確實有點難,并且它和文檔/視圖框架聯(lián)系緊密。在第七章我將介紹WTL的分隔窗口,它比MFC的分隔窗口要簡單一些。WTL的分隔窗口沒有MFC那么多特性,但是易于使用和擴展。</P>
<p>本章的例子工程是用WTL重寫的<a href="http://www.codeproject.com/clipboard/clipspy.asp">ClipSpy</a>,如果你對這個程序不太熟悉,現(xiàn)在可以快速瀏覽一下本章內(nèi)容,因為我只是復(fù)制了ClipSpy的功能而沒用深入的解釋它是如何工作的,畢竟這篇文章的重點是分隔窗口,不是剪貼板。</p>
<H2><A name=wtlsplitters></A><font color="#FFFF66">WTL 的分隔窗口</font></H2>
<P>頭文件atlsplit.h含有所有WTL的分隔窗口類,一共有三個類:CSplitterImpl,CSplitterWindowImpl和CSplitterWindowT,不過你通常只會用到其中的一個。下面將介紹這些類和它們的基本方法。</P>
<H3><A name=splitclasses></A><font color="#FFFF66">相關(guān)的類</font></H3>
<P>CSplitterImpl是一個有兩個參數(shù)的模板類,一個是窗口界面類的類名,另一個是布爾型變量表示分隔窗口的方向:true表示垂直方向,false表示水平方向。CSplitterImpl類包含了幾乎所有分隔窗口的實現(xiàn)代碼,它的許多方法是可重載的,重載這些方法可以自己繪制分隔條的外觀或者實現(xiàn)其它的效果。CSplitterWindowImpl類是從CWindowImpl和CSplitterImpl兩個類派生出來的,但是它的代碼不多,有一個空的WM_ERASEBKGND消息處理函數(shù)和一個WM_SIZE處理函數(shù)用于重新定位分隔窗口。</P>
<p>最后一個是CSplitterWindowT類,它從CSplitterImpl類派生,它的窗口類名是“WTL_SplitterWindow”。還有兩個自定義數(shù)據(jù)類型通常用來取代上面的三個類:CSplitterWindow用于垂直分隔窗口,CHorSplitterWindow用于水平分隔窗口。</p>
<H3><A name=creatingsplitter></A><font color="#FFFF66">創(chuàng)建分割窗口</font></H3>
<P>由于CSplitterWindow是從CWindowImpl類派生的,所以你可以像創(chuàng)建其他子窗口那樣創(chuàng)建分隔窗口。分隔窗口將存在于整個主框架窗口的生命周期,應(yīng)該在CMainFrame類添加一個CSplitterWindow類型的變量。在CMainFrame::OnCreate()函數(shù)內(nèi),你可以將分隔窗口作為主窗口的子窗口創(chuàng)建,然后將其設(shè)置為主窗口的客戶區(qū)窗口:</P>
<PRE><font color="#0033FF">LRESULT CMainFrame::OnCreate ( LPCREATESTRUCT lpcs )
{
<SPAN class=cpp-comment>// ...</SPAN>
<SPAN class=cpp-keyword>const</SPAN> DWORD dwSplitStyle = WS_CHILD | WS_VISIBLE |
WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
dwSplitExStyle = WS_EX_CLIENTEDGE;
m_wndSplit.Create ( *<SPAN class=cpp-keyword>this</SPAN>, rcDefault, NULL,
dwSplitStyle, dwSplitExStyle );
m_hWndClient = m_wndSplit;
}</font></PRE>
<P>創(chuàng)建分隔窗口之后,你就可以為每個窗格指定窗口或者做其他必要的初始化工作。</P>
<H3><A name=splittermethods></A><font color="#FFFF66">基本方法</font></H3>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">bool</font></SPAN><font color="#0033FF"> SetSplitterPos(<SPAN class=cpp-keyword>int</SPAN> xyPos = -<SPAN class=cpp-literal>1</SPAN>, <SPAN class=cpp-keyword>bool</SPAN> bUpdate = <SPAN class=cpp-keyword>true</SPAN>)
<SPAN class=cpp-keyword>int</SPAN> GetSplitterPos()</font></PRE>
<P>可以調(diào)用SetSplitterPos()函數(shù)設(shè)置分隔條的位置,這個位置表示分割條距離分隔窗口的上邊界(水平分隔窗口)或左邊界(垂直分隔窗口)有多少個象素點。你可以使用默認值-1將分隔條設(shè)置到分隔窗口的中間,使兩個窗格大小相同,通常傳遞true給bUpdate參數(shù)表示在移動分隔條之后相應(yīng)的改變兩個窗格的大小。GetSplitterPos()返回當前分隔條的位置,這個位置也是相對于分隔窗口的上邊界或左邊界。</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">bool</font></SPAN><font color="#0033FF"> SetSinglePaneMode(<SPAN class=cpp-keyword>int</SPAN> nPane = SPLIT_PANE_NONE)
<SPAN class=cpp-keyword>int</SPAN> GetSinglePaneMode()</font></PRE>
<P>調(diào)用SetSinglePaneMode()函數(shù)可以改變分隔窗口的模式使單窗格模式還是雙窗格模式,在單窗格模式下,只有一個窗格使可見的并且隱藏了分隔條,這和MFC的動態(tài)分隔窗口相似(只是沒有那個小鉗子形狀的手柄,它用于重新分隔分隔窗口)。對于nPane參數(shù)可用的值是SPLIT_PANE_LEFT,SPLIT_PANE_RIGHT,SPLIT_PANE_TOP,SPLIT_PANE_BOTTOM,和SPLIT_PANE_NONE,前四個指示顯示那個窗格(例如,使用SPLIT_PANE_LEFT參數(shù)將顯示左邊的窗格,隱藏右邊的窗格),使用SPLIT_PANE_NONE表示兩個窗格都顯示。GetSinglePaneMode()返回五個SPLIT_PANE_*值中的一個表示當前的模式。</P>
<PRE><font color="#0033FF">DWORD SetSplitterExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = <SPAN class=cpp-literal>0</SPAN>)
DWORD GetSplitterExtendedStyle()</font></PRE>
<P>分隔窗口有自己的樣式用于控制當整個分隔窗口改變大小時如何移動分隔條。有以下幾種樣式:</P>
<UL>
<LI>SPLIT_PROPORTIONAL: 兩個窗格一起改變大小
<LI>SPLIT_RIGHTALIGNED: 右邊的窗格保持大小不變,只改變左邊的窗格大小
<LI>SPLIT_BOTTOMALIGNED: 下部的窗格保持大小不變,只改變上邊的窗格大小</LI>
</UL>
<P>如果既沒有指定SPLIT_PROPORTIONAL,也沒有指定SPLIT_RIGHTALIGNED/SPLIT_BOTTOMALIGNED,則分隔窗口會變成左對齊或上對齊。如果將SPLIT_PROPORTIONAL和SPLIT_RIGHTALIGNED/SPLIT_BOTTOMALIGNED一起使用,則優(yōu)先選用SPLIT_PROPORTIONAL樣式。</P>
<p>還有一個附加的樣式用來控制分隔條是否可以被用戶移動:</p>
<UL>
<LI>SPLIT_NONINTERACTIVE:分隔條不能被移動并且不相應(yīng)鼠標</LI>
</UL>
<P>擴展樣式的默認值是 SPLIT_PROPORTIONAL。</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">bool</font></SPAN><font color="#0033FF"> SetSplitterPane(<SPAN class=cpp-keyword>int</SPAN> nPane, HWND hWnd, <SPAN class=cpp-keyword>bool</SPAN> bUpdate = <SPAN class=cpp-keyword>true</SPAN>)
<SPAN class=cpp-keyword>void</SPAN> SetSplitterPanes(HWND hWndLeftTop, HWND hWndRightBottom, <SPAN class=cpp-keyword>bool</SPAN> bUpdate = <SPAN class=cpp-keyword>true</SPAN>)
HWND GetSplitterPane(<SPAN class=cpp-keyword>int</SPAN> nPane)</font></PRE>
<P>可以調(diào)用SetSplitterPane()為分隔窗口的窗格指派子窗口,nPane是一個SPLIT_PANE_*類型的值,表示設(shè)置拿一個窗格。hWnd是子窗口的窗口句柄。你可以使用SetSplitterPane()將一個子窗口同時指定給兩個窗格,對于bUpdate參數(shù)通常使用默認值,也就是告訴分隔窗口立即調(diào)整子窗口的大小以適應(yīng)窗格的大小。可以調(diào)用GetSplitterPane()得到某個窗格的子窗口句柄,如果窗格沒有指派子窗口則GetSplitterPane()返回NULL。</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">bool</font></SPAN><font color="#0033FF"> SetActivePane(<SPAN class=cpp-keyword>int</SPAN> nPane)
<SPAN class=cpp-keyword>int</SPAN> GetActivePane()</font></PRE>
<P>SetActivePane()函數(shù)將分隔窗口中的某個子窗口設(shè)置為當前焦點窗口,nPane是SPLIT_PANE_*類型的值,表示需要激活哪個窗格,這個函數(shù)還可以設(shè)置默認的活動窗格(后面介紹)。GetActivePane()函數(shù)查看所有擁有焦點的窗口,如果擁有焦點的窗口是窗格或窗格的子窗口就返回一個SPLIT_PANE_*類型的值,表示是哪個窗格。如果當前擁有焦點的窗口不是窗格的子窗口,那么GetActivePane()返回SPLIT_PANE_NONE。</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">bool</font></SPAN><font color="#0033FF"> ActivateNextPane(<SPAN class=cpp-keyword>bool</SPAN> bNext = <SPAN class=cpp-keyword>true</SPAN>)</font></PRE>
<P>如果分隔窗口是單窗格模式,焦點被設(shè)到可見的窗格上,否則的話,ActivateNextPane()函數(shù)將調(diào)用GetActivePane()查看擁有焦點的窗口。如果一個窗格(或窗格內(nèi)的子窗口)擁有檢點,分隔窗口就將焦點設(shè)給另一個窗格,否則ActivateNextPane()將判斷bNext的值,如果是true就激活left/top窗格,如果是false則激活right/bottom窗格。</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">bool</font></SPAN><font color="#0033FF"> SetDefaultActivePane(<SPAN class=cpp-keyword>int</SPAN> nPane)
<SPAN class=cpp-keyword>bool</SPAN> SetDefaultActivePane(HWND hWnd)
<SPAN class=cpp-keyword>int</SPAN> GetDefaultActivePane()</font></PRE>
<P>調(diào)用SetDefaultActivePane()函數(shù)可以設(shè)置默認的活動窗格,它的參數(shù)可以是SPLIT_PANE_*類型的值,也可以是窗口的句柄。如果分隔窗口自身得到的焦點,可以通過調(diào)用SetFocus()將焦點轉(zhuǎn)移給默認窗格。GetDefaultActivePane()函數(shù)返回SPLIT_PANE_*類型的值表示哪個窗格是當前默認的活動窗格。</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">void</font></SPAN><font color="#0033FF"> GetSystemSettings(<SPAN class=cpp-keyword>bool</SPAN> bUpdate)</font></PRE>
<P>GetSystemSettings()讀取系統(tǒng)設(shè)置并相應(yīng)的設(shè)置數(shù)據(jù)成員。分隔窗口在OnCreate()函數(shù)中自動調(diào)用這個函數(shù),你不需要自己調(diào)用這個函數(shù)。當然,你的主框架窗口應(yīng)該響應(yīng)WM_SETTINGCHANGE并將它傳遞給分隔窗口,
CSplitterWindow在WM_SETTINGCHANGE消息的處理函數(shù)中調(diào)用GetSystemSettings()。傳遞true給bUpdate參數(shù),分隔窗口會根據(jù)新的設(shè)置重畫自己。</P>
<H3><A name=splitdata></A><font color="#FFFF66">數(shù)據(jù)成員</font></H3>
<P>其他的一些特性可以通過直接訪問CSplitterWindow的公有成員來設(shè)定,只要GetSystemSettings()被調(diào)用了,這些公有成員也會相應(yīng)的被重置。</P>
<P>m_cxySplitBar:控制分隔條的寬度(垂直分隔條)和高度(水平分隔條)。默認值是通過調(diào)用GetSystemMetrics(SM_CXSIZEFRAME)(垂直分隔條)或GetSystemMetrics(SM_CYSIZEFRAME)(水平分隔條)得到的。</P>
<P>m_cxyMin:控制每個窗格的最小寬度(垂直分隔)和最小高度(水平分隔),分隔窗口不允許拖動比這更小的寬度或高度。如果分隔窗口有WS_EX_CLIENTEDGE擴展屬性,則這個變量的默認值是0,否則其默認值是2*GetSystemMetrics(SM_CXEDGE)(垂直分隔)或2*GetSystemMetrics(SM_CYEDGE)(水平分隔)。</P>
<p>m_cxyBarEdge:控制畫在分隔條兩側(cè)的3D邊界的寬度(垂直分隔)或高度(水平分隔),其默認值剛好和m_cxyMin相反。</p>
<p>m_bFullDrag:如果是true,當分隔條被拖動時窗格大小跟著調(diào)整,如果是false,拖動時只顯示一個分隔條的影子,直到拖動停止才調(diào)整窗格的大小。默認值是調(diào)用SystemParametersInfo(SPI_GETDRAGFULLWINDOWS)函數(shù)的返回值。</p>
<H2><A name=startsample></A><font color="#FFFF66">開始一個例子工程</font></H2>
<P>既然我們已經(jīng)對分隔窗口有了基本的了解,我們就來看看如何創(chuàng)建一個包含分隔窗口的框架窗口。使用WTL向?qū)ч_始一個新工程,在第一頁選擇SDI Application并單擊Next,在第二頁,如下圖所示取消工具條并選擇不使用視圖窗口:</P>
<P><IMG height=387 alt=" [AppWizard pg 2 - 22K] "
src="images/appwiz7.png"
width=477 align=bottom border=0></P>
<P>我們不使用分隔窗口是因為分隔窗口和它的窗格將作為“視圖窗口”,在CMainFrame類中添加一個CSplitterWindow類型的數(shù)據(jù)成員:</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">class</font></SPAN><font color="#0033FF"> CMainFrame : <SPAN class=cpp-keyword>public</SPAN> ...
{
<SPAN class=cpp-comment>//...</SPAN>
<SPAN class=cpp-keyword>protected</SPAN>:
CSplitterWindow m_wndVertSplit;
};</font></PRE>
<P>接著在OnCreate()中創(chuàng)建分隔窗口并將其設(shè)為視圖窗口:</P>
<PRE><font color="#0033FF">LRESULT CMainFrame::OnCreate ( LPCREATESTRUCT lpcs )
{
<SPAN class=cpp-comment>//...</SPAN>
<SPAN class=cpp-comment>// Create the splitter window</SPAN>
<SPAN class=cpp-keyword>const</SPAN> DWORD dwSplitStyle = WS_CHILD | WS_VISIBLE |
WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
dwSplitExStyle = WS_EX_CLIENTEDGE;
m_wndVertSplit.Create ( *<SPAN class=cpp-keyword>this</SPAN>, rcDefault, NULL,
dwSplitStyle, dwSplitExStyle );
<SPAN class=cpp-comment>// Set the splitter as the client area window, and resize</SPAN>
<SPAN class=cpp-comment>// the splitter to match the frame size.</SPAN>
m_hWndClient = m_wndVertSplit;
UpdateLayout();
<SPAN class=cpp-comment>// Position the splitter bar.</SPAN>
m_wndVertSplit.SetSplitterPos ( <SPAN class=cpp-literal>200</SPAN> );
<SPAN class=cpp-keyword>return</SPAN> <SPAN class=cpp-literal>0</SPAN>;
}</font></PRE>
<P>需要注意的是在設(shè)置分隔窗口的位置之前要先設(shè)置m_hWndClient并調(diào)用CFrameWindowImpl::UpdateLayout()函數(shù),UpdateLayout()將分隔窗口設(shè)置為初始時的大小。如果跳過這一步,分隔窗口的大小將不確定,可能小于200個象素點的寬度,最終導(dǎo)致SetSplitterPos()出現(xiàn)意想不到的結(jié)果。還有一種不調(diào)用UpdateLayout()函數(shù)的方,就是先得到框架窗口的客戶區(qū)坐標,然后使用這個客戶區(qū)坐標替換rcDefault坐標創(chuàng)建分隔窗口。使用這種方式創(chuàng)建的分隔窗口一開始就在正確的初始位置上,隨后對位置調(diào)整的函數(shù)(例如
SetSplitterPos())都可以正常工作。</P>
<P>現(xiàn)在運行我們的程序就可以看到分隔條,即使沒有創(chuàng)建任何窗格窗口它仍具有基本的行為。你可以拖動分隔條,用鼠標雙擊分隔條使其移到窗口的中間位置。</P>
<P><img height=255 alt=" [Empty splitter - 4K] "
src="images/emptysplit7.png"
width=329 align=bottom border=0></P>
<P>為了演示分隔窗口的不同使用方法,我將使用一個CListViewCtrl派生類和一個簡單的CRichEditCtrl,下面是從CClipSpyListCtrl類摘錄的代碼,我們在左邊的窗格使用這個類:</P>
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -