论模式和非模式对话框

来源:互联网 发布:php单双引号的区别 编辑:程序博客网 时间:2024/05/21 10:25
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.DoModal()来显示,然后调用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);}


如下代码是局部变量,函数执行完对象生命周期就已经结束,所以无法显示对话框。解决方案有两种,一种是把对话框定义为类的成员变量,另一种是定义为指针在堆上分配内存,而且非模式对话框,必须调用ShowWindow()来显示窗口

BOOL CMFCListCtrlDlg::OnInitDialog(){CTestDLG Dlg;Dlg.Create(IDD_DIALOG1,this);Dlg.ShowWindow(SW_SHOW);}

你要使用Create函数创建非模式对话框,并且在退出时,必须调用CWnd::DestroyWindow函数销毁窗口。而且要注意的是,你若想点击OK按钮使非模式对话框退出,要重写OnOK函数,使其调用CWnd::DestroyWindow。因为EndDialog只是隐藏了非模式对话框的显示。

如下的代码中对话框指针是一个局部变量,在执行完时候地址就丢失了,造成内存的泄漏,解决方法一是窗口指针定义为类的成员变量,在析构函数中delete,方法二是重载对话框类中的PostNcDestroy()虚函数,×××××修改后的对话框类是不能同时用于模式和非模式对话框的创建的××××××××。

void CMFCClass::OnBnClicked(){CTestDLG *dlg=new CTestDLG();dlg->Create(IDD_DIALOG1,this);dlg->ShowWindow(SW_SHOW);}


void CTestDLG::PostNcDestroy(){delete this;CDialog::PostNcDestroy();}


原创粉丝点击