上一篇讲了控件创建,这篇说说控件消息。directui的中心思想是在真实窗口之上画出所有控件,那这些控件是如何获取各自消息的?

通过第一篇的示例可以看到窗口消息的传递过程:

  1. CWindowWnd::__WndProc
  2. CWindowWnd::HandleMessage(CFrameWindowWnd类覆盖此函数)
  3. CPaintManagerUI::MessageHandler

消息最终传递到CPaintManagerUI::MessageHandler中,以WM_LBUTTONDOWN消息为例,看看消息如何分发到控件

。。。

 1     case WM_LBUTTONDOWN:
{
// We alway set focus back to our app (this helps
// when Win32 child windows are placed on the dialog
// and we need to remove them on focus change).
::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();
TEventUI event = { };
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; 。。。

可以清楚地看到,分发消息分为两步:

  1. 查找控件:CPaintManagerUI::FindControl
  2. 调用控件接口:CControlUI::Event

现在看看控件查找是怎么回事:

 CControlUI* CPaintManagerUI::FindControl(POINT pt) const
{
ASSERT(m_pRoot);
return m_pRoot->FindControl(__FindControlFromPoint, &pt, UIFIND_VISIBLE | UIFIND_HITTEST | UIFIND_TOP_FIRST);
}

m_pRoot看名字应该是根控件了,在第二篇的控件创建过程中CDialogBuilder::Create返回了根控件,并且通过CPaintManagerUI::AttachDialog将根控件指针保存在了m_pRoot中。看看xml文件

 <?xml version="1.0" encoding="UTF-8"?>
<Window mininfo="400,240" size="400,240" alpha="250">
<Font name="微软雅黑" size="18" bold="true" default="true"/>
<VerticalLayout inset="10,6,10,6" bkimage="back.jpg">
<Label name="label" float="true" pos="10,10,100,30" bkcolor="#FF00FFFF" textcolor="#FFFFFFFF" text="Label"/>
</VerticalLayout>
</Window>

根控件为VerticalLayout,是CContainerUI控件容器子类。而CContainerUI又是CControlUI子类。创建控件时,所有的子控件都添加到了根控件容器中,查找控件自然通过根控件容器实现。

 CControlUI* CContainerUI::FindControl(FINDCONTROLPROC Proc, LPVOID pData, UINT uFlags)
{
。。。 if( (uFlags & UIFIND_TOP_FIRST) != ) {
for( int it = m_items.GetSize() - ; it >= ; it-- ) {
CControlUI* pControl = static_cast<CControlUI*>(m_items[it])->FindControl(Proc, pData, uFlags);
if( pControl != NULL ) {
if( (uFlags & UIFIND_HITTEST) != && !pControl->IsFloat() && !::PtInRect(&rc, *(static_cast<LPPOINT>(pData))) )
continue;
else
return pControl;
}
}
}
else {
for( int it = ; it < m_items.GetSize(); it++ ) {
CControlUI* pControl = static_cast<CControlUI*>(m_items[it])->FindControl(Proc, pData, uFlags);
if( pControl != NULL ) {
if( (uFlags & UIFIND_HITTEST) != && !pControl->IsFloat() && !::PtInRect(&rc, *(static_cast<LPPOINT>(pData))) )
continue;
else
return pControl;
}
}
} 。。。
}

容器CContainerUI的成员m_items中保存了所有子控件指针,通过遍历子控件(调用CControlUI::FindControl)查找符合条件的子控件,还可以根据标识uFlags进行不同的查找方式。

查找到子控件后,再看看控件的事件接口CControlUI::Event。很明显这是一个虚函数,通过多态,各子控件可以实现自己的事件处理函数。比如控件CButtonUI可以处理UIEVENT_KEYDOWN、UIEVENT_BUTTONUP事件,更新按钮状态。

至此,控件消息分发介绍完了。那如何添加控件事件处理?答案是通过INotifyUI接口。在第一篇的示例中,框架类CFrameWindowWnd不光继承了窗口类CWindowWnd,也实现了INotifyUI接口。INotifyUI很简单

 class INotifyUI
{
public:
virtual void Notify(TNotifyUI& msg) = ;
};

接口参数为结构体TNotifyUI

 typedef struct tagTNotifyUI
{
CStdString sType;//事件类型
CControlUI* pSender;//控件
DWORD dwTimestamp;
POINT ptMouse;
WPARAM wParam;
LPARAM lParam;
} TNotifyUI;

框架类只要实现这个接口,并注册(CPaintManagerUI::AddNotifier),就可以监听控件事件了。这是典型的观察者模式。再看看第一篇给出的示例代码。

 class CFrameWindowWnd : public CWindowWnd, public INotifyUI
{
public:
CFrameWindowWnd() { };
LPCTSTR GetWindowClassName() const { return _T("FrameWnd"); };
UINT GetClassStyle() const { return UI_CLASSSTYLE_FRAME | CS_DBLCLKS; };
void OnFinalMessage(HWND /*hWnd*/) { delete this; }; void Notify(TNotifyUI& msg)
{
if( msg.sType == _T("windowinit") ) {
}
else if( msg.sType == _T("click") ) {
}
} //消息处理:窗口函数__WndProc ---> HandleMessage
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if( uMsg == WM_CREATE ) {
m_pm.Init(m_hWnd);
//根据XML创建控件
CDialogBuilder builder;
CControlUI* pRoot = builder.Create(_T("HelloWorld.xml"), (UINT), NULL, &m_pm);
ASSERT(pRoot && "Failed to parse XML");
m_pm.AttachDialog(pRoot);
m_pm.AddNotifier(this);
return ;
}
else if( uMsg == WM_DESTROY ) {
::PostQuitMessage(0L);
}
else if( uMsg == WM_ERASEBKGND ) {
return ;
} //消息处理:CPaintManagerUI::MessageHandler
LRESULT lRes = ;
if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes;
return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
} public:
CPaintManagerUI m_pm;
};

关于控件消息基本就这些了,有些地方没有细究,先了解大体流程比较重要。

DuiLib(三)——控件消息的更多相关文章

  1. 问题-[DelphiXE2]提示第三控件不存在

    问题情况:在DelphiXE2启动时界面显示加载了控件,并且控件的路径也放在了环境变量中,但打开程序报第三控件不存在. 问题原因:是没有选择要加载的控件. 问题处理:点击Component->I ...

  2. duilib List控件,横向滚动时列表项不移动或者显示错位的bug的修复

    转载请说明出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/42264673 关于这个bug的修复我之前写过一篇博客,连接为:http:/ ...

  3. 增加duilib edit控件的提示功能和多种文字颜色

    转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/41786407 duilib的CEditUI控件内部使用了win32的原生 ...

  4. 修复duilib CEditUI控件和CWebBrowserUI控件中按Tab键无法切换焦点的bug

    转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/41556615 在duilib中,按tab键会让焦点在Button一类的控 ...

  5. duilib combo控件,当鼠标滚动时下拉列表自动关闭的bug的修复

    转载请说明出处,谢谢~~ 群里有朋友提到了使用Combo控件时,当下拉列表出现,此时鼠标滚轮滚动,下拉列表就自动消失了.我看了一下源码,这个bug的修复很简单. CComboUI控件被单击时创建CCo ...

  6. Clean小程序(控件消息)

    一 . 准备工作 创建一个基于对话框的MFC项目 删除对话框上的工具 二 . 实现将seven图片贴到上面,按一下则换一张图片 1.在资源视图中添加位图资源,通过属性修改图片ID 2.将对话框拉长,防 ...

  7. Duilib的控件拖拽排序,支持跨容器拖拽(网易云信版本)

    完整代码见:https://github.com/netease-im/NIM_Duilib_Framework/pull/151 核心代码(思路): appitem.h #pragma once # ...

  8. soui中subscribeEvent订阅控件消息与宏订阅注意事项

    同一个控件,subscribeEvent与宏定义不能同时响应,优先响应sub 所以,同一个控件的同一个消息,要想在多个地方响应,就必须sub方式订阅

  9. duilib List控件,横向滚动时列表项不移动或者移动错位的bug的修复

    转载请说明出处,谢谢~~ 这篇博客已经作废,只是留作记录,新的bug修复博客地址:http://blog.csdn.net/zhuhongshu/article/details/42264673 之前 ...

随机推荐

  1. vim变ide

    如果你稍微写过一点代码,就能知道“集成开发环境”(IDE)是多么的便利.不管是Java.C还是Python,当IDE会帮你检查语法.后台编译,或者自动导入你需要的库时,写代码就变得容易许多.另外,如果 ...

  2. 面试题 IQ

    现在有一大块金条,它可以分为七小块金条.是这样子的,工人为你工作7天,每天都将获得一小块金条,你要做的就是发工资,切割大块金条的次数最多两次,你有什么方法让工人每天都获得一小块金条呢?

  3. Android 使用Instrumentation进行界面的单元测试

    如果我们要对一个Activity界面上的一个按钮的点击事件进行单元测试,则可使用ActivityInstrumentationTestCase2类来进行测试. 首先我们定义一个测试类: public ...

  4. MyBatis学习 之 二、SQL语句映射文件(1)resultMap

    目录(?)[-] 二SQL语句映射文件1resultMap resultMap idresult constructor association联合 使用select实现联合 使用resultMap实 ...

  5. HDU5764 After a Sleepless Night 树形乱搞题

    分析(官方题解): 假设根已确定,可以发现新树若合法,需满足以下性质:根节点是n:儿子的值不大于父亲:具有相同值的节点形成一条链,并且链不会发生“分叉”(即有多个最低点).所以对于新树中有出现的值x, ...

  6. HDU 5778 abs (BestCoder Round #85 C)素数筛+暴力

    分析:y是一个无平方因子数的平方,所以可以从sqrt(x)向上向下枚举找到第一个无平方因子比较大小 大家可能觉得这样找过去暴力,但实际上无平方因子的分布式非常密集的,相关题目,可以参考 CDOJ:无平 ...

  7. DataGrid的打印预览和打印

    using System;using System.Drawing;using System.Collections;using System.ComponentModel;using System. ...

  8. ORA-12162: TNS:net service name is incorrectly specified

    今天在进行修改oracle_sid环境变量的时候,将相关的环境变量值去掉,从而不能进入sqlplus,报错如下: [oracle@kel ~]$ sqlplus / as sysdba SQL*Plu ...

  9. 利用原生JavaScript获取样式的方式小结

    来源:http://www.ido321.com/930.html ps:是获取样式,不是设置样式.若没有给元素设置样式值,则返回浏览器给予的默认值.(论坛整理) 1.element.style:只能 ...

  10. 编译安装vim8.0

    由于我的网络问题,我download vim的原码进行手动编译:碰到的问题:1:编译的时候找不到python.h ??     编译的vim的时候需要python 支持,有两种一种python2, p ...