📄 mfc13.php
字号:
<P align=justify>ASSERT(m_nIDTracking >= AFX_IDS_SCFIRST
&&</P>
<P align=justify>m_nIDTracking < AFX_IDS_SCFIRST + 31);</P>
<P align=justify>}</P>
<P align=justify>else if (nItemID >= AFX_IDM_FIRST_MDICHILD)</P>
<P align=justify>{</P>
<P align=justify>//如果选中的菜单项表示一个MDI子窗口</P>
<P align=justify>m_nIDTracking = AFX_IDS_MDICHILD;</P>
<P align=justify>}</P>
<P align=justify>else</P>
<P align=justify>{</P>
<P align=justify>//选中了一个菜单项</P>
<P align=justify>m_nIDTracking = nItemID;</P>
<P align=justify>}</P>
<P align=justify>pFrameWnd->m_nFlags |= WF_NOPOPMSG;</P>
<P align=justify>}</P>
<P align=justify></P>
<P align=justify>// when running in-place, it is necessary to cause a
message to</P>
<P align=justify>// be pumped through the queue.</P>
<P align=justify>if (m_nIDTracking != m_nIDLastMessage &&
GetParent() != NULL)</P>
<P align=justify>PostMessage(WM_KICKIDLE);</P>
<P align=justify>}</P>
<P
align=justify>OnMenuSelect的作用在于跟踪当前选中的菜单项,把菜单项对应的ID保存在CFrameWnd的成员变量m_nIDTracking中。</P>
<P align=justify>如果菜单项没有选中,或者选中的是一个子菜单,则设置nIDTracking为0。</P>
<P align=justify>如果选中的是系统菜单,则把系统菜单ID转换成一个对应的命令ID;保存该值到nIDTracking中。</P>
<P
align=justify>如果选中的菜单是MDI子窗口创建时添加的(用来表示MDI子窗口),则转换菜单ID为AFX_IDS_MDICHILD,所有对应MDI子窗口的菜单项都使用AFX_IDS_MDICHILD,保存该值到nIDTracking中。</P>
<P align=justify>其他情况,就是选中菜单项的ID,把它保存到nIDTracking中。</P>
<P align=justify></P>
<P
align=justify>跟踪被选择的菜单项并保存其ID在m_nIDTracking中,OnEnterIdle将用到m_nIDTracking。OnEnterIlde是消息WM_ENTERIDLE的处理函数,CFrameWnd的实现如下。</P>
<P align=justify>void CFrameWnd::OnEnterIdle(UINT nWhy, CWnd* pWho)</P>
<P align=justify>{</P>
<P align=justify>CWnd::OnEnterIdle(nWhy, pWho);</P>
<P align=justify></P>
<P align=justify>//若不是因为菜单选择进入该函数</P>
<P align=justify>//或者当前跟踪到的菜单项ID是最近一次处理的,则返回</P>
<P align=justify>if (nWhy != MSGF_MENU || m_nIDTracking ==
m_nIDLastMessage)</P>
<P align=justify>return;</P>
<P align=justify></P>
<P align=justify>//将发送消息WM_SETMESSAGETEXT</P>
<P align=justify>//在状态栏显示m_nIDTracking对应的字符串</P>
<P align=justify>SetMessageText(m_nIDTracking);</P>
<P align=justify>ASSERT(m_nIDTracking == m_nIDLastMessage);</P>
<P align=justify>}</P>
<P
align=justify>当一个对话框或者菜单被显示的时候,Windows发送WM_ENTERIDLE消息。消息参数wParam取值为MSGF_DIALOGBOX或者MSGF_MENU。前者表示显示对话框时发送该消息,这时消息参数lParam表示对话框的句柄;后者表示显示菜单时发送该消息,这时消息参数lParam表示菜单的句柄。</P>
<P
align=justify>经过消息映射,wParam的值传递给OnEnterIdle的参数nWhy,参数lParam的值传给参数who。如果参数1取值为MSGF_MENU,并且OnEnterIdle最近一次在菜单显示被调用时的菜单ID不同于这一次,则调用SetMessageText在状态栏显示对应ID命令的字符串,并且记录当前菜单ID到变量m_nIDTracking中(见消息处理函数OnSetMessageText)。</P>
<P
align=justify>这样,在菜单选择期间,用户选择的菜单项ID被OnMenuSelect记录,在消息WM_ENTERIDLE处理时在状态栏显示ID命令的提示。</P>
<P align=justify></P>
<LI><A name=_Toc457299149></A><B>控制条的消息分发处理</B>
<P></P></LI></OL></OL></OL></OL>
<P
align=justify>工具条(包括对话框工具条)是一个子窗口,它们可以响应各种消息。如果按标准的Windows消息和命令消息的分发途径,一些消息不能送到拥有工具条的边框窗口,因为这些消息都将被工具条(对话框工具条)处理掉。所以,CControlBar覆盖了虚拟函数PreTranslateMessage和WindowProc以便实现特定的消息分发路径。</P>
<OL>
<P align=justify>
<LI>WindowProc
<P></P>
<P align=justify>CControlBar 的WindowProc实现了如下的消息分发路径:</P>
<P
align=justify>用户对控制条的输入消息或者分发给CControlBar及其派生类处理,或者送给拥有控制条的边框窗口处理,或者送给Windows控制“窗口类”的窗口过程处理。</P>
<P align=justify>WindowProc的实现如下:</P>
<P align=justify>LRESULT CControlBar::WindowProc(UINT nMsg, </P>
<P align=justify>WPARAM wParam, LPARAM lParam)</P>
<P align=justify>{</P>
<P align=justify>ASSERT_VALID(this);</P>
<P align=justify></P>
<P align=justify>LRESULT lResult;</P>
<P align=justify>switch (nMsg)</P>
<P align=justify>{</P>
<P align=justify>//本函数处理以下消息</P>
<P align=justify>case WM_NOTIFY:</P>
<P align=justify>case WM_COMMAND:</P>
<P align=justify>case WM_DRAWITEM:</P>
<P align=justify>case WM_MEASUREITEM:</P>
<P align=justify>case WM_DELETEITEM:</P>
<P align=justify>case WM_COMPAREITEM:</P>
<P align=justify>case WM_VKEYTOITEM:</P>
<P align=justify>case WM_CHARTOITEM:</P>
<P align=justify>//首先,工具条处理上述消息,如果没有处理,则接着给所属边框窗口处理</P>
<P align=justify>if (OnWndMsg(nMsg, wParam, lParam, &lResult))</P>
<P align=justify>return lResult;</P>
<P align=justify>else</P>
<P align=justify>return GetOwner()->SendMessage(nMsg, wParam, lParam);</P>
<P align=justify>}</P>
<P align=justify>}</P>
<P align=justify>// 最后,给基类CWnd,按缺省方式处理</P>
<P align=justify>lResult = CWnd::WindowProc(nMsg, wParam, lParam);</P>
<P align=justify>return lResult;</P>
<P align=justify>}</P>
<P
align=justify>从上述实现可以看出,对于case范围内的一些消息,如WM_COMMAND、WM_NOTIFY等,控制条如果不能处理,则优先分发给其父窗口(边框窗口)处理,然后进入缺省处理,对于其他消息直接调用基类CWnd的实现(缺省处理)。基于这样的机制,可以把用户对工具条按钮或者对话框工具条内控制的操作解释成相应的命令消息,执行对应的命令处理。</P>
<P
align=justify>对于工具条,当用户选中某个按钮时(鼠标左键弹起,消息是WM_LBUTTONUP),工具条窗口接收到WM_LBUTTONUP消息,该消息不在CControlBar::WindowProc特别处理的消息范围内,于是进行缺省处理,也就是说,把该消息派发给控制条对应的Windows控制的窗口过程处理(即被MFC统一窗口过程所取代的原窗口过程),该窗口过程则把该消息转换成一条命令消息WM_COMMAND,命令ID就是选中按钮对应的ID,然后,发送该命令消息给拥有工具条的边框窗口,导致相应的命令处理函数被调用。</P>
<P
align=justify>对于对话框工具条,当工具条的某个控制子窗口被选中之后,则产生一条命令通知消息WM_COMMAND,wParam是控制子窗口的ID。CControlBar::WindowProc处理该消息。WindowProc首先调用OnWndMsg把消息发送给对话框工具条或者对话框工具条的基类处理,如果没有被处理的话,则OnWndMsg返回FALSE。接着,WindowPoc把命令消息传递给父窗口(边框窗口)处理。由于工具条的控制窗口的ID对应的是命令ID,所以,这条WM_COMMAND消息传递给边框窗口时,被解释成一个命令消息,相应的命令处理函数被调用。</P>
<P align=justify></P>
<LI>PreTranslateMessage
<P></P></LI></OL>
<P
align=justify>CControlBar覆盖PreTranslateMessage函数,主要是为了在光标落在工具条按钮上时显示FLYBY信息,并且让对话框工具条过滤Dialog消息。</P>
<P align=justify>BOOL CControlBar::PreTranslateMessage(MSG* pMsg)</P>
<P align=justify>{</P>
<P align=justify>ASSERT_VALID(this);</P>
<P align=justify>ASSERT(m_hWnd != NULL);</P>
<P align=justify></P>
<P align=justify>//过滤Tooltip消息</P>
<P align=justify>if (CWnd::PreTranslateMessage(pMsg))</P>
<DIR>
<P align=justify>return TRUE; //是Tooltip消息,已经被处理</P>
<P align=justify></P></DIR>
<P align=justify>UINT message = pMsg->message;</P>
<P align=justify>//控制条的父窗口,对工具条和对话框工具条,总是创建它的边框窗口</P>
<P align=justify>CWnd* pOwner = GetOwner();</P>
<P align=justify></P>
<P align=justify>//必要的话,在状态条显示工具栏按钮的提示</P>
<P align=justify>if (((m_dwStyle & CBRS_FLYBY) ||</P>
<DIR>
<P align=justify>message == WM_LBUTTONDOWN || message == WM_LBUTTONUP)
&&</P>
<P align=justify>((message >= WM_MOUSEFIRST && message <=
WM_MOUSELAST) ||</P>
<P align=justify>(message >= WM_NCMOUSEFIRST && </P>
<DIR>
<P align=justify>message <= WM_NCMOUSELAST)))</P></DIR></DIR>
<P align=justify>{</P>
<DIR>
<P align=justify>_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();</P>
<P align=justify></P>
<P align=justify>//确认鼠标在工具栏的哪个按钮上</P>
<P align=justify>CPoint point = pMsg->pt;</P>
<P align=justify>ScreenToClient(&point);</P>
<P align=justify>TOOLINFO ti; memset(&ti, 0, sizeof(TOOLINFO));</P>
<P align=justify>ti.cbSize = sizeof(TOOLINFO);</P>
<P align=justify>int nHit = OnToolHitTest(point, &ti);</P>
<P align=justify>if (ti.lpszText != LPSTR_TEXTCALLBACK)</P>
<DIR>
<P align=justify>free(ti.lpszText);</P></DIR>
<P align=justify>BOOL bNotButton =</P>
<DIR>
<P align=justify>message == WM_LBUTTONDOWN && (ti.uFlags &
TTF_NOTBUTTON);</P></DIR>
<P align=justify>if (message != WM_LBUTTONDOWN &&
GetKeyState(VK_LBUTTON) < 0)</P>
<DIR>
<P align=justify>nHit = pThreadState->m_nLastStatus;</P>
<P align=justify></P></DIR>
<P align=justify>//更新状态栏的提示信息</P>
<P align=justify>if (nHit < 0 || bNotButton)</P>
<P align=justify>{</P>
<DIR>
<P align=justify>if (GetKeyState(VK_LBUTTON) >= 0 || bNotButton)</P>
<P align=justify>{</P>
<P align=justify>SetStatusText(-1);</P>
<P align=justify>KillTimer(ID_TIMER_CHECK);</P>
<P align=justify>}</P></DIR>
<P align=justify>}</P>
<P align=justify>else</P>
<P align=justify>{</P>
<DIR>
<P align=justify>if (message == WM_LBUTTONUP)</P>
<P align=justify>{</P>
<DIR>
<DIR>
<P align=justify>SetStatusText(-1);</P>
<P align=justify>ResetTimer(ID_TIMER_CHECK, 200);</P></DIR></DIR>
<P align=justify>}</P></DIR>
<P align=justify>else</P>
<P align=justify>{</P>
<DIR>
<P align=justify>if ((m_nStateFlags & statusSet) || GetKeyState(VK_LBUTTON)
< 0)</P>
<P align=justify>SetStatusText(nHit);</P></DIR>
<P align=justify>else if (nHit != pThreadState->m_nLastStatus)</P>
<DIR>
<P align=justify>ResetTimer(ID_TIMER_WAIT, 300);</P></DIR>
<P align=justify>}</P></DIR>
<P align=justify>}</P>
<P align=justify>pThreadState->m_nLastStatus = nHit;</P>
<P align=justify>}</P>
<P align=justify></P>
<P align=justify>// don't translate dialog messages when in Shift+F1 help
mode</P>
<P align=justify>CFrameWnd* pFrameWnd = GetTopLevelFrame();</P>
<P align=justify>if (pFrameWnd != NULL && pFrameWnd->m_bHelpMode)</P>
<DIR>
<P align=justify>return FALSE;</P>
<P align=justify></P></DIR>
<P align=justify>//在IsDialogMessage之前调用边框窗口的PreTranslateMessage,</P>
<P align=justify>//给边框窗口一个处理快捷键的机会</P>
<P align=justify>while (pOwner
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -