論模式對話框與非模式對話框

 
Mqt-2003
小豆
論模式和非模式對話框
 
論模式和非模式對話框
                                                                     作者:孟慶濤  Email: [email protected]

論模式和非模式對話框... 1

1:摘要... 1

2:模式對話框的顯示... 1

3:模式對話框的循環等待... 3

4:模式對話框的循環終止... 6

5:與OK和Cancle按鈕的聯繫... 6

1:摘要
模式對話框使用dlg.DoModal()函數,程序會在你按下OK或者Cancle按鈕之前處於等待狀態。然後點擊OK或者Cancle按鈕,就可以調用EndDialog函數消除模式對話框。

相比之下,非模式對話框可能要顯得複雜,你要使用Create函數創建非模式對話框,並且在推出時,必須調用CWnd::DestroyWindow函數銷燬窗口。而且要注意的是,你若想點擊OK按鈕使非模式對話框推出,要重寫OnOK函數,使其調用CWnd::DestroyWindow。

那麼,這是爲什麼呢?模式對話框的實現真的比非模式要簡單嗎?讓我們看一下CDialog::DoModal()的源代碼。

2:模式對話框的顯示
       INT_PTR CDialog::DoModal()

{

      ***********************************************

      //加載模板資源

************************************************

     ASSERT(m_lpszTemplateName != NULL || m_hDialogTemplate != NULL ||

         m_lpDialogTemplate != NULL);

       // load resource as necessary

     LPCDLGTEMPLATE lpDialogTemplate = m_lpDialogTemplate;

     HGLOBAL hDialogTemplate = m_hDialogTemplate;

     HINSTANCE hInst = AfxGetResourceHandle();

     if (m_lpszTemplateName != NULL)

     {

         hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);

         HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);

         hDialogTemplate = LoadResource(hInst, hResource);

     }

     if (hDialogTemplate != NULL)

         lpDialogTemplate = (LPCDLGTEMPLATE)LockResource(hDialogTemplate);

     // return -1 in case of failure to load the dialog template resource

     if (lpDialogTemplate == NULL)

         return -1;

     ***********************************************

     //使父窗口無效

***********************************************

     HWND hWndParent = PreModal();

     AfxUnhookWindowCreate();

     BOOL bEnableParent = FALSE;

     if (hWndParent && hWndParent != ::GetDesktopWindow() && ::IsWindowEnabled(hWndParent))

     {

         ::EnableWindow(hWndParent, FALSE);

         bEnableParent = TRUE;

     }

       TRY

     {

         ***********************************************

         //創建非模式對話框

***********************************************

         AfxHookWindowCreate(this);

         if (CreateDlgIndirect(lpDialogTemplate,

                            CWnd::FromHandle(hWndParent), hInst))

         {

              if (m_nFlags & WF_CONTINUEMODAL)

              {

                   // enter modal loop

                   DWORD dwFlags = MLF_SHOWONIDLE;

                   if (GetStyle() & DS_NOIDLEMSG)

                       dwFlags |= MLF_NOIDLEMSG;

                   ***********************************************

                  //關鍵:調用RunModalLoop函數,程序進入其內的for循環

                //所以,模式對話框在點擊OK或Cancel前,程序會暫時等待。

                   ***********************************************

                   VERIFY(RunModalLoop(dwFlags) == m_nModalResult);

              }

              ***********************************************

              //在父窗口可用前,先隱藏對話框(注:暫時還沒有銷燬)

              ***********************************************

                            if (m_hWnd != NULL)

                   SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW|

                                   SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);

         }

     }

     CATCH_ALL(e)

     {

         DELETE_EXCEPTION(e);

         m_nModalResult = -1;

     }

       END_CATCH_ALL

     ***********************************************

     //使父窗口可用,並且激活父窗口

     ***********************************************

     if (bEnableParent)

         ::EnableWindow(hWndParent, TRUE);

     if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)

         ::SetActiveWindow(hWndParent);

 

***********************************************

//銷燬對話框

***********************************************

     // destroy modal window

     DestroyWindow();

     PostModal();

 

     // unlock/free resources as necessary

     if (m_lpszTemplateName != NULL || m_hDialogTemplate != NULL)

         UnlockResource(hDialogTemplate);

     if (m_lpszTemplateName != NULL)

         FreeResource(hDialogTemplate);

 

     return m_nModalResult;

}

3:模式對話框的循環等待
從上面的代碼,我們可以發現,模式對話框的底層爲我們實現了對話框的create和destroywindow,所以我們可以只管dlg.domoadl()來顯示,然後調用EndDialog來結束。那麼EndDialog 的作用是什麼呢?我們看它裏面的循環函數,就可以理解,原來Enddialog的作用其實是爲了跳出循環函數RunModalLoop,使程序繼續執行。

具體代碼如下:

 

int CWnd::RunModalLoop(DWORD dwFlags)

{

       ASSERT(::IsWindow(m_hWnd)); // window must be created

     ASSERT(!(m_nFlags & WF_MODALLOOP)); // window must not already be in modal state

 

     // for tracking the idle time state

     BOOL bIdle = TRUE;

     LONG lIdleCount = 0;

     BOOL bShowIdle = (dwFlags & MLF_SHOWONIDLE) && !(GetStyle() & WS_VISIBLE);

     HWND hWndParent = ::GetParent(m_hWnd);

     m_nFlags |= (WF_MODALLOOP|WF_CONTINUEMODAL);

     MSG *pMsg = AfxGetCurrentMessage();

 

    **************************************************

    //通過for (;;),使程序處於循環等待狀態。

    ****************************************************

     for (;;)

     {

              ASSERT(ContinueModal());

 

         // phase1: check to see if we can do idle work

         while (bIdle &&

              !::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE))

         {

                     ASSERT(ContinueModal());

 

              // show the dialog when the message queue goes idle

              if (bShowIdle)

              {

                   ShowWindow(SW_SHOWNORMAL);

                   UpdateWindow();

                   bShowIdle = FALSE;

              }

 

              // call OnIdle while in bIdle state

              if (!(dwFlags & MLF_NOIDLEMSG) && hWndParent != NULL && lIdleCount == 0)

              {

                   // send WM_ENTERIDLE to the parent

                   ::SendMessage(hWndParent, WM_ENTERIDLE, MSGF_DIALOGBOX, (LPARAM)m_hWnd);

              }

              if ((dwFlags & MLF_NOKICKIDLE) ||

                   !SendMessage(WM_KICKIDLE, MSGF_DIALOGBOX, lIdleCount++))

              {

                   // stop idle processing next time

                   bIdle = FALSE;

              }

         }

 

         // phase2: pump messages while available

         do

         {

                     ASSERT(ContinueModal());

 

              // pump message, but quit on WM_QUIT

              if (!AfxPumpMessage())

              {

                   AfxPostQuitMessage(0);

                   return -1;

              }

 

              // show the window when certain special messages rec'd

              if (bShowIdle &&

                   (pMsg->message == 0x118 || pMsg->message == WM_SYSKEYDOWN))

              {

                   ShowWindow(SW_SHOWNORMAL);

                   UpdateWindow();

                   bShowIdle = FALSE;

              }

 

              *************************************************************8

            //通過判斷,跳出循環,可以斷定,EndDialog 和ContinueModal有聯繫

            **************************************************************

              if (!ContinueModal())

                   goto ExitModal;

 

              // reset "no idle" state after pumping "normal" message

              if (AfxIsIdleMessage(pMsg))

              {

                   bIdle = TRUE;

                   lIdleCount = 0;

              }

 

         } while (::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE));

     }

 

ExitModal:

     m_nFlags &= ~(WF_MODALLOOP|WF_CONTINUEMODAL);

     return m_nModalResult;

}

4:模式對話框的循環終止
EndDialog函數調用EndMoadlLoop函數,以便跳出循環。

void CDialog::EndDialog(int nResult)

{

     ASSERT(::IsWindow(m_hWnd));

     if (m_nFlags & (WF_MODALLOOP|WF_CONTINUEMODAL))

             EndModalLoop(nResult);

     ::EndDialog(m_hWnd, nResult);

}

 

BOOL CWnd::ContinueModal()

{

     return m_nFlags & WF_CONTINUEMODAL;

}

 

void CWnd::EndModalLoop(int nResult)

{

       ASSERT(::IsWindow(m_hWnd));

 

*****************************************************

// m_nModalResult的值爲IDOK或者IDCANCEL,它將作爲DoModal的返回值

*****************************************************

     m_nModalResult = nResult;

 

     // make sure a message goes through to exit the modal loop

     if (m_nFlags & WF_CONTINUEMODAL)

     {

         m_nFlags &= ~WF_CONTINUEMODAL;

         PostMessage(WM_NULL);

     }

}

5:與OK和Cancle按鈕的聯繫
爲什麼按下OK或者Cancle按鈕會終止模式對話框呢?因爲它們都調用了EndDialog函數,代碼如下:

注:IDOK和IDCANCEL將會作爲DoModal的返回值。

從下面的代碼可以看出,OnOK()和OnCancel()消息響應函數並沒有調用DestroyWindow,它們只是調用了EndDialog跳出循環,並沒有銷燬窗庫。對模式對話框,DoModal函數自動調用DestroyWindow,而對非模式對話框,我們若要使用OK或者Cancle按鈕結束對話框,必須重寫OnOK按鈕以使其調用DestroyWindow銷燬窗口。

 

void CDialog::OnOK()

{

     if (!UpdateData(TRUE))

     {

         TRACE(traceAppMsg, 0, "UpdateData failed during dialog termination./n");

              // the UpdateData routine will set focus to correct item

         return;

     }

     EndDialog(IDOK);

}

 

void CDialog::OnCancel()

{

     EndDialog(IDCANCEL);

}

 

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