上一篇讲了控件创建,这篇说说控件消息。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. UVa 11300 Spreading the Wealth 分金币

    圆桌旁坐着 n 个人,每个人都有一定数量的金币,金币总数能够被 n 整除.每个人可以给他左右相邻的人一些金币,最终使得每个人的金币数目相等.你的任务是求出被转手的金币数量的最小值,比如 n = 4, ...

  2. zoj 2286 Sum of Divisors

    // f(n)表示 n的约数和 不包括自己// 给你一个m 求1 到 100万里面 f(n)<=m 的个数// 那么首先要用筛选求出所有出 f(n)// 然后就好办了 // 写好后 看见别人好快 ...

  3. 深入学习Oracle分区表及分区索引

    关于分区表和分区索引(About Partitioned Tables and Indexes)对于10gR2而言,基本上可以分成几类: •       Range(范围)分区 •       Has ...

  4. ORACLE 如何定位消耗资源的SQL

    在分析SQL性能的时候,经常需要确定资源消耗多的SQL,总结如下: 1 查看值得怀疑的SQLselect substr(to_char(s.pct, '99.00'), 2) || '%' load, ...

  5. Android下fragment切换的动画效果

    网上看到的例子,转过来记录一下,学习一下,感谢原作者的辛勤编码,效果非常好 基于Android3.0新增的动画api,效果很赞 共21种动画效果: <item>X轴缩放</item& ...

  6. .NET之美——1.1 C#中的泛型

    1.1 C#中的泛型 .Net 1.1版本最受诟病的一个缺陷就是没有提供对泛型的支持.通过使用泛型,我们可以极大地提高代码的重用度,同时还可以获得强类型的支持,避免了隐式的装箱.拆箱,在一定程度上提升 ...

  7. [Everyday Mathematics]20150110

    试证: $$\bex \vlm{n}\frac{\ln^2n}{n}\sum_{k=2}^{n-2}\frac{1}{\ln k\cdot \ln(n-k)}=1. \eex$$

  8. android左右滑动加载分页以及动态加载数据

    android UI 往右滑动,滑动到最后一页就自动加载数据并显示 如图: package cn.anycall.ju; import java.util.ArrayList; import java ...

  9. IOS 屏幕截图 UIScrollview

    //截图UIView:截全图 -(UIImage*)captureView:(UIView *)theView{ CGRect rect = theView.frame; if ([theView i ...

  10. 再谈Jquery Ajax方法传递到action(转)

    之前写过一篇文章Jquery Ajax方法传值到action,本文是对该文的补充. 假设 controller中的方法是如下: public ActionResult ReadPerson(Perso ...