1.简介
如果想响应Notify事件,在自己的窗口的 HandleMessage() 函数中必须调用CPaintManagerUI::MessageHandler(),该函数处理了绝大部分常用的消息响应。
DuiLib将发送的Notify消息分为了同步和异步消息。同步就是立即调用,异步就是先放到队列中,下次再处理(类似PostMessage与SendMessage)。
2.发送Notify消息
CPaintManagerUI中有个函数用于程序主动发送Notify消息:
- CPaintManagerUI::SendNotify(CControlUI*, LPCTSTR, WPARAM, LPARAM, bool)
该函数将PostMessage与SendMessage两个函数的功能集成在一个函数中,通过最后一个bool参数来区分同步消息或者异步消息:true:异步消息,false:同步消息。同步消息可以立马被执行,在消息被处理后,SendNotify() 才能返回;异步消息是先放在消息队列中,然后 SendNotify() 直接返回,异步消息会在窗口函数收到下一条消息的时候被处理。详细可以看代码:
void CPaintManagerUI::SendNotify(CControlUI* pControl, LPCTSTR pstrMessage, WPARAM wParam /*= 0*/, LPARAM lParam /*= 0*/, bool bAsync /*= false*/)
{
//对消息进行了封装
TNotifyUI Msg;
Msg.pSender = pControl;
Msg.sType = pstrMessage;
Msg.wParam = wParam;
Msg.lParam = lParam;
//发送消息还得看下面这个函数
SendNotify(Msg, bAsync);
}
void CPaintManagerUI::SendNotify(TNotifyUI& Msg, bool bAsync /*= false*/)
{
Msg.ptMouse = m_ptLastMousePos;
Msg.dwTimestamp = ::GetTickCount();
if( m_bUsedVirtualWnd )
{
Msg.sVirtualWnd = Msg.pSender->GetVirtualWnd();
}
if( !bAsync )
{
// Send to all listeners
if( Msg.pSender != NULL )
{
//控件本身处理此消息
if( Msg.pSender->OnNotify )
Msg.pSender->OnNotify(&Msg);
}
for( int i = 0; i < m_aNotifiers.GetSize(); i++ )
{
//同步消息,被立即执行
static_cast<INotifyUI*>(m_aNotifiers[i])->Notify(Msg);
}
}
else
{
//将消息存到堆上去
TNotifyUI *pMsg = new TNotifyUI;
pMsg->pSender = Msg.pSender;
pMsg->sType = Msg.sType;
pMsg->wParam = Msg.wParam;
pMsg->lParam = Msg.lParam;
pMsg->ptMouse = Msg.ptMouse;
pMsg->dwTimestamp = Msg.dwTimestamp;
//异步消息,被放在消息队列中
m_aAsyncNotify.Add(pMsg);
}
}
3.异步消息被处理
异步消息的处理时机:在窗口过程被执行的时候,即执行窗口的 HandleMessage() 的时候,且在 HandleMessage() 中调用了 CPaintManagerUI::MessageHandler()。原因在于异步消息只在CPaintManagerUI::MessageHandler()中进行处理。如下代码,将其他与异步消息无关代码已简化,并将异步消息处理提出成了一个函数:
bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes)
{
//异步消息处理
handleAsyncNotifyMsg();
// 消息过滤
// 普通消息处理
switch( uMsg )
{
case WM_APP + 1:
case WM_CLOSE:
case WM_ERASEBKGND:
case WM_PAINT: //重要,负责绘制窗口中的各个控件
....
}
// 异步消息处理:此处是为了处理上面switch时可能产生的一些异步消息
handleAsyncNotifyMsg();
return false;
}
//异步消息处理:循环取出异步消息,依次处理
void handleAsyncNotifyMsg()
{
TNotifyUI* pMsg = NULL;
while( pMsg = static_cast<TNotifyUI*>(m_aAsyncNotify.GetAt(0)) )
{
m_aAsyncNotify.Remove(0);
if( pMsg->pSender != NULL )
{
if( pMsg->pSender->OnNotify ) pMsg->pSender->OnNotify(pMsg);
}
for( int j = 0; j < m_aNotifiers.GetSize(); j++ )
{
static_cast<INotifyUI*>(m_aNotifiers[j])->Notify(*pMsg);
}
delete pMsg;
}
}
4.窗口的 Notify(TNotifyUI& msg) 函数
// Listener interface
class INotifyUI
{
public:
virtual void Notify(TNotifyUI& msg) = 0;
};
bool CPaintManagerUI::AddNotifier(INotifyUI* pNotifier)
{
ASSERT(m_aNotifiers.Find(pNotifier)<0);
return m_aNotifiers.Add(pNotifier);
}
从上可知,Notify() 函数为 INotifyUI 定义的一个接口函数,所有的窗口都可以实现该函数。通过利用函数 AddNotifier() 注册监听,即可以处理Notify消息。
通过搜索发现, Notify() 函数在以下地方被调用:
- void CPaintManagerUI::SendNotify(TNotifyUI& Msg, bool bAsync /= false/);
- bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes);
在上述第一个函数中,用于处理同步消息;在第二个函数中处理异步消息。而异步消息的入口也是 SendNotify() 函数,通过此函数将异步消息添加到队列中。依次可以得出结论:所有的notify消息都是通过 SendNotify() 函数来进入到notify消息处理中的。
5.结束
Duilib的所有消息类型如下:
#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"))