天天看點

RepositionBars函數的用法

      對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。

3、函數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()。