按照如下方式传递消息:
首先是主窗口的回调函数,然后是主窗口的HandleMessage,最后是调用CPaintManagerUI::MessageHandler处理界面及其子控件
1.LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2.LRESULT WindowImplBase::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
3.bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes)
消息循环如下:获取消息,发放消息
UINT CWindowWnd::ShowModal()
{
ASSERT(::IsWindow(m_hWnd));
UINT nRet = 0;
HWND hWndParent = GetWindowOwner(m_hWnd);
::ShowWindow(m_hWnd, SW_SHOWNORMAL);
::EnableWindow(hWndParent, FALSE);
MSG msg = { 0 };
while( ::IsWindow(m_hWnd) && ::GetMessage(&msg, NULL, 0, 0) ) {
if( msg.message == WM_CLOSE && msg.hwnd == m_hWnd ) {
nRet = msg.wParam;
::EnableWindow(hWndParent, TRUE);
::SetFocus(hWndParent);
}
if( !CPaintManagerUI::TranslateMessage(&msg) ) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
if( msg.message == WM_QUIT ) break;
}
::EnableWindow(hWndParent, TRUE);
::SetFocus(hWndParent);
if( msg.message == WM_QUIT ) ::PostQuitMessage(msg.wParam);
return nRet;
}
sendnotify
1.发送消息
m_pManager->SendNotify(this, DUI_MSGTYPE_CLICK);
2.注册消息函数
DUI_BEGIN_MESSAGE_MAP(WindowImplBase, CNotifyPump)
DUI_ON_MSGTYPE(DUI_MSGTYPE_CLICK,OnClick)
DUI_END_MESSAGE_MAP()
3.消息响应函数
void WindowImplBase::OnClick(TNotifyUI& msg)
{
CDuiString sCtrlName = msg.pSender->GetName();
if( sCtrlName == _T("closebtn") ) {
Close();
return;
}
else if( sCtrlName == _T("minbtn")) {
SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0);
return;
}
else if( sCtrlName == _T("maxbtn")) {
SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, 0);
return;
}
else if( sCtrlName == _T("restorebtn")) {
SendMessage(WM_SYSCOMMAND, SC_RESTORE, 0);
return;
}
return;
}
// Structure for notifications to the outside world
typedef struct tagTNotifyUI
{
CDuiString sType;//操作类型
CDuiString sVirtualWnd;//虚拟窗口
CControlUI* pSender;//控件发送者
DWORD dwTimestamp;//发送时间
POINT ptMouse;//鼠标位置
WPARAM wParam;//参数1
LPARAM lParam;//参数2
} TNotifyUI;
看下CWindowWnd类与CPaintManagerUI类是咋进行消息分发的吧.
// Structure for notifications from the system
// to the control implementation.//系统向控件发送的消息
typedef struct UILIB_API tagTEventUI
{
int Type;//类型
CControlUI* pSender;//发送的控件
DWORD dwTimestamp;//发送时间
POINT ptMouse;//鼠标位置
TCHAR chKey;
WORD wKeyState;
WPARAM wParam;
LPARAM lParam;
} TEventUI;
case WM_TIMER:
{
for( int i = 0; i < m_aTimers.GetSize(); i++ ) {
const TIMERINFO* pTimer = static_cast<TIMERINFO*>(m_aTimers[i]);
if(pTimer->hWnd == m_hWndPaint &&
pTimer->uWinTimer == LOWORD(wParam) &&
pTimer->bKilled == false)
{
TEventUI event = { 0 };
event.Type = UIEVENT_TIMER;
event.pSender = pTimer->pSender;
event.dwTimestamp = ::GetTickCount();
event.ptMouse = m_ptLastMousePos;
event.wKeyState = MapKeyState();
event.wParam = pTimer->nLocalID;
event.lParam = lParam;
pTimer->pSender->Event(event);
break;
}
}
}
1. 先看下CPaintManagerUI类的MessageLoop函数:
[cpp] view plaincopyprint?
- void CPaintManagerUI::MessageLoop()
- {
- MSG msg = { 0 };
- while( ::GetMessage(&msg, NULL, 0, 0) ) { // 获取消息
- if( !CPaintManagerUI::TranslateMessage(&msg) ) { // 消息过滤
- ::TranslateMessage(&msg);
- ::DispatchMessage(&msg); // 分发到窗口的消息处理窗口中. 也就是调用CWindowWnd类的__WndProc函数或是__ControlProc函数.
- }
- }
- }
消息第一次会由CPaintManagerUI类的TranslateMessage消息接收到.
2. 调用CWindowWnd::Create创建窗口. 完成以下操作:
1) 如果要子类下Window的控件(就是系统的控件, 而不是duilib的模拟控件), 就设置__ControlProc函数为消息回调函数.
2)不子类化, 就注册窗口类. 此时设置__WndProc为窗口消息处理回调函数.
3)用CreateWindowEx API函数创建窗口.
这里先不看子类化相关的, 我要先看明白标准的窗口创建过程. 这也操作后消息就会分发到__WndProc了,
3. 看下__WndProc函数的定义:
[cpp] view plaincopyprint?
- LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
- {
- CWindowWnd* pThis = NULL;
- if( uMsg == WM_NCCREATE ) { // 要在此消息中把类于窗口进行绑定
- LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam); // 来自于CreateWindowEx函数的最后一个参数( 也就是CWindowWnd对象指针了 )
- pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);
- pThis->m_hWnd = hWnd;
- ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis)); // 设置到窗口的用户数据中
- }
- else {
- pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
- if( uMsg == WM_NCDESTROY && pThis != NULL ) {
- LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam); // 收到窗口能处理到的最后一个消息了, 要进行收尾工作了.
- ::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L); // 取消类对象与窗口的绑定关系
- if( pThis->m_bSubclassed ) pThis->Unsubclass();
- pThis->m_hWnd = NULL;
- pThis->OnFinalMessage(hWnd);
- return lRes;
- }
- }
- if( pThis != NULL ) {
- return pThis->HandleMessage(uMsg, wParam, lParam); // 在此调用继承类的消息处理函数
- }
- else {
- return ::DefWindowProc(hWnd, uMsg, wParam, lParam); // 未绑定类对象, 就调用默认的窗口消息处理函数
- }
- }
消息第二次就由__WndProc接收到, 然后再传到CWindowWnd类的HandlerMessage函数中.
3. 看看CWindowWnd类的继承类对于HandlerMessage虚函数的实现.
[cpp] view plaincopyprint?
- LRESULT CMainWnd::HandleMessage( UINT uMsg, WPARAM wParam, LPARAM lParam )
- {
- LRESULT lRes = 0; // 消息处理返回值.
- BOOL bHandled = TRUE; // 消息是否要继续往下传.
- switch ( uMsg )
- {
- case WM_CREATE: lRes = OnInitResource( bHandled ); break; // 进行初始化工作. 比如最重要的XML加载解析工作.
- default:
- bHandled = FALSE;
- }
- if ( bHandled )
- {
- return lRes;
- }
- if ( m_pm.MessageHandler( uMsg, wParam, lParam, lRes ) ) // 传给CPaintManagerUI::MessageHandler函数进行具体的控件处理工作
- {
- return lRes;
- }
- return CWindowWnd::HandleMessage( uMsg, wParam, lParam ); // 没处理过的就调用CWindowWnd类的默认消息处理函数吧.
- }
在这里就是用户要按消息进行具体的处理了. 之后要传到CPaintManagerUI类对象的MessageHandler函数. 未处理的消息就要返回给CWindowWnd类的默认消息处理函数来处理了.
4. CPaintManagerUI类的TranslateMessage, MessageHandler函数的内容.
[cpp] view plaincopyprint?
- BOOL CPaintManagerUI::TranslateMessage(const LPMSG pMsg)
- {
- HWND hwndParent = ::GetParent(pMsg->hwnd); // 获取消息接收窗口的父窗口
- UINT uStyle = GetWindowStyle(pMsg->hwnd); // 获取窗口的样式
- LRESULT lRes = 0;
- for( int i = 0; i < m_aPreMessages.GetSize(); i++ ) { // 这个m_aPreMessage保存着CPaintManagerUI类对象.
- CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]);
- if( pMsg->hwnd == pT->GetPaintWindow() // 消息是否属于当前CPaintManagerUI绑定的窗口
- || (hwndParent == pT->GetPaintWindow() && ((uStyle & WS_CHILD) != 0)) ) // 消息是否为当前窗口中窗口的消息, (如ActiveX控件 )
- {
- if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) return TRUE; // 此时就调用PreMessageHandler过滤函数.
- }
- }
- return FALSE;
- }
m_aPreMessage为静态成员变量, 在CPaintManagerUI::Init进行窗口与此类绑定时添加到此变量中.
5. CPaintManagerUI::PreMessageHandler消息过滤函数.
[cpp] view plaincopyprint?
- BOOL CPaintManagerUI::PreMessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& /*lRes*/)
- {
- // 遍历当前的消息过滤列表. m_aPreMessageFilter的元素为IMessageFilterUI接口.只一个虚函数MessageHandler.
- // 用户可以添加此接口的继承类变量到m_aPreMessageFilters列表中. ( 调用AddMessageFilter函数实现 )
- for( int i = 0; i < m_aPreMessageFilters.GetSize(); i++ )
- {
- BOOL bHandled = FALSE;
- LRESULT lResult = static_cast<IMessageFilterUI*>(m_aPreMessageFilters[i])->MessageHandler(uMsg, wParam, lParam, bHandled);
- if( bHandled ) {
- return TRUE;
- }
- }
- // 以下是对几个按键消息的过滤.
- // WM_KEYDOWN 检查是否为VK_TAB键, 要进行控件焦点的移动.
- // WM_SYSCHAR 获取与wParam中的字符加速键匹配的控件, 并激活它.
- // WM_SYSKEYDOWN 生成控件事件( 用TEventUI来模拟 )
- }
5. CPaintManagerUI::MessageHandler函数.
1) 遍历m_aMessageFilters列表中的IMessageFilterUI接口, 并调用MessageHandler函数, 再次进行相关的消息过滤功能.(与上面的m_aPreMessageFilters类似)
2) 在此会处理窗口的WM_PAINT消息. 显示所有控件的外观与状态.
3) 处理鼠标事件, 实现控件激活和相关事件.
4) 处理WM_TIMER消息, 所有控件要用CPaintManagerUI的SetTimer, KillTimer等函数实现计时器功能.
5) 处理CPaintManagerUI类的自定消息, WM_APP + 1与 +2,
WM_APP + 1是用于控件延迟销毁控件对象
WM_APP + 2销毁异步消息的处理.
( 异步控件消息用CPaintManagerUI::SendNotify函数, 把消息对象添加到m_aAsyncNotify列表中, 再PostMessage函数WM_APP + 2 )
5) 其它基本的窗口相关消息的处理.
CPaintManagerUI把DUILIB内部的事件都是用TEventUI结构的形式调用CControlUI类的Event函数来投递的.
Duilib类似MFC的消息响应如下:
DUI_DECLARE_MESSAGE_MAP()
DUI_BEGIN_MESSAGE_MAP(CYmChatUI, CXMLWnd)
DUI_ON_MSGTYPE(DUI_MSGTYPE_CLICK, OnClick)
DUI_END_MESSAGE_MAP()
绑定消息响应函数:
#define DUI_MSGTYPE_MENU (_T("menu"))
#define DUI_MSGTYPE_LINK (_T("link"))
#define DUI_MSGTYPE_TIMER (_T("timer"))
#define DUI_MSGTYPE_CLICK (_T("click"))
#define DUI_MSGTYPE_RETURN (_T("return"))
#define DUI_MSGTYPE_SCROLL (_T("scroll"))
#define DUI_MSGTYPE_DROPDOWN (_T("dropdown"))
#define DUI_MSGTYPE_SETFOCUS (_T("setfocus"))
#define DUI_MSGTYPE_KILLFOCUS (_T("killfocus"))
#define DUI_MSGTYPE_ITEMCLICK (_T("itemclick"))
#define DUI_MSGTYPE_TABSELECT (_T("tabselect"))
#define DUI_MSGTYPE_ITEMSELECT (_T("itemselect"))
#define DUI_MSGTYPE_ITEMEXPAND (_T("itemexpand"))
#define DUI_MSGTYPE_WINDOWINIT (_T("windowinit"))
#define DUI_MSGTYPE_BUTTONDOWN (_T("buttondown"))
#define DUI_MSGTYPE_MOUSEENTER (_T("mouseenter"))
#define DUI_MSGTYPE_MOUSELEAVE (_T("mouseleave"))
#define DUI_MSGTYPE_TEXTCHANGED (_T("textchanged"))
#define DUI_MSGTYPE_HEADERCLICK (_T("headerclick"))
#define DUI_MSGTYPE_ITEMDBCLICK (_T("itemdbclick"))
#define DUI_MSGTYPE_SHOWACTIVEX (_T("showactivex"))
#define DUI_MSGTYPE_ITEMCOLLAPSE (_T("itemcollapse"))
#define DUI_MSGTYPE_ITEMACTIVATE (_T("itemactivate"))
#define DUI_MSGTYPE_VALUECHANGED (_T("valuechanged"))
#define DUI_MSGTYPE_SELECTCHANGED (_T("selectchanged"))
映射到的函数:
//定义消息类型--执行函数宏
#define DUI_ON_MSGTYPE(msgtype, memberFxn) \
{ msgtype, _T(""), DuiSig_vn, (DUI_PMSG)&memberFxn}, \
//定义消息类型--控件名称--执行函数宏
#define DUI_ON_MSGTYPE_CTRNAME(msgtype,ctrname,memberFxn) \
{ msgtype, ctrname, DuiSig_vn, (DUI_PMSG)&memberFxn }, \
//定义click消息的控件名称--执行函数宏
#define DUI_ON_CLICK_CTRNAME(ctrname,memberFxn) \
{ DUI_MSGTYPE_CLICK, ctrname, DuiSig_vn, (DUI_PMSG)&memberFxn }, \
//定义selectchanged消息的控件名称--执行函数宏
#define DUI_ON_SELECTCHANGED_CTRNAME(ctrname,memberFxn) \
{ DUI_MSGTYPE_SELECTCHANGED,ctrname,DuiSig_vn,(DUI_PMSG)&memberFxn }, \
//定义killfocus消息的控件名称--执行函数宏
#define DUI_ON_KILLFOCUS_CTRNAME(ctrname,memberFxn) \
{ DUI_MSGTYPE_KILLFOCUS,ctrname,DuiSig_vn,(DUI_PMSG)&memberFxn }, \
//定义menu消息的控件名称--执行函数宏
#define DUI_ON_MENU_CTRNAME(ctrname,memberFxn) \
{ DUI_MSGTYPE_MENU,ctrname,DuiSig_vn,(DUI_PMSG)&memberFxn }, \
//定义与控件名称无关的消息宏
//定义timer消息--执行函数宏
#define DUI_ON_TIMER() \
{ DUI_MSGTYPE_TIMER, _T(""), DuiSig_vn,(DUI_PMSG)&OnTimer }, \
WindowImplBase也实现了一些常规消息的响应函数:
//点击事件
virtual void OnClick(TNotifyUI& msg);
//关闭窗口事件
virtual LRESULT OnClose(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled);
//销毁窗口事件
virtual LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled);
//窗口尺寸改变时响应此事件
virtual LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
//拦截字符响应
virtual LRESULT OnChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
//拦截系统消息响应
virtual LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
//窗口创建时响应
virtual LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
//键盘按下时响应
virtual LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled);
//失去焦点时响应
virtual LRESULT OnKillFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled);
//获得焦点时响应
virtual LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled);
//左键按下响应
virtual LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled);
//左键抬起响应
virtual LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled);
//鼠标移动响应
virtual LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled);
//鼠标滚轮响应
virtual LRESULT OnMouseWheel(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled);
//鼠标放在控件上响应
virtual LRESULT OnMouseHover(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
作者:彦子凡
链接:https://www.jianshu.com/p/5422cdec2b8f
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。