1. 程式人生 > >MFC模態對話方塊和非模態對話方塊

MFC模態對話方塊和非模態對話方塊

下面是一個我的手寫的模態對話方塊和非模態對話方塊圖:

模態對話方塊是指當其顯示時,程式會暫停執行,直到關閉這個模態對話方塊後,才能繼續執行程式中其他任務。非模態對話方塊是指當其顯示時,允許轉而執行程式中其他任務,而不用關閉這個對話方塊。 

    模態對話方塊的建立:建立模態對話方塊需要呼叫CDialog類的成員函式:DoModal,該函式的功能是建立並顯示一個模態對話方塊,其返回值將作為CDialog類的另一個成員函式:EndDialog的引數,後者的功能是關閉模態對話方塊。一般顯示模態對話方塊的實現程式碼如下:

void CASCEView::OnDialog()

{

         CASCEDlg dlg;

         dlg.DoModal();

非模態對話方塊的建立:要建立非模態對話方塊就需要利用CDialog類的Create成員函式,該函式有以下兩種形式的宣告:

virtual BOOL Create(

   LPCTSTR lpszTemplateName,

   CWnd* pParentWnd = NULL

);

virtual BOOL Create(

   UINT nIDTemplate,

   CWnd* pParentWnd = NULL

); 

    由上可知,CDialog::Create函式的第一個引數可以是對話方塊資源的ID(nIDTemplate),或者也可以是對話方塊模板的名稱(lpszTemplateName);而第二個引數指定了對話方塊的父視窗,如果其值是NULL,則對話方塊的父視窗就是主應用程式視窗。

    當利用Create函式建立非模態對話方塊時,我們還需要接著呼叫ShowWindow函式來將這個對話方塊顯示出來;而利用DoModal建立的模態對話方塊之所以不用,是因為DoModal函式本身就有顯示模態對話方塊的作用。同時我們不能像模態對話方塊那樣將對話方塊定義成物件,如下程式碼是顯示不出非模態對話方塊的:

void CASCEView::OnDialog()

{

         CASCEDlg dlg;

         dlg.Create(IDD_DIALOG1, this);

         dlg.ShowWindow(SW_SHOW);

    因為這裡建立的非模態對話方塊物件dlg是一個區域性物件,當程式執行時,會依次執行各條程式碼,當OnDialog函式執行結束時,dlg這個物件的生命週期也就玩玩了,它就會銷燬與之相關聯的對話方塊資源,對話方塊自然就顯示不出來啦!而模態對話方塊之所以能夠顯示,是因為當執行到呼叫DoModal函式以顯示模態對話方塊時,程式就會暫停執行,直到模態對話方塊關閉之後,程式才繼續向下執行。而這之前,dlg還沒銷燬。

    因此,在建立非模態對話方塊時,不能將對話方塊物件定義成區域性變數,解決方法有二:一是把對話方塊物件定義成CASCEView類的成員變數;二是把它定義成指標,在堆上分配記憶體,如下:

void CASCEView::OnDialog()

{

         CASCEDlg *pDlg = new CASCEDlg;

         pDlg->Create(IDD_DIALOG1, this);

         pDlg->ShowWindow(SW_SHOW);

    但是這又引入了新的問題:我們必須釋放pDlg佔用的資源,否則會造成記憶體洩漏!況且這裡pDlg還是一個區域性指標變數,當它的生命週期結束時,在程式中就無法再引用它所指向的那塊記憶體了。解決方法同樣有兩個:一是將pDlg定義成CASCEView類的成員變數,然後在CASCEView類的解構函式中呼叫delete函式釋放它指向的記憶體;二是在CASCEDlg類中過載PostNcDestroy虛擬函式,釋放this指標指向的記憶體:

void CASCEDlg::PostNcDestroy()

{

         delete this;

         CDialog::PostNcDestroy();

    還有一點需要注意的是:當單擊對話方塊上的預設OK按鈕時,兩種對話方塊都會消失。但對於模態對話方塊而言,此時對話方塊視窗物件被銷燬了;而對非模態對話方塊來說,對話方塊視窗物件並未被銷燬,只是隱藏起來而已。 

    在非模態對話方塊中單擊OK按鈕後,程式會呼叫基類CDialog的OnOK函式,這是一個虛擬函式,後者又會呼叫EndDialog函式,這個函式用於終止模態對話方塊,但對於非模態對話方塊,這個函式只是使對話方塊視窗不可見,並不銷燬它。因此,對非模態對話方塊來說,如果有一個ID值為IDOK的按鈕,就必須重寫基類的OnOK虛擬函式,並在重寫的函式中呼叫DestroyWindow函式,以完成銷燬對話方塊的工作,同時注意不要再呼叫基類的OnOK函式。同理,如果非模態對話方塊中有一個ID值為IDCANCEL的按鈕,也必須重寫基類的OnCancel虛擬函式,並在重寫的函式中呼叫DestroyWindow函式,銷燬對話方塊,同時注意不要再呼叫基類的OnCancel函數了。 

     非模態對話方塊相對於模態對話方塊,他的建立和銷燬過程和模態對話方塊有一定的區別

先看一下MSDN的原文:

When   you   implement   a   modeless   dialog   box,   always   override   the   OnCancel   member   function   and   call   DestroyWindow   from   within   it.   Don’t   call   the   base   class   CDialog::OnCancel,   because   it   calls   EndDialog,   which   will   make   the   dialog   box   invisible   but   will   not   destroy   it.   You   should   also   override   PostNcDestroy   for   modeless   dialog   boxes   in   order   to   delete   this,   since   modeless   dialog   boxes   are   usually   allocated   with   new.   Modal   dialog   boxes   are   usually   constructed   on   the   frame   and   do   not   need   PostNcDestroy   cleanup.

MS的指示:非模態對話方塊需要過載函式OnCanel,並且在這個函式中呼叫DestroyWindow。並且不能呼叫基類的OnCancel,因為基類的OnCancel呼叫了EndDialog這個函式,這個函式是針對模態對話方塊的。

還有一個必須過載的函式就是PostNcDestroy,這也是一個虛擬函式,通常的非模態對話方塊是用類的指標,通過new建立的,這就需要在PostNcDestroy函式中delete掉這個指標。 

瞭解了理論過後,下面我們就可以用程式碼實現一下非模態對話方塊的建立和銷燬過程:

//建立

//主框架中:

CTestDlg *pDlg=new CTestDlg;

pDlg->Create(IDD_TESTDLG,this);

pDlg->ShowWindow(SW_SHOW); 

//對話方塊中:

void CTestDlg::OnCancel()

{

     DestroyWindow();

}

void CTestDlg::PostNcDestroy()

{

     CDialog::PostNcDestroy();

     delete this;

如果要在點選按鈕的情況下,銷燬非模態對話方塊,只需要把按鈕的事件對映到OnCancel函式即可。

以下是一點資料供參考: 

MFC應用程式中處理訊息的順序

1.AfxWndProc()       該函式負責接收訊息,找到訊息所屬的CWnd物件,然後呼叫AfxCallWndProc 

2.AfxCallWndProc()   該函式負責儲存訊息(儲存的內容主要是訊息識別符號和訊息引數)供應用程式以後使用,然後呼叫WindowProc()函式 

3.WindowProc()       該函式負責傳送訊息到OnWndMsg()函式,如果未被處理,則呼叫DefWindowProc()函式 

4.OnWndMsg()         該函式的功能首先按位元組對訊息進行排序,對於WM_COMMAND訊息,呼叫OnCommand()訊息響應函式,對於WM_NOTIFY訊息呼叫OnNotify()訊息響應函式。任何被遺漏的訊息將是一個視窗訊息。OnWndMsg()函式搜尋類的訊息映像,以找到一個能處理任何視窗訊息的處理函式。如果OnWndMsg()函式不能找到這樣的處理函式的話,則把訊息返回到WindowProc()函式,由它將訊息傳送給DefWindowProc()函式 

5.OnCommand()       該函式檢視這是不是一個控制元件通知(lParam引數不為NULL,如果lParam引數為空的話,說明該訊息不是控制元件通知),如果它是,OnCommand()函式會試圖將訊息對映到製造通知的控制元件;如果他不是一個控制元件通知(或者如果控制元件拒絕對映的訊息)OnCommand()就會呼叫OnCmdMsg()函式 

6.OnCmdMsg()         根據接收訊息的類,OnCmdMsg()函式將在一個稱為命令傳遞(Command Routing)的過程中潛在的傳遞命令訊息和控制元件通知。例如:如果擁有該視窗的類是一個框架類,則命令和通知訊息也被傳遞到檢視和文件類,併為該類尋找一個訊息處理函式 

MFC應用程式建立視窗的過程

1.PreCreateWindow()   該函式是一個過載函式,在視窗被建立前,可以在該過載函式中改變建立引數 (可以設定視窗風格等等) 

2.PreSubclassWindow() 這也是一個過載函式,允許首先子分類一個視窗 

3.OnGetMinMaxInfo()   該函式為訊息響應函式,響應的是WM_GETMINMAXINFO訊息,允許設定視窗的最大或者最小尺寸 

4.OnNcCreate()         該函式也是一個訊息響應函式,響應WM_NCCREATE訊息,傳送訊息以告訴視窗的客戶區即將被建立 

5.OnNcCalcSize()       該函式也是訊息響應函式,響應WM_NCCALCSIZE訊息,作用是允許改變視窗客戶區大小 

6.OnCreate()           該函式也是一個訊息響應函式,響應WM_CREATE訊息,傳送訊息告訴一個視窗已經被建立 

7.OnSize()             該函式也是一個訊息響應函式,響應WM_SIZE訊息,傳送該訊息以告訴該視窗大小已經發生變化 

8.OnMove()             訊息響應函式,響應WM_MOVE訊息,傳送此訊息說明視窗在移動 

9.OnChildNotify()     該函式為過載函式,作為部分訊息對映被呼叫,告訴父視窗即將被告知一個視窗剛剛被建立 

MFC應用程式關閉視窗的順序(非模態視窗) 

1.OnClose()       訊息響應函式,響應視窗的WM_CLOSE訊息,當關閉按鈕被單擊的時候傳送此訊息 

2.OnDestroy()     訊息響應函式,響應視窗的WM_DESTROY訊息,當一個視窗將被銷燬時,傳送此訊息 

3.OnNcDestroy()   訊息響應函式,響應視窗的WM_NCDESTROY訊息,當一個視窗被銷燬後傳送此訊息 

4.PostNcDestroy() 過載函式,作為處理OnNcDestroy()函式的最後動作,被CWnd呼叫 

MFC應用程式中開啟模式對話方塊的函式呼叫順序 

1.DoModal()             過載函式,過載DoModal()成員函式 

2.PreSubclassWindow()    過載函式,允許首先子分類一個視窗 

3.OnCreate()             訊息響應函式,響應WM_CREATE訊息,傳送此訊息以告訴一個視窗已經被建立 

4.OnSize()               訊息響應函式,響應WM_SIZE訊息,傳送此訊息以告訴視窗大小發生變化 

5.OnMove()               訊息響應函式,響應WM_MOVE訊息,傳送此訊息,以告訴視窗正在移動 

6.OnSetFont()           訊息響應函式,響應WM_SETFONT訊息,傳送此訊息,以允許改變對話方塊中控制元件的字型 

7.OnInitDialog()         訊息響應函式,響應WM_INITDIALOG訊息,傳送此訊息以允許初始化對話方塊中的控制元件,或者是建立新控制元件 

8.OnShowWindow()         訊息響應函式,響應WM_SHOWWINDOW訊息,該函式被ShowWindow()函式呼叫 

9.OnCtlColor()           訊息響應函式,響應WM_CTLCOLOR訊息,被父視窗傳送已改變對話方塊或對話方塊上面控制元件的顏色 

10. OnChildNotify()     過載函式,作為WM_CTLCOLOR訊息的結果傳送 

MFC應用程式中關閉模式對話方塊的順序 

1.OnClose()         訊息響應函式,響應WM_CLOSE訊息,當"關閉"按鈕被單擊的時候,該函式被呼叫 

2.OnKillFocus()     訊息響應函式,響應WM_KILLFOCUS訊息,當一個視窗即將失去鍵盤輸入焦點以前被髮送 

3.OnDestroy()       訊息響應函式,響應WM_DESTROY訊息,當一個視窗即將被銷燬時,被髮送 

4.OnNcDestroy()     訊息響應函式,響應WM_NCDESTROY訊息,當一個視窗被銷燬以後被髮送 

5.PostNcDestroy()   過載函式,作為處理OnNcDestroy()函式的最後動作被CWnd呼叫 

開啟無模式對話方塊的順序 

1.PreSubclassWindow()     過載函式,允許使用者首先子分類一個視窗 

2.OnCreate()             訊息響應函式,響應WM_CREATE訊息,傳送此訊息以告訴一個視窗已經被建立 

3.OnSize()               訊息響應函式,響應WM_SIZE訊息,傳送此訊息以告訴視窗大小發生變化 

4.OnMove()               訊息響應函式,響應WM_MOVE訊息,傳送此訊息以告訴視窗正在移動 

5.OnSetFont()             訊息響應函式,響應WM_SETFONT訊息,傳送此訊息以允許改變對話方塊中控制元件的字型 

以上這些的執行都是按給定的順序執行!