摘要:在大型專業(yè)軟件的工程管理中,要求在打開一個工程的時候,能夠恢復(fù)其保存時的所有信息,窗口恢復(fù)是其中的重點,而窗口Z坐標處理是窗口恢復(fù)的難點,也是實現(xiàn)窗口恢復(fù)的關(guān)鍵。文章以自動處理窗口信息來實現(xiàn)工程中各獨立窗口所有信息的輕松保存與恢復(fù)為目標,提供一種基于MFC的視圖Z坐標保存與恢復(fù)算法。
關(guān)鍵詞:窗口;Z坐標;算法;消息
引言
在一個大型專業(yè)軟件項目中,存在很多不同類型的窗口。而一個優(yōu)秀的專業(yè)軟件,在工程管理方面,應(yīng)該能夠在打開一個舊的工程時,自動恢復(fù)該工程關(guān)閉時的所有信息。然而,由于實現(xiàn)的難度大,許多專業(yè)軟件都沒有這一功能,用戶在打開一個工程后,往往還要手動調(diào)整以恢復(fù)到需要的操作狀態(tài),這給用戶帶來極大不便。
面對不同類型的窗口對象,找到一個簡單通用的工程視圖信息保存與恢復(fù)算法,以支持窗口恢復(fù),提高軟件的友好性與操作便利性,顯得十分必要。窗口Z坐標處理是窗口恢復(fù)的難點,也是實現(xiàn)窗口恢復(fù)的關(guān)鍵,只要解決了窗口Z坐標處理的問題,實現(xiàn)窗口恢復(fù)便不再困難。本文充分利用Windows系統(tǒng)的消息機制,以自動處理窗口信息、實現(xiàn)工程中各獨立窗口所有信息輕松保存與恢復(fù)為目標,提供一種基于MFC的視圖Z坐標保存與恢復(fù)算法。在實現(xiàn)的過程中,建立一個文檔類和一個視圖類,在這兩個類中實現(xiàn)這一套算法,其它的不同類型的窗口只需從這兩個類派生,就能一勞永逸地實現(xiàn)窗口Z坐標保存與恢復(fù),進而實現(xiàn)窗口恢復(fù)。這是一個通用的算法,可以應(yīng)用在任何使用MFC文檔視圖結(jié)構(gòu)的應(yīng)用程序中。
1、MFC文檔管理解析
MFC(Microsoft Foundation Class Library)是一個編程框架,將其中的各種類結(jié)合起來就能構(gòu)成一個應(yīng)用程序框架。MFC框架定義了應(yīng)用程序的輪廓,并提供了用戶接口的標準實現(xiàn)方法,程序員所要做的就是通過預(yù)定義的接口把具體應(yīng)用程序特有的東西填入這個輪廓。文檔視圖結(jié)構(gòu)(Document/ViewArchitecture)是MFC的精髓,也是Observer模式的具體實現(xiàn)框架之一,Document/View Architecture通過將數(shù)據(jù)和其表示分開,提供了很好的數(shù)據(jù)層次和表現(xiàn)層次的解耦。
文檔用來保存數(shù)據(jù),提供對數(shù)據(jù)進行處理的主要方法,并為視圖顯示提供數(shù)據(jù)。一個文檔可以擁有多個視圖。視圖在Windows中就是一個窗口,也就是一個可視化的矩形區(qū)域,用來展示文檔中的數(shù)據(jù)。每個視圖必須依附于一個框架。框架實際也是一個Windows窗口,但是在框架上可以放置菜單、工具欄、狀態(tài)欄等,而視圖則放在框架的客戶區(qū)。因此在MFC中我們看到的窗口實際上是Frame和View共同作用的結(jié)果。在某一具體時刻,程序中只有一個活動的文檔、框架和視圖,即當前的文檔、框架、視圖。文檔視圖結(jié)構(gòu)如圖1所示。
圖2示出了MFC多文檔管理方法。MFC文檔視圖結(jié)構(gòu)的應(yīng)用程序類CWinApp可以包含多個文檔模板對象,MFC通過文檔模板管理類CDocManager對這些文檔模板對象進行管理。文檔模板管理類中包含一個鏈表CPtrList m_templateList,用于保存程序中加入的文檔模板類對象。
文檔模板管理類CDocManager是一個隱藏的類,甚至在MSDN中都找不到其相關(guān)說明。在應(yīng)用程序類中,可以直接通過函數(shù)AddDocTemplate把已定義的文檔模板對象加入到文檔模板管理類對象m_pDocManager中。函數(shù)AddDocTemplate的原型為:
void AddDocTemplate(CDocTemplate* pTemplate);
pTemplate是要添加的文檔模板的指針。
文檔模板類CDocTemplate統(tǒng)一了文檔和視圖,CDocTem-plate在定義時必須指明一個文檔類、一個框架類和一個視圖類。這三個類的不同,形成了各式各樣的窗口。多文檔模板類CMuliiDocTemplate派生于CDocTemplate,其中增加了一個保護型鏈表成員CPtrList m_docList用來保存相應(yīng)文檔類所打開的各個文檔。這樣,應(yīng)用程序就和文檔有機地結(jié)合起來。
MFC通過文檔模板管理類,文檔模板類,文檔這三層關(guān)系緊密結(jié)合,有機地管理著程序中的文檔。
基于以上MFC文檔視圖結(jié)構(gòu)及其多文檔管理方法,我們設(shè)計出以下一套窗口Z坐標的保存與恢復(fù)算法。本算法把窗口信息保存在與之相對應(yīng)的文檔中,將視圖類與框架類結(jié)合,處理窗口操作信息。
2、窗口Z坐標的提取與保存
窗口Z坐標用以表示窗口前后位置,表明在多窗口的情況下,哪個窗口在前面,哪個在后面(被遮住)。窗口Z坐標的保存過程,即是提取窗口Z坐標并保存在適當?shù)胤降倪^程。本文為實現(xiàn)窗口z坐標處理,自定義兩個消息,即:WM_GETWNDPOS,用于提取窗口Z坐標;WM_SETWNDPOS,用于窗口Z坐標的恢復(fù)處理。自定義消息由消息名稱和消息處理函數(shù)兩部分組成。其中,消息名稱是消息的標識,為一個常數(shù),常用宏的形式給出;消息處理函數(shù)即消息處理的方法,它帶兩個參數(shù),通過這兩個參數(shù),實現(xiàn)窗口之間信息的傳遞,這兩個參數(shù)在發(fā)送消息時,由消息發(fā)送函數(shù)帶進來。
消息WM_GETWNDPOS的處理函數(shù)是OnGetWndPos,它接收一個有效參數(shù)wParam,用于計算窗口的坐標z。消息處理算法流程圖如圖3所示。
2.1算法解析
消息WM_GETWNDPOS傳遞參數(shù)wParam,通過在各個窗口之間傳遞,可逐一得到各窗口Z坐標。以下是結(jié)合圖3對該算法的解析,其中的關(guān)鍵點如取頂層窗口的方法等在算法實現(xiàn)部分給出。
z坐標的提取算法從當前激活窗口也就是頂層窗口開始執(zhí)行。算法啟動,首先取上層窗口,如果不成功,則證明本窗口為最頂層窗口,置z=0。如果沒有得到上層窗口,則wParam+l即為當前窗口z坐標。
為本窗口設(shè)置完z坐標后,取下層窗口,如果成功,則把wParam作為參數(shù),向下層窗口發(fā)送本消息。如果沒有下層窗口,證明所有窗口已全部計算完畢,提取Z坐標處理終止。
通過以上算法,得到窗口的Z坐標,然后將數(shù)據(jù)保存到文檔,并進一步保存到文件中,為窗口恢復(fù)準備數(shù)據(jù)。
2.2算法實現(xiàn)
2.2.1取頂層窗口
頂層窗口就是當前處于激活狀態(tài)的窗口,處于所有窗口的頂層。它代表著當前活動的文檔、框架和視圖,可以通過取主框架窗口類的活動框架,然后進一步取其活動視圖來得到。在MFC中可以用標準方法GetActiveFrame和GetActiveView分別實現(xiàn)。
2.2.2取下一窗口的方法
視圖類沒有取下一窗口的方法,但框架類有。所以取下一窗口,首先要用GetParentFrame函數(shù)得到視圖類的父框架,然后通過框架窗口的GetNextWindow函數(shù)來得到下一窗口,如果成功,函數(shù)返回下一窗口的指針,否則返回NULL。GetNextWindow函數(shù)帶一個參數(shù)nFlag,nFlag有兩種取值:GW_HWNDNEXT、GW_HWNDPREV,分別用來取下層和上層窗口,這i-i5函數(shù)的具體用法,參見MSDN。
2.2.3發(fā)進消息
Windows通過消息實現(xiàn)窗口之間數(shù)據(jù)的傳遞。向窗口發(fā)送消息,用Windows API函數(shù)SendMessage,其原型如下:
LRESULT SendMessage(HWND hWnd,UINT Msg,
WPARAM wParam,LPARAM IParam);
hWnd為消息目標窗口句柄。Msg為要發(fā)送的消息。wParam,IParam為消息參數(shù)。
本消息傳遞的四個參數(shù)是:消息的目的地(下一個窗口);消息名稱WM_GE7WNDPOS;消息參數(shù)wParam;還有一個參數(shù)不使用,填為0即可。
3、窗口Z坐標的恢復(fù)方法
窗口Z坐標的恢復(fù),即是在所需窗口全部打開后,根據(jù)工程文件中保存的Z坐標數(shù)據(jù),調(diào)整窗口的前后位置至工程保存前的狀態(tài)。
自定義消息WM_SETWNDPOS,處理函數(shù)OnSetWndPos。用來設(shè)置Z坐標。它接收一個參數(shù)wParam,wParam用于控制本消息的正常路由方向。打開工程后,可根據(jù)工程參數(shù)創(chuàng)建相應(yīng)的各類型窗口(創(chuàng)建窗口的方法可參見3.2.1)。自定義消息處理算法流程圖如圖4所示。
3.1算法解析
Z坐標恢復(fù)過程可以選擇在任意一窗口啟動,為方便起見,這里取頂層窗口。
算法開始,從文件中讀取Z坐標z并賦予臨時坐標zT,對zT進行處理,從而防止改變坐標z,然后移動窗口。進一步計算消息路由條件。如果消息應(yīng)該繼續(xù)路由,則向路由方向上的下一窗口路由消息;如果不再滿足路由條件,則退出算法,也同時結(jié)束了Z坐標的恢復(fù)過程。
3.2算法實現(xiàn)
3.2.1創(chuàng)建所有窗口
Z坐標恢復(fù)的前提是所有窗口已經(jīng)創(chuàng)建。而要創(chuàng)建一個窗口,只需有這個窗口的類型,也就是知道文檔模板類對象,然后通過CDocTemplate:OpenDocumentFile函數(shù)來打開一個文檔,并同時創(chuàng)建窗口。
3.2.2移動窗口
移動窗口,包括移動窗口的位置,控制窗口的大小以及改變窗口的z坐標。這里主要控制窗口的Z坐標??墒褂肅Wnd類的SetWindowPos函數(shù),原型為:
BOOL SetWindowPos(const CWnd*pWndlnsertAfter,int x,
int y,int cx,int cy,UINT nFlags);
參數(shù)說明:pWndlnsertAfter,指名移動窗口的位置,也就是把窗口移動到哪一個窗口的后面。可以有四個取值:wndBottom,wndTop,wndTopMost,wndNoTopMost。x,y,cx,cy,指定窗口大小。nFlags是大小和位置處理方式的指示。
本算法中移動窗口,還包括對臨時坐標zT操作:首先定義COMNUM代表最大窗口個數(shù),為一個足夠大的值,可以取500。COMMAX為一個較COMNUM大的值,用作輔助標記,可取1000。觀察ZT是否等于0,如果等于o,則移動窗口至最底層,并置zT=COMMAX,否則,讓zT自減1。置zT=COMMAX,就是讓zT成為一個很大的值,這將在計算路由條件時用到。
3.2.3路由條件
路由條件就是決定消息是否繼續(xù)路由的條件,同時還包含消息路由所必需的消息,如消息路由方向,路由數(shù)據(jù)等。路由條件由消息參數(shù)wParam及zT共同決定。wParam控制初始路由方向,wParam=1時,消息向上層窗口傳遞,wParam=0時,消息向下層窗口傳遞。zT為臨時z坐標,窗口的每一次操作都會改變zT。通過觀察zT的變化與取值,結(jié)合路由方向,可計算出路由條件。
路由條件計算方法:通過zT 4、算法復(fù)雜度 窗口z坐標的提取算法: 提取算法比較簡單,由于各窗口僅需遍歷一遍,其復(fù)雜度為0(N)。 窗口z坐標的恢復(fù)算法: 每一個窗口的臨時坐標zT根據(jù)z自減至0時算法即結(jié)束。其復(fù)雜度為: 1+2+3+…+n-1+n=(n+1)*n/2為O(n2)。 由上述算法復(fù)雜度分析可以得出本文所提供的窗口z坐標保存與恢復(fù)算法的復(fù)雜度是很低的。在實際應(yīng)用中它能在毫無感覺的情況下保存和恢復(fù)眾多窗口,滿足復(fù)雜專業(yè)項目應(yīng)用要求。 5、結(jié)束語 窗口Z坐標的提取、保存與恢復(fù)是實現(xiàn)大型專業(yè)軟件項目工程保存與恢復(fù)的關(guān)鍵技術(shù),處理了Z坐標,就可以進一步完成整個項目工程的保存與恢復(fù)了。本算法基于MFC,以較低的復(fù)雜度實現(xiàn)了窗口Z坐標的快速恢復(fù),具有很好的實用價值。同時,本算法具有很強的通用性,可以輕松地應(yīng)用到任何基于MFC的軟件。 該算法已在3G移動通信路測軟件項目中成功使用。