?? mfc教程_ 對話框和對話框類cdialog.htm
字號:
<P align=justify>CDialog::OnCmdMsg不僅適用于模式對話框,也適用于無模式對話框。</P>
<P align=justify></P>
<LI><A name=_Toc457299122></A><B>消息預(yù)處理和Dialog消息</B>
<P></P>
<P
align=justify>另外,對話框窗口的消息處理還有一個特點,就是增加了對Dialog消息的處理,如同在介紹::IsDialogMessage函數(shù)時所述。如果是Dialog消息,MFC框架將不會讓它進入下一步的消息循環(huán)。為此,MFC覆蓋了CDialog的虛擬函數(shù)PreTranslateMessage,該函數(shù)的實現(xiàn)如下:</P>
<P align=justify>BOOL CDialog::PreTranslateMessage(MSG* pMsg)</P>
<P align=justify>{</P>
<P align=justify>// 用于無模式或者模式對話框的處理</P>
<P align=justify>ASSERT(m_hWnd != NULL);</P>
<P align=justify></P>
<P align=justify>//過濾tooltip messages</P>
<P align=justify>if (CWnd::PreTranslateMessage(pMsg))</P>
<P align=justify>return TRUE;</P>
<P align=justify></P>
<P align=justify>//在Shift+F1幫助模式下,不轉(zhuǎn)換Dialog messages</P>
<P align=justify>CFrameWnd* pFrameWnd = GetTopLevelFrame();</P>
<P align=justify>if (pFrameWnd != NULL &&
pFrameWnd->m_bHelpMode)</P>
<P align=justify>return FALSE;</P>
<P align=justify></P>
<P align=justify>//處理Escape鍵按下的消息</P>
<P align=justify>if (pMsg->message == WM_KEYDOWN &&</P>
<P align=justify>(pMsg->wParam == VK_ESCAPE || pMsg->wParam ==
VK_CANCEL) &&</P>
<P align=justify>(::GetWindowLong(pMsg->hwnd, GWL_STYLE) &
ES_MULTILINE) &&</P>
<P align=justify>_AfxCompareClassName(pMsg->hwnd, _T("Edit")))</P>
<P align=justify>{</P>
<P align=justify>HWND hItem = ::GetDlgItem(m_hWnd, IDCANCEL);</P>
<P align=justify>if (hItem == NULL || ::IsWindowEnabled(hItem))</P>
<P align=justify>{</P>
<P align=justify>SendMessage(WM_COMMAND, IDCANCEL, 0);</P>
<P align=justify>return TRUE;</P>
<P align=justify>}</P>
<P align=justify>}</P>
<P align=justify>// 過濾來自控制該對話框子窗口的送給該對話框的Dialog消息</P>
<P align=justify>return PreTranslateInput(pMsg);</P>
<P align=justify>}</P>
<P
align=justify>從其實現(xiàn)可以看出,如果是Tooltip消息或者Dialog消息,這些消息將在PreTranslateMessage中被處理,不會進入消息發(fā)送的處理。</P>
<P
align=justify>PreTranslateInput是CWnd的成員函數(shù),它調(diào)用::IsDialogMessage函數(shù)來處理Dialog消息。</P>
<P align=justify>PreTranslateMessage的實現(xiàn)不僅用于模式對話框,而且用于無模式對話框。</P>
<P align=justify></P>
<LI><A name=_Toc457299123></A><B>模式對話框的消息循環(huán)</B>
<P></P></LI></OL>
<P
align=justify>從DoModal的實現(xiàn)可以看出,DoModal調(diào)用CreateDlgIndirect創(chuàng)建的是無模式對話框,MFC如何來接管和控制應(yīng)用程序的消息隊列,實現(xiàn)一個模式對話框的功能呢?</P>
<P
align=justify>CDialog調(diào)用了RunModalLoop來實現(xiàn)模式窗口的消息循環(huán)。RunModalLoop是CWnd的成員函數(shù),它和相關(guān)函數(shù)的實現(xiàn)如下:</P>
<P align=justify>int CWnd::RunModalLoop(DWORD dwFlags)</P>
<P align=justify>{</P>
<P align=justify>ASSERT(::IsWindow(m_hWnd)); //窗口必須已經(jīng)創(chuàng)建且不在模式狀態(tài)
ASSERT(!(m_nFlags & WF_MODALLOOP)); </P>
<P align=justify></P>
<P align=justify>// 以下變量用于Idle處理</P>
<P align=justify>BOOL bIdle = TRUE;</P>
<P align=justify>LONG lIdleCount = 0;</P>
<P align=justify>BOOL bShowIdle = (dwFlags & MLF_SHOWONIDLE)
&& </P>
<P align=justify>!(GetStyle() & WS_VISIBLE);</P>
<P align=justify>HWND hWndParent = ::GetParent(m_hWnd);</P>
<P align=justify>m_nFlags |= (WF_MODALLOOP|WF_CONTINUEMODAL);</P>
<P align=justify>MSG* pMsg = &AfxGetThread()->m_msgCur;</P>
<P align=justify></P>
<P align=justify>//獲取和派發(fā)消息直到模式狀態(tài)結(jié)束</P>
<P align=justify>for (;;)</P>
<P align=justify>{</P>
<P align=justify>ASSERT(ContinueModal());</P>
<P align=justify></P>
<P align=justify>//第一階段,判斷是否可以進行Idle處理</P>
<P align=justify>while (bIdle &&!::PeekMessage(pMsg, NULL, NULL,
NULL, PM_NOREMOVE))</P>
<P align=justify>{</P>
<P align=justify>ASSERT(ContinueModal());</P>
<P align=justify></P>
<P align=justify>//必要的話,當Idle時顯示對話框窗口</P>
<P align=justify>if (bShowIdle)</P>
<P align=justify>{</P>
<P align=justify>ShowWindow(SW_SHOWNORMAL);</P>
<P align=justify>UpdateWindow();</P>
<P align=justify>bShowIdle = FALSE;</P>
<P align=justify>}</P>
<P align=justify></P>
<P align=justify>// 進行Idle處理</P>
<P align=justify>//必要的話發(fā)送WM_ENTERIDLE消息給父窗口</P>
<P align=justify>if (!(dwFlags & MLF_NOIDLEMSG) &&hWndParent
!= NULL && lIdleCount == 0)</P>
<P align=justify>{</P>
<P align=justify>::SendMessage(hWndParent, WM_ENTERIDLE, </P>
<P align=justify>MSGF_DIALOGBOX, (LPARAM)m_hWnd);</P>
<P align=justify>}</P>
<P align=justify>//必要的話發(fā)送WM_KICKIDLE消息給父窗口</P>
<P align=justify>if ((dwFlags & MLF_NOKICKIDLE) ||</P>
<P align=justify>!SendMessage(WM_KICKIDLE, MSGF_DIALOGBOX,
lIdleCount++))</P>
<P align=justify>{</P>
<P align=justify>//終止Idle處理</P>
<P align=justify>bIdle = FALSE;</P>
<P align=justify>}</P>
<P align=justify>}</P>
<P align=justify></P>
<P align=justify>//第二階段,發(fā)送消息</P>
<P align=justify>do</P>
<P align=justify>{</P>
<P align=justify>ASSERT(ContinueModal());</P>
<P align=justify></P>
<P align=justify>// 若是WM_QUIT消息,則發(fā)送該消息到消息隊列,返回;否則發(fā)送消息。</P>
<P align=justify>if (!AfxGetThread()->PumpMessage())</P>
<P align=justify>{</P>
<P align=justify>AfxPostQuitMessage(0);</P>
<P align=justify>return -1;</P>
<P align=justify>}</P>
<P align=justify></P>
<P align=justify>//必要的話,顯示對話框窗口</P>
<P align=justify>if (bShowIdle &&</P>
<P align=justify>(pMsg->message == 0x118 || pMsg->message ==
WM_SYSKEYDOWN))</P>
<P align=justify>{</P>
<P align=justify>ShowWindow(SW_SHOWNORMAL);</P>
<P align=justify>UpdateWindow();</P>
<P align=justify>bShowIdle = FALSE;</P>
<P align=justify>}</P>
<P align=justify></P>
<P align=justify>if (!ContinueModal())</P>
<P align=justify>goto ExitModal;</P>
<P align=justify></P>
<P align=justify>//在派發(fā)了“正常 ”消息后,重新開始Idle處理</P>
<P align=justify>if (AfxGetThread()->IsIdleMessage(pMsg))</P>
<P align=justify>{</P>
<P align=justify>bIdle = TRUE;</P>
<P align=justify>lIdleCount = 0;</P>
<P align=justify>}</P>
<P align=justify>} while (::PeekMessage(pMsg, NULL, NULL, NULL,
PM_NOREMOVE));</P>
<P align=justify>}</P>
<P align=justify></P>
<P align=justify>ExitModal:</P>
<P align=justify>m_nFlags &= ~(WF_MODALLOOP|WF_CONTINUEMODAL);</P>
<P align=justify>return m_nModalResult;</P>
<P align=justify>}</P>
<P align=justify></P>
<P align=justify>BOOL CWnd::ContinueModal()</P>
<P align=justify>{</P>
<P align=justify>return m_nFlags & WF_CONTINUEMODAL;</P>
<P align=justify>}</P>
<P align=justify></P>
<P align=justify>void CWnd::EndModalLoop(int nResult)</P>
<P align=justify>{</P>
<P align=justify>ASSERT(::IsWindow(m_hWnd));</P>
<P align=justify></P>
<P align=justify>// this result will be returned from
CWnd::RunModalLoop</P>
<P align=justify>m_nModalResult = nResult;</P>
<P align=justify></P>
<P align=justify>// make sure a message goes through to exit the modal
loop</P>
<P align=justify>if (m_nFlags & WF_CONTINUEMODAL)</P>
<P align=justify>{</P>
<P align=justify>m_nFlags &= ~WF_CONTINUEMODAL;</P>
<P align=justify>PostMessage(WM_NULL);</P>
<P align=justify>}</P>
<P align=justify>}</P>
<P
align=justify>和CWinThread::Run的處理過程比較,RunModalLoop也分兩個階段進行處理。不同之處在于,這里不同于Run的Idle處理,RunModalLoop是給父窗口發(fā)送WM_ENTERIDLE消息(如果需要的話);另外,當前對話框的父窗口被Disabled,是不接收用戶消息的。</P>
<P
align=justify>RunModalLoop是一個實現(xiàn)自己的消息循環(huán)的示例,消息循環(huán)的條件是模式化狀態(tài)沒有結(jié)束。實現(xiàn)線程自己的消息循環(huán)見8.5.6節(jié)。</P>
<P
align=justify>當用戶按下按鈕“取消”、“確定”時,將導(dǎo)致RunModalLoop退出消息循環(huán),結(jié)束對話框模式狀態(tài),并調(diào)用::EndDialog關(guān)閉窗口。有關(guān)關(guān)閉對話框的處理如下:</P>
<P align=justify>void CDialog::EndDialog(int nResult)</P>
<P align=justify>{</P>
<P align=justify>ASSERT(::IsWindow(m_hWnd));</P>
<P align=justify></P>
<P align=justify>if (m_nFlags & (WF_MODALLOOP|WF_CONTINUEMODAL))</P>
<P align=justify>EndModalLoop(nResult);</P>
<P align=justify></P>
<P align=justify>::EndDialog(m_hWnd, nResult);</P>
<P align=justify>}</P>
<P align=justify>void CDialog::OnOK()</P>
<P align=justify>{</P>
<P align=justify>if (!UpdateData(TRUE)) {</P>
<P align=justify>TRACE0("UpdateData failed during dialog
termination.\n");</P>
<P align=justify>// the UpdateData routine will set focus to correct
item</P>
<P align=justify>return;</P>
<P align=justify>}</P>
<P align=justify>EndDialog(IDOK);</P>
<P align=justify>}</P>
<P align=justify></P>
<P align=justify>void CDialog::OnCancel()</P>
<P align=justify>{</P>
<P align=justify>EndDialog(IDCANCEL);</P>
<P align=justify>}</P>
<P align=justify>上述函數(shù)OnOk、OnCancle、EndDialog都可以用來關(guān)閉對話框窗口。其中:</P>
<P align=justify>OnOk首先進行數(shù)據(jù)交換,獲取對話框中各個控制子窗口的數(shù)據(jù),然后調(diào)用EndDialog結(jié)束對話框。</P>
<P align=justify>OnCancle直接EndDialog結(jié)束對話框。</P>
<P
align=justify>EndDialog首先修改m_nFlag的值,表示結(jié)束模式循環(huán),然后調(diào)用::EndDialog關(guān)閉對話框窗口。</P>
<P align=justify></P>
<LI><A name=_Toc452641002></A><A name=_Toc457299124></A><B>對話框的數(shù)據(jù)交換</B>
<P></P>
<P
align=justify>對話框數(shù)據(jù)交換指以下兩種動作,或者是把內(nèi)存數(shù)據(jù)寫入對應(yīng)的控制窗口,或者是從控制窗口讀取數(shù)據(jù)并保存到內(nèi)存變量中。MFC為了簡化這些操作,以CDataExchange類和一些數(shù)據(jù)交換函數(shù)為基礎(chǔ),提供了一套數(shù)據(jù)交換和校驗的機制。</P>
<OL>
<P align=justify>
<LI><A name=_Toc457299125></A><B>數(shù)據(jù)交換的方法</B>
<P></P>
<P
align=justify>首先,定義保存數(shù)據(jù)的內(nèi)存變量──給對話框添加成員變量,每個控制窗口可以對應(yīng)一個成員變量,或者是控制窗口類型,或者是控制窗口表示的數(shù)據(jù)的類型。例如,對于對話框的一個編輯控制窗口,可以定義一個CEdit類型的成員變量,或者一個CString類型的成員變量。</P>
<P align=justify>其次,覆蓋對話框的虛擬函數(shù)DoDataExchange,實現(xiàn)數(shù)據(jù)交換和驗證。</P>
<P
align=justify>ClassWizard可以協(xié)助程序員自動地添加成員變量,修改DoDataExchange。例如,一個對話框有兩個控制窗口,其中的一個編輯框表示姓名,ID是IDC_NAME,另一個編輯框表示年齡,ID是IDC_AGE,ClassWizard添加如下的成員變量:</P>
<P align=justify>// Dialog Data</P>
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -