还记得在第二章的时候,我用到了procedure CreateParams(var Params: TCreateParams);这个函数的吧!为什么我会使用这个函数来实现那个对齐的问题呢!现在就来追根底的来看看!这个过程其实是在构建窗口的时候会调用的,当然我说的这个是针对TWincontrol继承过来的组件说的,从TGraphicontrol等继承过来的是没有这个的。这个函数的产生也是Windows组件库所特有的,如果列为看官有Windows编程的基础,那么这个就很容易理解了,记得,在Windows编程的时,注册这个窗口类之前,我们都会为一个窗口类指定一系列的参数,而这个CreateParams函数就是产生在这个注册过程之前,目的是用来为创建过程指定参数。

在讲CreateParams的来源之前,我们必须简略说说组件由生成到显示在用户面前的这个过程。这是个灰常纠结的问题,纠结到我不晓得怎么去说(当然纠结的主要原因还是本人的水平有限,下面大家就简单看看吧,解说可能有错,欢迎指正)。由于组件都是依托于Form之上的,所以组件要显示出来最首要的是要组件所依托的容器显示出来,那么最首要,我们需要看看Form的创建然后显示出来的过程。至于窗口的创建过程可以参考一下黄叉叉的博客,在这里我在给他的细化一下,便于我们的工作的展开!这个细化应该是在他那个说明的第5步之前,也就是他说的

此处说明一下:


   对 TWinControl 的 Handle 属性的读取会触发 TWinControl.GetHandle;

可以察看 Property Handle; 的声明。

 

5、第四步中对 Handle 进行读取,触发下述序列:(TWinControl)

    Handle->GetHandle->HandleNeeded

 

这个HandleNeeded是在什么时候第一次调用的,其实他不是在GetHandle的时候第一次调用的,而是在窗口显示出来之前,也就是Visible变化的过程中第一次调用的,而这个Visible的变化,是在Delphi读取Form资源文件的属性了之后触发(这个属性读取过程,可以参考Delphi 的持续机制浅探)。我们看看Visible这个属性变化所触发的过程,这个属性定义在TControl中,属性变化对应的过程为

procedure TControl.SetVisible(Value: Boolean);
begin
if FVisible <> Value then
begin
VisibleChanging;
FVisible := Value;
Perform(CM_VISIBLECHANGED, Ord(Value), 0);
RequestAlign;
end;
end;

由此可以看到在属性变化的时候发送了一个CM_VISIBLECHANGE的消息出去,然后我们再去这个消息的触发过程

procedure TWinControl.CMVisibleChanged(var Message: TMessage);
begin
if not FVisible and (Parent <> nil) then RemoveFocus(False);
if not (csDesigning in ComponentState) or
(csNoDesignVisible in ControlStyle) then UpdateControlState;
end;

本过程在TControl中也有,但是在TWinControl中被重写了,所以我这里只列出了TWinControl的,在Visible变化的时候会调用UpdateControlState函数来更新控件状态,然后这个更新过程中调用了另外一个更新控件显示的函数UpdateShowing,我们来看看UpdateShowing这个过程

procedure TWinControl.UpdateShowing;
var
ShowControl: Boolean;
I: Integer;
begin
ShowControl := (FVisible and (not (csDesigning in ComponentState) or not (csDesignerHide in ControlState)) or
((csDesigning in ComponentState) and not (csDesignerHide in ControlState)) and
not (csNoDesignVisible in ControlStyle)) and
not (csReadingState in ControlState) and not (csDestroying in ComponentState);
if ShowControl then
begin //这个时候如果是第一次显示,FHandle为0,就会调用CreateHandle来创建一个窗口句柄了,也就是说
//在这个时候才真真实实的创建Windows的标准控件!
if FHandle = 0 then CreateHandle;
if FWinControls <> nil then
for I := 0 to FWinControls.Count - 1 do
TWinControl(FWinControls[I]).UpdateShowing;//之后会更新属于这个控件容器的所有子控件显示
end;
if FHandle <> 0 then
if FShowing <> ShowControl then
begin
FShowing := ShowControl;
try
SetPerformingShowingChanged(Self);
try
Perform(CM_SHOWINGCHANGED, 0, 0);
finally
ClearPerformingShowingChanged(Self);
end;
except
FShowing := not ShowControl;
raise;
end;
end;
end;

CreateHandle过程中调用了CreateWnd,然后CreateWnd得时候就调用我们上面声明的CreateParams来为标准控件传递参数。上面说了控件的最终容器Form的创建到显示过程,那么我们现在再来说说一般控件的创建显示过程,其实也就和TForm的创建显示过程一样!只是TForm的显示从读取了属性之后触发,而一般控件由他所在的容器触发,也就是上面的UpdateShowing过程中的实现过程,后面会遍历子控件,然后更新他们的显示,第一次显示的时候都会触发CreateHandle的过程,所以Windows组件的真实创建过程实际上应该是在组件的第一次显示的过程中创建,而不是我们调用Create的时候,在Delphi中,我们Create的时候,仅仅是为这个组件提供了一些初始化信息以及各种参数而已。说到这里,那么,第二章中的CreateParams的实现方法也就相当顺其自然了,因为在CreateParams中为Edit指定其他的扩展样式时,实际上Windows的真实Edit控件实际上还没有创建出来。那么当指定了新样式,当他创建出来的时候,就自然具备了我们指定的扩展样式了。然后,我在设置新样式的时候,调用了一个RecreateWnd的方法,这个方法的目的是重建句柄,也就是重建Windows组件,这个函数的实现过程相当简单,仅仅就是发送了一个组件重建的消息CM_RECREATEWND,然后我们看看这个消息过程的实现方法

procedure TWinControl.CMRecreateWnd(var Message: TMessage);
var
WasFocused: Boolean;
begin
WasFocused := Focused;//先保存控件是否是焦点状态
UpdateRecreatingFlag(True);//这个函数,我就不贴他的代码了,从他的代码中,我们可以看出来,这个函数
//的目的是为所有的子控件打上重建的标记
try
DestroyHandle;//释放句柄,同时释放所有子控件的句柄
UpdateControlState;//更新控件状态,这个函数上面已经分析,会建立句柄,同时子控件句柄。
finally
UpdateRecreatingFlag(False);//重建状态完成
end;
if WasFocused and (FHandle <> 0) then
Windows.SetFocus(FHandle);//如果重建成功,并且原先有焦点,就恢复原先的焦点状态
end;

可见,这个重建的过程,如果你是一个容器控件,内部有很多子控件的话,使用这个方式来实现某些效果,效率是灰常低下的,所以容器类不建议频繁使用重建方法!

至此为止,组件的生成过程就讲解完毕,欢迎专家指正!

组件教程指南目录

Delphi 组件开发教程指南(4)组件生成过程(针对TWinControl继承而来的组件)相关推荐

  1. 微搭低代码自定义组件开发教程

    低代码以其组件丰富著称,但是在实际开发的过程中有时候官方组件开发进展也不是特别快,尤其我们需要一些特色的小程序组件时要等官方更新可能来不及.为了保证项目进度,就不得不自己封装一些组件. 正好微搭低代码 ...

  2. 移动端日历插件_小程序日历组件开发教程!

    微信小程序是我们生活中很常用的工具,在一些生活服务方面尤其有用,比如购物.点餐.预定,甚至是查看天气.日历等.小程序日历是很方便也很容易制作的,那么具体怎么做一个微信小程序日历呢?你可以下载安装微信官 ...

  3. android ui组件开发教程,安卓开发入门教程-UI控件_Switch

    什么是Switch Switch是一种用于显示开关状态的UI控件. 基础样例 1.普通开关 效果图 代码 android:layout_width="wrap_content" a ...

  4. 指南-Luat二次开发教程指南-第一个Luat程序

    第一个Luat程序 目录名称 第一个Luat程序 第一个工程 安装编辑器 更改软件各项默认设置 推荐安装的拓展 新建工程 模块化编程 连接硬件 点亮LED小灯 让LED灯闪烁起来 第一个工程 本文将从 ...

  5. Luat模块应用手册-指南-Luat二次开发教程指南-功能开发教程-长连接超低功耗方案

    目录 简介 什么是超低功耗 能降低多少功耗 什么时候用 哪个版本才支持 实现方法 主要命令 示例 实现原理 两种心跳包 实现代码 常见问题 相关资料以及购买链接 相关开发板购买链接 简介 什么是超低功 ...

  6. 指南-Luat二次开发教程指南-功能开发教程-socket

    目录 socket 简介 API说明 实现流程 同步与异步 示例 开机与连接网络 连接服务器 socket发送与接收消息 常见问题 连接服务器失败 最多同时支持多少个连接 socket异常的情况排查 ...

  7. 指南-Luat二次开发教程指南-功能开发教程-功耗管理

    目录 功耗管理 简介 常用模块功耗指标 API说明 3种工作模式: 1. 全功能模式 2. 休眠模式 3. 最少功能模式 实现流程 全功能模式 休眠模式 最少功能模式 示例 全功能模式 休眠模式 最少 ...

  8. Luat模块应用手册-指南-Luat二次开发教程指南-功能开发教程-虚拟AT

    目录 简介 实现方法 示例 常见问题 相关资料以及购买链接 测试源码 相关开发板购买链接 简介 虚拟AT 也叫 VAT , 'V' 的英文就是 virtual 虚拟的意思; ,传统的AT 是上位机何下 ...

  9. 指南-Luat二次开发教程指南-功能开发教程-蓝牙

    目录 蓝牙 简介 1.蓝牙概念 2.蓝牙使用场景 API说明 主模式 从模式 广播模式 实现流程 示例 常见问题 1.需要增加信号强度,用外置天线. 2.没有设置过蓝牙mac地址的模块,mac地址是随 ...

最新文章

  1. setprecision、fixed、showpoint的用法总结(经典!!超经典!!)【转】
  2. Django之Form组件
  3. python prophet 预言家预知未来
  4. openfire(一):使用idea编译openfire4.2.3源码
  5. 侦听127.0.01_Spring 4.2中由注释驱动的事件侦听器
  6. 小哥送一单外卖应该拿多少钱?
  7. yuv420图文详解
  8. oracle复合字段,复合索引 选择频繁的字段,还是选择选择性低的字段 放在前面?...
  9. pandas多行合并一行_Pandas函数妙用
  10. mac install brew
  11. 你靠什么在单位立足?此文堪称经典
  12. 如何查询Oracle性能监控
  13. 判断 失效_复合材料渐进失效Hashin模型及参数详解
  14. 讲座记录《捷联惯导解算的历史及发展》
  15. 利用Python进行游戏脚本编程,不愧是最强的脚本语言
  16. 356T光耦隔离电路和2P4M MX728单向可控硅开关电路的应用
  17. 当下的区块链游戏该如何推广?一文通解全部套路
  18. ArcGis乱码问题集汇总
  19. Bootstrap 组件:页头组件 (page-header)
  20. 浅析Activity启动模式

热门文章

  1. 新研究旨在用“黑箱”算法解决人工智能偏差问题
  2. 打开数“智”化之门,一字之差带来的思考
  3. 程序员的“凡尔赛文学”大赏!
  4. ​Google 鼓励的 13 条代码审查标准,建议收藏!
  5. 【其他】GIT常用原生命令
  6. pythopn 迭代器
  7. io_service work 的作用
  8. JDK1.8快速入门
  9. project euler之甚至斐波那契数字(Even Fibonacci numbers)
  10. 细说Java主流日志工具库