Duilib源码分析(六)整体流程
在《Duilib源码分析(一)整体框架》、《Duilib源码分析(二)控件构造器—CDialogBuilder》以及《Duilib源码分析(三)XML解析器—CMarkup》中我们已从粗略的角度去分析框架操作流程和消息流程,只能对其有基本的印象,此处我们将通过实际的举例分析,duilib创建的工程,在整个资源解析、控件创建、控件加载与绘制,控件数据处理等管理的整个过程进行整合:
为了便于分析,我们仍然从项目中附带的工程“TestApp1”进行更深入的学习,以下执行流程为具体的大致步骤和操作内容;
从入口点WinMain:
1. CPaintManagerUI整个绘制UI的管理器,调用静态成员函数设置当前应用程序实例句柄以及资源路径(主要为各种xml和图片资源);
2. 初始化COM组件;
3. 创建窗口对象实例,并调用接口Create创建窗口;
4. 调用ShowWindow显示当前窗口界面;
5. 调用CPaintManagerUI的静态成员MessageLoop,执行消息循环;
6. 卸载COM组件。
以上几个简单的步骤基本上是所有duilib应用程序的执行大体框架;而最重要的莫过于Create、MessageLoop;
在调用Create函数中执行了几个重要的操作:
0. 注册窗口类、创建窗口;
1. 触发WM_CREATE消息(事实上还有其他的消息,但暂时先无需关心), 创建控件构造器CDialogBuilder,并以其内部CMarkup解析指定的test1.xml文件,并解析XML文件各节点;
2. 解析完成后,调用构建器的_Parse接口,实现对整个CMarkup各节点控件的申请、创建并以各控件所在xml文件中的布局组织控件树;
3. 对于构建完控件树后,得到树根控件并通过CPaintManagerUI绘制UI管理器的接口AttachDialog添加至绘制管理中;
4. 通过AddNotifier添加通告对象至CPaintManagerUI中,以便于可收到通告消息,触发void Notify(TNotifyUI& msg)调用,用户可以在该触发中做一些响应操作;
所以以上的几个操作基本上完成了需要绘制和控件响应的基本条件;
在调用MessageLoop中,实现消息循环(先忽略我们不太感兴趣的消息):
0. 收到WM_PAINT消息,在里面先完成了通告消息windowinit,使得触发Notify,此时可以做一些初始化操作;
1. 另外创建窗体的兼容DC、兼容位图;接着重要的是调用m_pRoot->DoPaint(m_hDcOffscreen, ps.rcPaint);此调用将完成所有被管理的控件树位置计算、绘制到兼容DC;
2. 然后调用BitBlt实现兼容DC到窗体DC的位块传输、拷贝并要求整个窗体重绘;
OK,以上两个主要操作完成了所有的主要功能包括xml文件解析,控件树的创建,绘制的管理、贴图,控件消息响应等;
因为我们知道duilib中各个控件是没有窗体句柄的,那么接下来,我们继续看各个控件对应的各种消息是如何被得知和传递的;
首先我们以该工程中的OnAlphaChanged接口说明,我们采用倒叙跟踪的方式;
1. 该接口在OnPrepare接口中被添加至一个被称为“alpha_controlor”的控件对象的OnNotify通告消息函数中,而OnPrepare函数在Notify接口中被调用,被调用的时机为
MessageLoop中收到WM_PAINT时候完成的通告消息windowinit;
2. 当鼠标左击按下时,CPaintManagerUI将收到WM_LBUTTONDOWN消息,所以我们要确定点击位于哪个控件上,跟踪该消息,内部通过WM_LBUTTONDOWN消息对应的
lParam参数获取到鼠标点击的位置,并通过FindControl查找到指定的鼠标位置下控件(整个查找过程为遍历当前控件以及其下所有子控件,每个控件根据自己的位置和鼠标的位置确定
是否为自己并返回自己);
3. 对得到的控件,调用该控件的SetFocus以及Event,在SetFocus中调用CPaintManagerUI的SetFocus构造TEventUI(一般将CPaintManagerUI的消息转化为duilib自定义的控件的消息)
并调用SendNotify,这样便触发了对应控件的通告消息并最终调用到OnAlphaChanged;在调用控件的Event时重新构造TEventUI结构参数,调用DoEvent完成触发事件响应;
OK,目前基本上已经知道了控件的消息数据流及其响应,以及控件绘制,不过当单个控件无效需要重绘时不应该调用耗费很大开销的WM_PAINT来导致所有的控件重绘这肯定是不科学的,所以
对于某些控件若不涉及到整个或是其他控件重绘的将直接对各个控件内部调整位置、状态,然后调用CPaintManagerUI的Invalidate进行区域性的重绘即可。
最后一步,再谈CPaintManagerUI::MessageLoop;
0. 在创建窗口的过程中一般会先注册窗口过程函数CWindowWnd::__WndProc,并保存窗口句柄和对象;那么可以认为消息被转换、翻译和分发后都会先向该处理函数发送并调用;
1. 调用本窗口类对象的HandleMessage接口,实现消息可以在类对象中接手并处理;
2. HandleMessage接口中先处理自己感兴趣的消息,若不处理则交给CPaintManagerUI对象的MessageHandler处理;
3. MessageHandler中会预先过来、预处理部分注册的消息;其他部分消息处理并转化为duilib的消息并执行一些通告、事件等操作,若MessageHandler未处理的则交给CWindowWnd的
HandleMessage,事实上该内部通过::CallWindowProc调用默认窗口过程处理函数::DefWindowProc进行大多数的默认处理(windows会自行并合理地处理,可不用关心);
4. 然而以上步骤是否真的只有这些,肯定不止;继续跟踪CPaintManagerUI::MessageLoop实现;
5. 该消息循环中在消息被翻译、转化前先调用了CPaintManagerUI::TranslateMessage,该函数内部先预处理已注册的预处理消息和加速器消息,并根据需要确定是否处理或继续传递;
6. 对于其不处理的消息,才会调用::TranslateMessage和::DispatchMessage翻译、分发该消息,而之后才会执行上述步骤的0~3。以上即为所有的窗体消息执行流程。
项目总结:
duilib整体上以无HWND的情况下自绘完成界面绘制显示,各个控件完成自己的绘制并通过贴图的方式实现整个窗体的绘制;另外采用双缓冲区绘制界面避免了闪烁,此外对某个控件失效重绘
也是采用句柄重绘的方式极大的减少绘制开销;通过XML文件配置界面布局、内容,比较灵活可扩展,用户可根据需要修改源码和增加感兴趣的属性实现,很多时候用户只需要把大量时间用在应用层、
业务逻辑层;内部各控件资源管理统一并内部实现自己的资源管理和一些功能组件,使得可不需依赖STL、MFC、BOOST等,移植方面只能是windows系列或以上版本,但要移植到其他平台则也需要
花费很大的时间和工作量;另外就是资源路径、加载,因内部CPaintManagerUI保存为静态变量,所以同一个程序如MFC中需多处创建duilib窗口可能导致资源混乱的情况,可统一资源路径或修改源码
实现以解决该问题;当然最重要的是小巧、轻便、资源可以打包,可结合打包工具轻松制作安装包,分发给最终用户;但内部部分控件绘制、数据处理等细节方面比较多可能还存在BUG等不足;
duilib相对于MFC不需要太多依赖和庞大的组件库;相对于原始纯WIN32开发更快捷,用户可根据需要、项目规模和功能需求选择该项目作为开发界面库。
uilib应为duilib原身,整体结构一致;相对duilib,uilib会多一些控件如动画、图表、GIF动画等,CDxAnimationUI、CDuiTimer、CAnimationTabLayoutUI、以及其他的工具、
托盘CDuiTrayIcon、CDuiAutoComplete、阴影CShadowUI等等,不过在使用和知名度上duilib更为广泛,可根据需要移植uilib的部分实现到duilib以满足需要,在项目中建议使用duilib。
Duilib源码分析(六)整体流程的更多相关文章
- Duilib源码分析(一)整体框架
Duilib界面库是一款由杭州月牙儿网络技术有限公司开发的界面开源库,以viksoe项目下的UiLib库的基础上开发(此后也将对UiLib库进行源码分析):通过XML布局界面,将用户界面和处理逻辑彻底 ...
- 104 - kube-scheduler源码分析 - predicate整体流程
(注:从微信公众:CloudGeek复制过来,格式略微错乱,更好阅读体验请移步公众号,二维码在文末) 今天我们来跟一下predicates的整个过程:predicate这个词应该是“断言.断定”的意思 ...
- nodejs的Express框架源码分析、工作流程分析
nodejs的Express框架源码分析.工作流程分析 1.Express的编写流程 2.Express关键api的使用及其作用分析 app.use(middleware); connect pack ...
- openVswitch(OVS)源码分析之工作流程(哈希桶结构体的解释)
这篇blog是专门解决前篇openVswitch(OVS)源码分析之工作流程(哈希桶结构体的疑惑)中提到的哈希桶结构flex_array结构体成员变量含义的问题. 引用下前篇blog中分析讨论得到的f ...
- Okhttp源码分析--基本使用流程分析
Okhttp源码分析--基本使用流程分析 一. 使用 同步请求 OkHttpClient okHttpClient=new OkHttpClient(); Request request=new Re ...
- [阿里DIN] 深度兴趣网络源码分析 之 整体代码结构
[阿里DIN] 深度兴趣网络源码分析 之 整体代码结构 目录 [阿里DIN] 深度兴趣网络源码分析 之 整体代码结构 0x00 摘要 0x01 文件简介 0x02 总体架构 0x03 总体代码 0x0 ...
- springmvc源码分析系列-请求处理流程
接上一篇-springmvc源码分析开头片 上一节主要说了一下springmvc与struts2的作为MVC中的C(controller)控制层的一些区别及两者在作为控制层方面的一些优缺点.今天就结合 ...
- MyBatis源码分析-MyBatis初始化流程
MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...
- Duilib源码分析(三)XML解析器—CMarkup
上一节介绍了控件构造器CDialogBuilder,接下来将分析其XML解析器CMarkup: CMarkup:xml解析器,目前内置支持三种编码格式:UTF8.UNICODE.ASNI,默认为UTF ...
随机推荐
- H5+CSS3知识点
概要:CSS3美化样式.自定义字体图标.滤镜设置.CSS3选择器.transform2D转换.新增表单控件.vaild表单验证.表单样式美化等. 属性选择器: E[attr]只使用属性名,但没有确定任 ...
- js实现继承的5种方式 (笔记)
js实现继承的5种方式 以下 均为 ES5 的写法: js是门灵活的语言,实现一种功能往往有多种做法,ECMAScript没有明确的继承机制,而是通过模仿实现的,根据js语言的本身的特性,js实现继承 ...
- leetcode笔记
82. Remove Duplicates from Sorted List II https://leetcode.com/problems/remove-duplicates-from-sorte ...
- 调用WebServices超时
1. 服务器端设置超时 在 web.config 的 system.web 里添加如下配置项: < httpRuntimeexecutionTimeout="300000"/ ...
- java.lang.ClassNotFoundException: com.mysql.jdbc.Driver解决办法
这个问题的原因是没有导入mysql连接库,我从官网上下载后照着网上的教程各种导入无果,最后发现是我导入的文件错了.... 官网上下下来的压缩文件是这个,不过这并不是直接要导入的文件,首先解压文件,然后 ...
- 编程轶事-java中的null-遁地龙卷风
1.null是个奇妙的东西,可以理解为对象占位符 User user = null; System.out.println(user.getCredits()); 可以通过编译, User user; ...
- web自动化工具-开篇
web自动化工具-开篇 最近几年,前端技术风一样的速度迭代更新,各种框架工具雨后春笋般涌现,作为一个平凡的开发者,也只能在洪流中沉沉浮浮,微不足道,以前前端叫做切图仔.美工,如今改了称号叫前端工程师, ...
- C#操作日志
首先引用NLog的dll文件 using System.IO; using NLog; -------------------------------------------------------- ...
- bzoj4692: Beautiful Spacing
先二分答案后dp 设\(su[n]\)为\(\sum_{1}^{n}xi[i]\) 设\(f[n]\)为1时表示第n个单次能做某一行的结尾,且之前的空格满足二分出来的答案. 考虑怎样的\(f[i]\) ...
- flex实验总结
1.父元素 .box{ display:flex; flex-direction: column;//铺满垂直排列 flex-direction: column-reverse;//铺满垂直反向排列 ...