📄 mfc12.php
字号:
<LI><A name=_Toc457299122></A><B>消息预处理和Dialog消息</B>
<P></P>
<P
align=justify>另外,对话框窗口的消息处理还有一个特点,就是增加了对Dialog消息的处理,如同在介绍::IsDialogMessage函数时所述。如果是Dialog消息,MFC框架将不会让它进入下一步的消息循环。为此,MFC覆盖了CDialog的虚拟函数PreTranslateMessage,该函数的实现如下:</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帮助模式下,不转换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>从其实现可以看出,如果是Tooltip消息或者Dialog消息,这些消息将在PreTranslateMessage中被处理,不会进入消息发送的处理。</P>
<P
align=justify>PreTranslateInput是CWnd的成员函数,它调用::IsDialogMessage函数来处理Dialog消息。</P>
<P align=justify>PreTranslateMessage的实现不仅用于模式对话框,而且用于无模式对话框。</P>
<P align=justify></P>
<LI><A name=_Toc457299123></A><B>模式对话框的消息循环</B>
<P></P></LI></OL>
<P
align=justify>从DoModal的实现可以看出,DoModal调用CreateDlgIndirect创建的是无模式对话框,MFC如何来接管和控制应用程序的消息队列,实现一个模式对话框的功能呢?</P>
<P
align=justify>CDialog调用了RunModalLoop来实现模式窗口的消息循环。RunModalLoop是CWnd的成员函数,它和相关函数的实现如下:</P>
<P align=justify>int CWnd::RunModalLoop(DWORD dwFlags)</P>
<P align=justify>{</P>
<P align=justify>ASSERT(::IsWindow(m_hWnd)); //窗口必须已经创建且不在模式状态
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>//获取和派发消息直到模式状态结束</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>//必要的话发送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>//必要的话发送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>//第二阶段,发送消息</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消息,则发送该消息到消息队列,返回;否则发送消息。</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>//在派发了“正常 ”消息后,重新开始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是给父窗口发送WM_ENTERIDLE消息(如果需要的话);另外,当前对话框的父窗口被Disabled,是不接收用户消息的。</P>
<P
align=justify>RunModalLoop是一个实现自己的消息循环的示例,消息循环的条件是模式化状态没有结束。实现线程自己的消息循环见8.5.6节。</P>
<P
align=justify>当用户按下按钮“取消”、“确定”时,将导致RunModalLoop退出消息循环,结束对话框模式状态,并调用::EndDialog关闭窗口。有关关闭对话框的处理如下:</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>上述函数OnOk、OnCancle、EndDialog都可以用来关闭对话框窗口。其中:</P>
<P align=justify>OnOk首先进行数据交换,获取对话框中各个控制子窗口的数据,然后调用EndDialog结束对话框。</P>
<P align=justify>OnCancle直接EndDialog结束对话框。</P>
<P
align=justify>EndDialog首先修改m_nFlag的值,表示结束模式循环,然后调用::EndDialog关闭对话框窗口。</P>
<P align=justify></P>
<LI><A name=_Toc452641002></A><A name=_Toc457299124></A><B>对话框的数据交换</B>
<P></P>
<P
align=justify>对话框数据交换指以下两种动作,或者是把内存数据写入对应的控制窗口,或者是从控制窗口读取数据并保存到内存变量中。MFC为了简化这些操作,以CDataExchange类和一些数据交换函数为基础,提供了一套数据交换和校验的机制。</P>
<OL>
<P align=justify>
<LI><A name=_Toc457299125></A><B>数据交换的方法</B>
<P></P>
<P
align=justify>首先,定义保存数据的内存变量──给对话框添加成员变量,每个控制窗口可以对应一个成员变量,或者是控制窗口类型,或者是控制窗口表示的数据的类型。例如,对于对话框的一个编辑控制窗口,可以定义一个CEdit类型的成员变量,或者一个CString类型的成员变量。</P>
<P align=justify>其次,覆盖对话框的虚拟函数DoDataExchange,实现数据交换和验证。</P>
<P
align=justify>ClassWizard可以协助程序员自动地添加成员变量,修改DoDataExchange。例如,一个对话框有两个控制窗口,其中的一个编辑框表示姓名,ID是IDC_NAME,另一个编辑框表示年龄,ID是IDC_AGE,ClassWizard添加如下的成员变量:</P>
<P align=justify>// Dialog Data</P>
<P align=justify>//{{AFX_DATA(CExDialog)</P>
<P align=justify>enum { IDD = IDD_DIALOG2 };</P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -