5Button的click消息流程(notify消息)

来源:互联网 发布:thinkphp投票系统源码 编辑:程序博客网 时间:2024/06/08 15:32

1.简介

本节研究下当一个 Button 按钮被点击时,消息是如何一步步传输的,并最终被主窗口中的 Notify() 函数处理掉。 通过此,可以清晰的了解 Notify 消息的产生过程及传递过程。

本节基于需要了解的一点是:在自定义窗口中的,如果 HandleMessage() 不对消息进行处理,则最终会走向 CPaintManagerUI::MessageHandler() 函数中,此函数对多种系统消息进行了处理。

2.鼠标左键按下

当鼠标左键按下时,系统会向窗口发送 WM_LBUTTONDOWN 消息,经过一系列传递后,WM_LBUTTONDOWN 到达 CPaintManagerUI::MessageHandler() 函数中。CPaintManagerUI中有成员变量 m_pEventClick 用于记录点击事件由哪个控件处理,在此处进行赋值,方便在鼠标左键抬起时使用。

bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes){    //异步消息处理......    //消息过滤......    switch( uMsg )     {    //此处省略了其他各种case处理    case WM_LBUTTONDOWN: //系统消息:左键按下        {            //窗口获得焦点            ::SetFocus(m_hWndPaint);             //根据鼠标位置判断是哪个控件被左键按下            POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };            m_ptLastMousePos = pt;            CControlUI* pControl = FindControl(pt);             if( pControl == NULL ) break;            if( pControl->GetManager() != this ) break;            //将焦点设置在控件上(当然会有关于焦点的一系列消息发生)            m_pEventClick = pControl;            pControl->SetFocus();            //捕获鼠标            SetCapture();            //初始化event事件            TEventUI event = { 0 };            event.Type = UIEVENT_BUTTONDOWN;            event.pSender = pControl;            event.wParam = wParam;            event.lParam = lParam;            event.ptMouse = pt;            event.wKeyState = (WORD)wParam;            event.dwTimestamp = ::GetTickCount();            //处理按下事件:按钮变换背景颜色等            pControl->Event(event);        }        break;    default:        break;    }    //异步消息处理......    return false;}

接着简介下 pControl->Event(event) 这句。由于 CButtonUI 未重载 Event() 函数,故调用父类 CControlUI 的,如下:

void CControlUI::Event(TEventUI& event){    if( OnEvent(&event) )         DoEvent(event);}

CButtonUI 并没有给 OnEvent 设置代理(后续再介绍),故会返回true,接着会直接调用 DoEvent() 函数。DoEvent() 会对多种event事件进行处理,在这里仅将对 UIEVENT_BUTTONDOWN 的处理贴出来:

void CButtonUI::DoEvent(TEventUI& event){    //其他各种event处理    if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK )    {        if( ::PtInRect(&m_rcItem, event.ptMouse) && IsEnabled() )         {            m_uButtonState |= UISTATE_PUSHED | UISTATE_CAPTURED; //更改状态样式            Invalidate(); //刷新控件        }        return;    }    CLabelUI::DoEvent(event);}

3.鼠标左键抬起

左键抬起的消息 WM_LBUTTONUP 的处理流程跟按下左键基本相同,bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes) 中对 WM_LBUTTONUP 消息的处理过程如下:

case WM_LBUTTONUP:    {        POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };        m_ptLastMousePos = pt;        if( m_pEventClick == NULL ) break;        //释放鼠标捕获        ReleaseCapture();        //初始化event事件        TEventUI event = { 0 };        event.Type = UIEVENT_BUTTONUP;        event.pSender = m_pEventClick;        event.wParam = wParam;        event.lParam = lParam;        event.ptMouse = pt;        event.wKeyState = (WORD)wParam;        event.dwTimestamp = ::GetTickCount();        //处理松开鼠标事件        m_pEventClick->Event(event);        m_pEventClick = NULL;    }    break;

之后便会进入 CButtonUI::DoEvent, 如下:

void CButtonUI::DoEvent(TEventUI& event){    //其他各种event处理    if( event.Type == UIEVENT_BUTTONUP )    {        if( (m_uButtonState & UISTATE_CAPTURED) != 0 )         {            if( ::PtInRect(&m_rcItem, event.ptMouse) )                 Activate();            m_uButtonState &= ~(UISTATE_PUSHED | UISTATE_CAPTURED);            Invalidate();        }        return;    }    CLabelUI::DoEvent(event);}

如果按下鼠标与松开鼠标的过程中没有移动鼠标,则鼠标位置仍在Button中,PtInRect()返回true,进入 Activate() 函数,如下:

bool CButtonUI::Activate(){    if( !CControlUI::Activate() )         return false;    if( m_pManager != NULL )         m_pManager->SendNotify(this, DUI_MSGTYPE_CLICK);    return true;}

4.进入 SendNotify 函数

从上面代码中可以看出,会执行 m_pManager->SendNotify(),而且是同步消息,消息会立即被执行。执行的过程简化如下:

void CPaintManagerUI::SendNotify(TNotifyUI& Msg, bool bAsync /*= false*/){    Msg.ptMouse = m_ptLastMousePos;    Msg.dwTimestamp = ::GetTickCount();    if( m_bUsedVirtualWnd )    {        Msg.sVirtualWnd = Msg.pSender->GetVirtualWnd();    }    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);    }}

故窗口的 Notify() 函数将会直接被执行。至于你要对按钮点击做出什么响应,在Notify()中实现就可以了。

5.结束

印证了一点,所有的notify消息都是通过 CPaintManagerUI::SendNotify() 产生的。

0 0