DuiLib(三)——控件消息
上一篇讲了控件创建,这篇说说控件消息。directui的中心思想是在真实窗口之上画出所有控件,那这些控件是如何获取各自消息的?
通过第一篇的示例可以看到窗口消息的传递过程:
- CWindowWnd::__WndProc
- CWindowWnd::HandleMessage(CFrameWindowWnd类覆盖此函数)
- 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; 。。。
可以清楚地看到,分发消息分为两步:
- 查找控件:CPaintManagerUI::FindControl
- 调用控件接口: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(三)——控件消息的更多相关文章
- 问题-[DelphiXE2]提示第三控件不存在
问题情况:在DelphiXE2启动时界面显示加载了控件,并且控件的路径也放在了环境变量中,但打开程序报第三控件不存在. 问题原因:是没有选择要加载的控件. 问题处理:点击Component->I ...
- duilib List控件,横向滚动时列表项不移动或者显示错位的bug的修复
转载请说明出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/42264673 关于这个bug的修复我之前写过一篇博客,连接为:http:/ ...
- 增加duilib edit控件的提示功能和多种文字颜色
转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/41786407 duilib的CEditUI控件内部使用了win32的原生 ...
- 修复duilib CEditUI控件和CWebBrowserUI控件中按Tab键无法切换焦点的bug
转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/41556615 在duilib中,按tab键会让焦点在Button一类的控 ...
- duilib combo控件,当鼠标滚动时下拉列表自动关闭的bug的修复
转载请说明出处,谢谢~~ 群里有朋友提到了使用Combo控件时,当下拉列表出现,此时鼠标滚轮滚动,下拉列表就自动消失了.我看了一下源码,这个bug的修复很简单. CComboUI控件被单击时创建CCo ...
- Clean小程序(控件消息)
一 . 准备工作 创建一个基于对话框的MFC项目 删除对话框上的工具 二 . 实现将seven图片贴到上面,按一下则换一张图片 1.在资源视图中添加位图资源,通过属性修改图片ID 2.将对话框拉长,防 ...
- Duilib的控件拖拽排序,支持跨容器拖拽(网易云信版本)
完整代码见:https://github.com/netease-im/NIM_Duilib_Framework/pull/151 核心代码(思路): appitem.h #pragma once # ...
- soui中subscribeEvent订阅控件消息与宏订阅注意事项
同一个控件,subscribeEvent与宏定义不能同时响应,优先响应sub 所以,同一个控件的同一个消息,要想在多个地方响应,就必须sub方式订阅
- duilib List控件,横向滚动时列表项不移动或者移动错位的bug的修复
转载请说明出处,谢谢~~ 这篇博客已经作废,只是留作记录,新的bug修复博客地址:http://blog.csdn.net/zhuhongshu/article/details/42264673 之前 ...
随机推荐
- 【转】eclipse中egit插件使用
原文网址:http://my.oschina.net/songxinqiang/blog/192567 eclipse和git这个两个工具的使用人数都是相当多的,在eclipse里面也有egit插件来 ...
- C++ primer里的template用法
来源:http://c.chinaitlab.com/cc/ccjq/200806/752604_2.html -- template 的用法 在程序设计当中经常会出现使用同种数据结构的不同实 ...
- 2016计蒜之道复赛 百度地图的实时路况 floyd+cdq分治
链接:https://nanti.jisuanke.com/t/11217 奉上官方题解: 枚举 d(x , y , z) 中的 y,把 y 从这个图中删去,再求这时的全源最短路即可,使用 Floyd ...
- Javascript——说说js的调试
最近比较吐槽,大家都知道,现在web前端相对几年前来说已经变得很重了,各种js框架,各种面对对象,而且项目多了,就会提取公共模块. 这些模块的UI展示都一样,不一样的就是后台逻辑,举个例子吧,我们做企 ...
- CentOS VPS创建pptpd VPN服务
原文地址http://www.hi-vps.com/wiki/doku.php?id=xen_vps_centos6_install_pptpd CentOS VPS创建pptpd VPN服务 Xen ...
- 一个鼠标键盘控制两台甚至多台主机的方法--Synergy
在多台主机,不同系统中操作.避免了更换键鼠的麻烦.即使下面图中的功能. 鼠标同时在三台或者多台主机之间进行移动,而且是无缝滑动,鼠标直接从左滑倒右,而且支持,这台电脑复制,另一台黏贴.非常的方便实用. ...
- Lucene学习笔记:一,全文检索的基本原理
一.总论 根据http://lucene.apache.org/java/docs/index.html定义: Lucene是一个高效的,基于Java的全文检索库. 所以在了解Lucene之前要费一番 ...
- The First Pig Task
The First Pig Program 环境: Hadoop-1.1.2 pig-0.11.1 linux系统为CentOS6.4 jdk1.6 在伪分布 ...
- 对ArrayList 进行深拷贝
ArrayList arr = new ArrayList(); arr.Add()); arr.Add()); arr.Add()); ArrayList arr2 = new ArrayList( ...
- 取消本地SVN文件夹与服务器关联
问题:之前建了一个SVN代码库,同步了代码上去,但中途发现建库时的规则搞错了,就把服务器上的库给删了重建,然后改变本地代码的svn服务器关联地址,但使用Relocate一直报错. 错误有两种情况:1. ...