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

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值