duilib学习领悟(1)
学习duilib已经有一段时间,一直没时间写总结,今天得出空来,写写心得体会!
由于本人知识有限,若有错误地方,望批评指正.多谢.!
初识duilib
刚开始接触duilib的时候,觉的他好神奇,整个界面只有一个句柄,怎么控制子控件的?怎么布局的?觉得很神奇,打算一探究竟!
其实任何基于Windows的界面开发都离不开五步曲:
- 注册窗口类
- 创建窗口
- 显示更新窗口
- 消息循环
- 退出
duilib也逃不过微软的魔爪! 接下来我会基于这个五步曲逐步接开duilib的神秘面纱!
对diulib熟悉的人,不会觉的duilib有多神秘,so do I! 因为总的来讲,duilib只不过是一种思想,什么思想?
duilib,从本质来讲,就是在一个真实的窗口(_tWinMain里创建的窗口)之上画出各种控件! 这就是duilib的思想. 这个画不是假画,是真画!实打实的画出来的!有些同学可能急着要问:"既然是画出来的,那窗口是怎么处理消息的? 窗口是如何判断当前鼠标点击是哪一个BUTTON?" 等等各种疑问,接下来 我将剖析duilib的框架,接开这两个疑问!
问题回到五步曲的第一步:注册窗口类
duilib将窗口类封装成类CWindowWnd,这个类相当于我们MFC里面的CWnd,里面有窗口类的注册,请看代码:
bool CWindowWnd::RegisterWindowClass()
{
WNDCLASS wc = { };
wc.style = GetClassStyle();
wc.cbClsExtra = ;
wc.cbWndExtra = ;
wc.hIcon = NULL;
wc.lpfnWndProc = CWindowWnd::__WndProc;//注意这个是回调函数
wc.hInstance = CPaintManagerUI::GetInstance();
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = GetWindowClassName();
ATOM ret = ::RegisterClass(&wc);
ASSERT(ret!=NULL || ::GetLastError()==ERROR_CLASS_ALREADY_EXISTS);
return ret != NULL || ::GetLastError() == ERROR_CLASS_ALREADY_EXISTS;
}
那么这个
bool CWindowWnd::RegisterWindowClass()
注册窗口类的函数是在哪里调用的?
在同一个源文件中,不难发现如下代码:
HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, const RECT rc, HMENU hMenu)
{
return Create(hwndParent, pstrName, dwStyle, dwExStyle, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hMenu);
} HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x, int y, int cx, int cy, HMENU hMenu)
{
if( GetSuperClassName() != NULL && !RegisterSuperclass() ) return NULL;
if( GetSuperClassName() == NULL && !RegisterWindowClass() ) return NULL;
m_hWnd = ::CreateWindowEx(dwExStyle, GetWindowClassName(), pstrName, dwStyle, x, y, cx, cy, hwndParent, hMenu, CPaintManagerUI::GetInstance(), this);
ASSERT(m_hWnd!=NULL);
return m_hWnd;
}
那么这个Create函数又是在哪里调用的?请看_tWinMain()里的代码
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
CPaintManagerUI::SetInstance(hInstance); CDuiFrameWnd duiFrame;
duiFrame.Create(NULL, _T("DUIWnd"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
duiFrame.ShowModal();
return ;
}
这样我们就完成了整个窗口类的注册流程. 从这里看,duilib封装的CWindowWnd是不是和MFC的CWnd大同小异呢?
接下来,我带同学们看看CWindowWnd::__WndProc()函数!
这是专门用来处理窗口消息的过程函数!和CWnd::WindowProc()是一样的!只不过CWnd::WindowProc()是个虚函数,我们可以重载这个虚函数,拦截消息.但是CWindowWnd::__WndProc()并没有被定义为虚函数,那么我们是不是没有办法拦截消息了呢?答案是否定的! duilib采用了另外的机制来处理消息!
LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CWindowWnd* pThis = NULL;
if( uMsg == WM_NCCREATE ) {
LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);
pThis->m_hWnd = hWnd;
::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis));
}
else {
pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
if( uMsg == WM_NCDESTROY && pThis != NULL ) {
LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam);
::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L);
if( pThis->m_bSubclassed ) pThis->Unsubclass();
pThis->m_hWnd = NULL;
pThis->OnFinalMessage(hWnd);
return lRes;
}
}
if( pThis != NULL ) {
return pThis->HandleMessage(uMsg, wParam, lParam);
}
else {
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
上面的代码 标红的部分,是可以帮助你理解duilib消息处理机制的最重要部分,不知大家有没有忘记CWindowWnd里的创建窗口的函数Create()中::CreateWindowEx(...,this);函数参数的最后传递的这个this指针!
这个this对于duilib来说相当重要,它是duilib处理消息的基础,那么这个this到底代码什么呢?它代表的是我们在_tWinMain()函数里声明的CDuiFrameWnd duiFrame;这个对象的地址.这个CDuiFrameWnd派生自哪里?就是派生自CWindowWnd,当然为了处理通知消息它还从INotifyUI派生(这个以后再讲为什么要从这个类派生).所以这个传递进来的this指针的首地址也代表了duiFrame在构造基类的对象时的首地址,请耐心体会这句话.
在上面的代码段中,过程函数__WndProc()拦截了WM_NCCREATE,这个消息是::CreateWindowEx被调用之后会发送的消息,然后做了什么?就是把这个this指针取出来,然后保存::SetWindowLongPtr()函数.接着else部分就是将这个this从USER_DATA中取出来,取出来干嘛呢?这个this指针有什么用呢?请接着往下看那个if else语句部分,看到这,你可能还是糊涂,"消息还是没流到我的派生类来啊!",我来帮你解答这个问题,我们看pThis->HandleMessage(uMsg, wParam, lParam); 的这个HandleMessage()也许你已经猜到这个函数肯定是个虚函数,要不取出pThis有什么用?是的,你猜的没错,这个函数就是个虚函数,直指我们的的派生类CDuiFrameWnd重载函数HandleMessage(uMsg, wParam, lParam);也许有同学会问,我不用this指针行不行,我不想把这个this指针保存在USER_DATA里!请注意这个过程函数是个静态的,它不属于任何对象,静态函数要访问非静态的数据,必须通过pThis指针!这是C++的特性!
duilib学习领悟(1)的更多相关文章
- duilib学习领悟(3)
世上本无窗口,窗口只是人的眼睛和电脑屏幕及鼠标键盘相互操作后的视觉效果! 下面我们来看看我们之前讲过的代码: class CDuiFrameWnd : public CWindowWnd, publi ...
- duilib学习领悟(4)
使用duilib创建的主窗口绘制工作全都发生在一个 真实存在的主窗口句柄当中,这个绘制过程稍稍有些复杂,但再复杂也逃不过WM_PAINT消息,所有的绘制工作也都由这个WM_PAINT消息完成.在上几篇 ...
- duilib学习领悟(2)
再次强调,duilib只不过是一种思想! 在上一节中,我剖析了duilib中窗口类的注册,其中遗留两个小问题没有细说的? 第一个问题:过程函数中__WndProc()中有这么一小段代码: pThis ...
- DuiLib学习笔记(二) 扩展CScrollbar属性
DuiLib学习笔记(二) 扩展CScrollbar属性 Duilib的滚动条滑块默认最小值为滚动条的高度(HScrollbar)或者宽度(VScrollbar).并且这个值默认为16.当采用系统样式 ...
- Duilib学习笔记《06》— 窗体基类WindowImpBase
在前面的例子中我们发现,窗口都是继承CWindowWnd.INotifyUI,然后重载相关函数去实现.显然,我们发现窗口的创建流程实际上都是差不多的,主要只是在OnCreate加载的配置文件不同等等… ...
- Duilib学习笔记《05》— 消息响应处理
在Duilib学习笔记<04>中已经知道了如何将窗体显示出来,而如何处理窗体上的事件.消息呢? 一. 系统消息 窗体显示的时候我们就已经说了,窗体是继承CWindowWnd类的,对于窗体的 ...
- Duilib学习笔记《04》— 窗体显示
在前面已经了解了duilib控件以及界面布局相关内容,接下来就要考虑该如何将xml中描述的布局通过界面展现出来.实际上在 Duilib学习笔记<01> 中我们已经简单提到过基本的流程及元素 ...
- Duilib学习笔记《03》— 控件使用
在前面已经对duilib有个一个基本的了解,并且创建了简单的空白窗体.这仅仅只是一个开始,如何去创建一个绚丽多彩的界面呢?这就需要一些控件元素(按钮.文本框.列表框等等)来完善. 一. Duilib控 ...
- duilib学习 --- 360demo 学习
我想通过360demo的学习,大概就能把握duilib的一般用法,同时引申出一些普遍问题,和普遍解决方法.并在此分享一些链接和更多内容的深入学习..... 原谅我是一个菜鸟,什么都想知道得清清楚楚.. ...
随机推荐
- javascript let
es6支持通过let关键字声明属于单独块{}的变量,更好的管理变量作用屿 funtion foo() { var a=1; if (a>1) { let b=2; //只属于if模块 while ...
- bootstrap导航栏PC端移动端之不同样式
在此之前,我先说我之所以要改变网站PC移动双端不同样式的原因. 首先我的网站用到了bootstrap响应式布局,这是我网站的PC端导航栏: 这是我网站的移动端导航栏,看着就难受: 我用谷歌浏览器F12 ...
- 《The C Programming Language》学习笔记
第五章:指针和数组 单目运算符的优先级均为2,且结合方向为自右向左. *ip++; // 将指针ip的值加1,然后获取指针ip所指向的数据的值 (*ip)++; // 将指针ip所指向的数据的值加1 ...
- java学习(东软睿道)2019-09-06(预课)《随堂笔记》
2019-09-06 13:19:56 1.变量:java 名称 2.服务器server 客户端client uft8 ascll 3.Java ...
- oracle分区表原理学习
1.创建普通表 create table normal_shp(id number,day date,city_number number,note varchar2(100)) tablespace ...
- SQLite进阶-14.子查询
目录 子查询 SELECT语句中的子查询 INSERT语句中的子查询 UPDATE语句中的子查询 DELETE语句中的子查询 子查询 子查询或内部查询或嵌套查询是在另一个SQLite查询内嵌入在WHE ...
- 【LOJ】#3102. 「JSOI2019」神经网络
LOJ#3102. 「JSOI2019」神经网络 首先我们容易发现就是把树拆成若干条链,然后要求这些链排在一个环上,同一棵树的链不相邻 把树拆成链可以用一个简单(但是需要复杂的分类讨论)的树背包实现 ...
- Python3 + selenium + Chrome浏览器(webdriver.Chrome()报错)
Python3 + selenium + Chrome浏览器 Error: selenium.common.exceptions.WebDriverException: Message: 'chrom ...
- Pycharm 配置houdini
一.houdini开发环境配置 1.添加Python可执行文件 2.设置代码自动补全 刚刚添加的Python.exe,右侧点击加号,依次添加以上长方形中的文件,路径会根据个人安装路径有所变化,后面的目 ...
- Istio技术与实践01: 源码解析之Pilot多云平台服务发现机制
服务模型 首先,Istio作为一个(微)服务治理的平台,和其他的微服务模型一样也提供了Service,ServiceInstance这样抽象服务模型.如Service的定义中所表达的,一个服务有一个全 ...