上一篇讲了控件创建,这篇说说控件消息。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. Squid故障

    1.COSS will not function without large file support (off_t is 4 bytes long. Please reconsider recomp ...

  2. System.arraycopy方法

    数组的复制有多种方法,其中有一种就是System.arraycopy方法,传闻速度也很快. 方法完整签名: public static void arraycopy(Object src, int s ...

  3. 【转】个人常用iOS第三方库以及XCode插件介绍 -- 不错

    原文网址:http://adad184.com/2015/07/08/my-favorite-libraries-and-plugins/ 第三方库是现在的程序员离不开的东西 不光是APP开发 基本上 ...

  4. 用defy来潜水最终还是挂了........

    defy526是6级,,不过好像这次我用来潜过去不足2米还是挂掉了... 国际通用的防水级别认证体系: IPX-0 没有防水保护 IPX-1 设备在正常操作状态下,可以提供相当于3-5毫米/分钟降雨的 ...

  5. [Everyday Mathematics]20150108

    设 $f$ 在 $(a,b)$ 上 $n+1$ 次可导, 且 $$\bex \ln\frac{f(b)+f'(b)+\cdots+f^{(n)}(b)}{f(a)+f'(a)+\cdots+f^{(n ...

  6. logback.xml配置

    一:根节点<configuration>包含的属性: scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true. scanPeriod: 设置监测配置文 ...

  7. 色谱峰的类型BB,BV,VB等都是什么意思

    B(Baseline):峰在基线处开始或结束V(Valley):峰在谷点线处开始或结束P(Peak): 峰开始或结束与基线贯穿点BB就代表标准的峰:从基线开始出峰,最后峰到基线结束(from base ...

  8. HDU 5749 Colmerauer 单调队列+暴力贡献

    BestCoder Round #84   1003 分析:(先奉上zimpha巨官方题解) 感悟:看到题解单调队列,秒懂如何处理每个点的范围,但是题解的一句算贡献让我纠结半天 已知一个点的up,do ...

  9. 《Python 学习手册4th》 第十一章 赋值、表达式和打印

    ''' 时间: 9月5日 - 9月30日 要求: 1. 书本内容总结归纳,整理在博客园笔记上传 2. 完成所有课后习题 注:“#” 后加的是备注内容 (每天看42页内容,可以保证月底看完此书) “重点 ...

  10. PHP:6种GET和POST请求发送方法

    在i94web博客中,我试过了畅言和多说两种社会化评论框,后来还是抛弃了畅言,不安全. 无论是畅言还是多说,我都需要从远程抓取文章的评论数,然后存入本地数据库.对于多说,请求的格式如下: // 获取评 ...