【轉】VC 對話框添加工具欄 以及RepositionBar 函數說明

1、添加工具欄資源ID爲IDR_TOOLBAR
2、在對話框的類定義中加:
CToolBar m_ToolBar;
3、在OnInitDialog中或其它合適的消息響應中加如下代碼:(函數可查看MSDN)

m_ToolBar.Create(this); //創建工具欄
m_ToolBar.LoadToolBar(IDR_TOOLBAR);//加載工具欄

//得出控件條大小.
CRect rect;
CRect rectNow;
GetClientRect(rect);
RepositionBars(AFX_IDW_CONTROLBAR_FIRST,AFX_IDW_CONTROLBAR_LAST,0,reposQuery,rectNow);

//放置控件條位置
CPoint ptOffset(rectNow.left-rect.left,rectNow.top-rect.top);

CRect rcChild;
CWnd* pwndChild=GetWindow(GW_CHILD);
while (pwndChild)
{                               
   pwndChild->GetWindowRect(rcChild);
   ScreenToClient(rcChild);
   rcChild.OffsetRect(ptOffset);
   pwndChild->MoveWindow(rcChild,FALSE);
   pwndChild=pwndChild->GetNextWindow();
}

//調整對話框尺寸
CRect rcWindow;
GetWindowRect(rcWindow);
rcWindow.right+=rect.Width()-rectNow.Width();
rcWindow.bottom+=rect.Height()-rectNow.Height();
MoveWindow(rcWindow, FALSE);

//控件條定位
RepositionBars(AFX_IDW_CONTROLBAR_FIRST,AFX_IDW_CONTROLBAR_LAST,0);
  
//對框居中
CenterWindow();


 

4、手工添加處理函數
afx_msg void OnBtnXXX();//消息響應函數聲明
ON_COMMAND(ID_BTN_XXX/*工具按鈕ID*/,OnBtnXXX/*函數名*/)//消息映射
         void CXXXDlg::OnBtnXXX(){}//消息處理函數

MFC窗口位置管理詳細分析及實例 在一般用MFC編寫的程序的窗口客戶區中,可能有好幾個子窗口(具有WM_CHILD風格的窗口)。上邊是工具欄,中間是視圖窗口,下邊是狀態欄。三個窗口在框架的客戶區裏和平共處,互不重疊。主框架窗口的尺寸改變了,別的子窗口都能及時調整自己的尺寸以便保持相互位置關係不變,例如狀態條窗口總能保持在主框架客戶區底部,並且其寬度總能和主框架客戶區寬度一致。工具欄窗口總能停靠在主框架的某一邊不變,其寬度或高度總能和主框架客戶區的寬度或高度一致,視圖窗口總能填滿主框架客戶區的剩餘空間。

假如我們自己從CWnd類派生一個窗口類並生成一個窗口,在它的客戶區裏要生成若干個子窗口,我們想使這些子窗口排列得規規矩矩,互不重疊,當父窗口的尺寸變了時各個子窗口能適時調整自己的尺寸和位置,使各個子窗口之間的位置大小比例關係不變。當移動其中一個或幾個子窗口時,別的子窗口能及時爲這個移動了的子窗口讓位。當然我們可以利用api函數裏管理窗口的函數來編寫自己的管理子窗口的方法。可是如果在父窗口的客戶區裏有了工具欄,狀態條等等子窗口時,你自己加進來的子窗口還能和這些mfc提供的子窗口融洽相處嗎?你如何保證你的子窗口不會覆蓋了能夠四處停靠的工具欄?當工具欄和狀態條消失後你的子窗口如何才能知道,以便及時調整自己的大小從而覆蓋工具欄和狀態條騰出的空間?基於文檔視圖構架的窗口的客戶區內還有個視圖,你自己硬加上的子窗口能不和視圖窗口爭地盤嗎?

所以必須瞭解mfc的窗口管理它的客戶區的方法。其實,mfc的窗口管理它的客戶區的方法是非常簡單的:父窗口調用一個函數,子窗口響應一個消息,就這麼多。

CWnd::RepositionBars函數和WM_SIZEPARENT消息

先簡述一下mfc的窗口爲子窗口分配客戶區空間的過程:這一過程是父窗口與子窗口共同協調完成的。父窗口先提供它的客戶區內的一塊區域,叫做起始可用區域。然後調用一個函數,在這個函數裏,父窗口把這片區域通過一個消息提交給它的第一個子窗口,該子窗口決定自己要佔用多大一塊,然後在可用區域裏把它將佔據的部分劃出去,這樣可用區域就被切去了一塊。父窗口再把這塊剩下的可用區域通過同樣的消息提交給第二個子窗口,第二個子窗口再根據自己的需要切掉一塊。如此這般,每個子窗口都切去自己所需的一塊。最後剩下的可用區域就給最後的子窗口使用。可以看出,除了最後一個子窗口外,其它子窗口都得在消息響應函數裏有自己的算法來決定自己將在可用區域裏佔據多大一塊,最後一個子窗口由於別無選擇,所以不需要這樣的算法。

當然,初始的可用區域是一個矩形,每次被切割後剩下的可用區域還是一個矩形,不可能是別的形狀的。

舉例說來,在一個典型單文檔程序中,父窗口就是從CFrameWnd派生的主框架窗口,最後一個子窗口就是視圖窗口,如果用了CSplitterWnd生成分隔條的話,最後一個子窗口就是擁有分隔條的那個窗口。其它子窗口就是工具欄窗口和狀態條窗口,以及可能有的別的控件窗口。

在典型多文檔界面程序中,父窗口就是主框架窗口,最後一個子窗口就是覆蓋在主窗口客戶區,背景爲黑灰色,擁有包含文檔的子框架窗口的那個窗口,這是個預定義了窗口類的窗口,它的窗口類名是“MDIClient”。如果用了CSplitterWnd生成分隔條的話,最後一個子窗口就是擁有分隔條的那個窗口。其它窗口就是工具欄窗口,狀態條窗口以及可能有的別的控件窗口。

這個函數和消息是:函數CWnd::RepositionBars()以及消息WM_SIZEPARENT。這個消息是mfc自定義的,不是windows自有的。

先簡單說明一下這個函數和消息。

1。函數CWnd::RepositionBars()

這個函數不是虛函數,所以就無法在派生類裏通過覆蓋來編制自己的版本了,只能搞懂它的功能,以便能靈活使用。

簡單而言,這個函數的功能是將可用的客戶區區域信息放到消息WM_SIZEPARENT的消息參數裏,然後枚舉本窗口的所有子窗口,給每個子窗口 (除掉一個特定的子窗口,相當於上文提到的最後一個子窗口)都發送這個消息,每個響應這個消息的子窗口都會把可用客戶區切去一塊。最後把那個特定的子窗口的尺寸和位置調整到剛好放在最後剩下的可用區域裏。

2。消息WM_SIZEPARENT

每個欲參與分配客戶區的子窗口都要響應這個消息,除非這個子窗口是那個特定的子窗口。

響應這個消息的子窗口至少要做兩件事:1,將可用的父窗口客戶區切去自己所佔據的一塊。2,根據消息參數的指示,將自己的大小和位置調整到剛好容納到自己所佔據的區域裏或不做調整。

下面詳細介紹一下函數CWnd::RepositionBars()和消息WM_SIZEPARENT。

1。函數CWnd::RepositionBars() void RepositionBars( UINT nIDFirst, UINT nIDLast, UINT nIDLeftOver, UINT nFlag = CWnd::reposDefault, LPRECT lpRectParam = NULL, LPCRECT lpRectClient = NULL, BOOL bStretch = TRUE );

參數比較多,但還是比較好懂的。

(1)nIDFirst和nIDLast

參與分配父窗口客戶區的子窗口的id範圍。

每個WM_CHILD風格的窗口都有個id,這是在窗口創建過程中指定的。函數CWnd::Create()的第六個參數就是這個id。api函數CreateWindow和 CreateWindowEx裏的那個HMENU類型的參數,當窗口的風格里有WM_CHILD時,它不是指的菜單句柄,而是該窗口的id。

nIDFirst和nIDLast參數指明瞭:如果一個子窗口的id值大於等於nIDFirst並且小於等於nIDLast,在這個函數中才會給這個子窗口發送 WM_SIZEPARENT消息,這個子窗口才能參與父窗口客戶區的分配。

(2)nIDLeftOver

前面說過,有一個特定的子窗口,它不響應WM_SIZEPARENT消息。只有當其它的子窗口都分配完了,它纔來撿取父窗口客戶區裏剩下的那塊。 nIDLeftOver正是這個子窗口的id。它也必須大於等於nIDFirst並且小於等於nIDLast。

(3)lpRectClient

這是一個指向RECT結構數據的指針。這個RECT結構裏存放的正是父窗口客戶區的初始可用區域。隨着在該函數裏依次給各個子窗口發送 WM_SIZEPARENT消息,每個響應這個消息的子窗口都會切去自己所佔據的部分。最後剩下的部分,就是id爲nIDLeftOver的子窗口將要佔據的區域了。這個參數可以爲NULL,這時初始的可用區域就是整個父窗口客戶區。

(4)nFlag和lpRectParam

這兩個參數放在一起講比較好。nFlag是該函數的功能標誌,它可以有三個值:reposDefault,reposQuery 和reposExtra。

當nFlag等於reposDefault時,RepositionBars函數的功能是這樣的:依次給id介於nIDFirst和nIDLast之間並且不等於nIDLeftOver的子窗口發送WM_SIZEPARENT消息,每個響應這個消息的子窗口從lpRectClient所指的結構裏切去自己所佔據的部分,並且將自己的大小和位置調整到自己所佔據的區域的大小,最後RepositionBars函數還將id爲nIDLeftOver的子窗口的大小和位置調整到被其他子窗口切剩的可用區域內,使這個子窗口正好完全覆蓋最後的可用區域。這種情況下lpRectParam不用,可以爲NULL。

當nFlag等於reposQuery 時,RepositionBars函數的功能是這樣的:依次給id介於nIDFirst和nIDLast之間並且不等於nIDLeftOver的子窗口發送WM_SIZEPARENT消息,每個響應這個消息的子窗口從lpRectClient所指的結構裏切去自己所佔據的部分,但是他們並不調整自己的大小和位置,最後RepositionBars函數並不調整將id爲nIDLeftOver的子窗口的大小和位置,而是根據bStretch的值來做動作:如果bStretch爲TRUE,那麼 RepositionBars函數把最後剩下的可用區域拷貝到lpRectParam指向的RECT結構裏;如果bStretch爲FALSE,那麼RepositionBars函數把所有其他子窗口占用掉的可用區域的高和寬(要所有的子窗口都緊排在一起,形成一個大的矩形,這個值纔有意義)拷貝到lpRectParam指向的RECT結構的bottom 和right成員裏,其top和left成員被置零。使用這個nFlag值來調用RepositionBars的目的不是要重排子窗口,而是要看看,假如重排子窗口的話,這些子窗口將佔去多大一塊,最後剩下的可用區域在什麼位置等等信息。

當nFlag等於reposExtra時,該函數的功能和nFlag等於reposDefault時差不多,有點小小的區別。此時需要用到lpRectParam。前面說過,當 nFlag等於reposDefault時,RepositionBars函數將在最後把id爲nIDLeftOver的子窗口的大小和位置調整到被其他子窗口切剩的可用區域內,使這個子窗口正好完全覆蓋最後的可用區域。而當nFlag等於reposExtra時,RepositionBars在調整id爲nIDLeftOver的子窗口的大小和位置前,還要用 lpRectParam來對最後剩下的可用區域做修正。假設lpRect指向的是最後的可用區域,那麼這個修正是這樣進行的:


lpRect->top+=lpRectParam->top;
lprect->left+=lpRectParam->left;
lpRect->right-=lpRectParam->right;
lpRect->bottom-=lpRectParam->bottom;

通過這樣的修正,可以使最後剩下的可用區域不被id爲nIDLeftOver的子窗口占滿,而是空出一些地方來留作他用。
(5)bStretch

這個參數上面已經提到一點它的作用。它主要是提供給各個響應WM_SIZEPARENT消息的子窗口用的,子窗口例如工具欄,狀態條等在決定自己將從父窗口客戶區的可用空間裏划走多少時,這個參數也是個判斷的依據。詳細可以參閱工具欄和狀態條響應WM_SIZEPARENT的函數OnSizeParent()。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章