首页 > 代码库 > 基于ATL的控件中使用加速键(AtlLoadAccelerators)
基于ATL的控件中使用加速键(AtlLoadAccelerators)
控件基本情况
控件本身+ CMainFrame + View
将所有的控件消息全部转发给CMainFrame来处理
BEGIN_MSG_MAP(CIECSChart) //CHAIN_MSG_MAP(CComControl<CIECSChart>) MESSAGE_HANDLER(MSG_SELECTION_CHANGED, MessageHandler) CHAIN_MSG_MAP(_Base) { MSG msg = { hWnd, uMsg, wParam, lParam }; if(!CMainFrame::PreTranslateMessage(&msg)) { CHAIN_MSG_MAP(CMainFrame) } } DEFAULT_REFLECTION_HANDLER() END_MSG_MAP()
再看处理加速键的部分
<pre name="code" class="cpp">STDMETHOD(TranslateAccelerator)(LPMSG pMsg){/*HACCEL hAccel;hAccel = AtlLoadAccelerators(MAKEINTRESOURCE(IDR_IECSCHART));if(WM_KEYFIRST<=pMsg->message && pMsg->message <= WM_KEYLAST){if(hAccel && ::TranslateAccelerator(m_hWnd,hAccel,pMsg))return TRUE;}*/return CMainFrame::PreTranslateMessage(pMsg) ? S_OK : S_FALSE;}
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) { /*if(pMsg->wParam == VK_DELETE) { if(m_hAccel != NULL && ::TranslateAccelerator(m_hWnd, m_hAccel, pMsg)) return TRUE; }*/ if (_Base::PreTranslateMessage(pMsg)) return TRUE; if (mapView.PreTranslateMessage(pMsg)) return TRUE; // allow typical message processing return FALSE; }
经过实际测试,在CMainFrame::PreTranslateMessage中可以接收到VK_DELETE消息,但收不到转化后的COMMAND消息。
再看一个函数:
TranslateAccelerator
TranslateAccelerator,函数功能:翻译加速键表。该函数处理菜单命令中的加速键。该函数将一个WM_KEYDOWN或WM_SYSKEYDOWN消息翻译成一个WM_COMMAND或WM_SYSCOMMAND消息(如果在给定的加速键表中有该键的入口),然后将WM_COMMAND或WM_SYSCOMMAND消息直接送到相应的窗口处理过程。综合以上分析应该是将消息转成COMMAND的时候失败了。
转换的时候需要一个转换表,其实就是在资源里面定义的加速键表,可以采用以下方式进行处理:
HACCEL hAccel; hAccel = AtlLoadAccelerators(MAKEINTRESOURCE(IDR_IECSCHART)); if(WM_KEYFIRST<=pMsg->message && pMsg->message <= WM_KEYLAST) { if(hAccel && ::TranslateAccelerator(m_hWnd,hAccel,pMsg)) return TRUE; }
但在基于ATL/WTL的应用程序里,我们都知道,根本不需要这样显式的加载,那么究竟差别在哪里呢?
下面要看一下atlframe.h中的一个函数
HWND CreateEx(HWND hWndParent = NULL, ATL::_U_RECT rect = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, LPVOID lpCreateParam = NULL) { const int cchName = 256; TCHAR szWindowName[cchName]; szWindowName[0] = 0; #ifndef _WIN32_WCE ::LoadString(ModuleHelper::GetResourceInstance(), T::GetWndClassInfo().m_uCommonResourceID, szWindowName, cchName); HMENU hMenu = ::LoadMenu(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID)); #else // CE specific ::LoadString(ModuleHelper::GetResourceInstance(), T::GetWndClassInfo().m_uCommonResourceID, szWindowName, cchName); // This always needs to be NULL for Windows CE. // Frame Window menus have to go onto the CommandBar. // Use CreateSimpleCECommandBar HMENU hMenu = NULL; #endif // _WIN32_WCE T* pT = static_cast<T*>(this); HWND hWnd = pT->Create(hWndParent, rect, szWindowName, dwStyle, dwExStyle, hMenu, lpCreateParam); if(hWnd != NULL) m_hAccel = ::LoadAccelerators(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID)); return hWnd; }
注意到最后调用了LoadAccelerators,那为什么在控件里却没有调用呢?
这个其实是由应用程序负责调用的方法,然后CMainFrame的OnCreate被调用的时候,窗口已经创建完了,但与在应用程序里的方式不一样了,即在控件里CreateEx没有调用,也就是说m_hAccel其实是空的,那么TranslateAccelerator当然没法转了。
解决方案
分析清楚了之后,其实就是要调用一下AtlLoadAccelerators,把m_hAccel初始化一下就行了,当然最合适的地方就是OnCreate函数里:
LRESULT CMainFrame::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { if(m_hWnd != NULL) { //m_hAccel = ::LoadAccelerators(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(::GetWndClassInfo().m_uCommonResourceID)); m_hAccel = AtlLoadAccelerators(MAKEINTRESOURCE(IDR_IECSCHART)); } m_hWndClient = mapView.Create(*this, NULL, NULL, WS_CHILD | WS_VISIBLE, WS_EX_STATICEDGE); PostMessage(MSG_POSTCREATE); return 0; }
其他事项
转换后,原消息是否被丢弃呢?
经测试,原消息并没有被丢弃,也就是说在CMainFrame::PreTranslateMessage中可以收到两个消息:一个是原始的VK_DELETE消息,另一个是转换后的COMMAND
基于ATL的控件中使用加速键(AtlLoadAccelerators)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。