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

简介:

     还记得在第二章的时候,我用到了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 ;
复制代码
可见,这个重建的过程,如果你是一个容器控件,内部有很多子控件的话,使用这个方式来实现某些效果,效率是灰常低下的,所以容器类不建议频繁使用重建方法!

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

 


本文转自 不得闲 博客园博客,原文链接:http://www.cnblogs.com/DxSoft/archive/2010/04/30/1724809.html   ,如需转载请自行联系原作者

相关文章
|
2月前
|
JavaScript Java C++
ArkTS揭秘:如何在无‘类’的世界里,用组件与对象构建HarmonyOS应用的奇妙桥梁?
【10月更文挑战第19天】在鸿蒙系统的ArkTS开发中,类和对象的概念类似于传统OOP语言,但融入了声明式UI的特性。本文通过对比Java中的类和对象,详细介绍了如何在ArkTS中定义组件和实例化组件,并展示了实际开发中的应用示例。通过示例代码,读者可以清晰地理解ArkTS中类和对象的模拟方式及其灵活性。
102 1
|
3月前
|
存储 设计模式 编译器
C++(十三) 类的扩展
本文详细介绍了C++中类的各种扩展特性,包括类成员存储、`sizeof`操作符的应用、类成员函数的存储方式及其背后的`this`指针机制。此外,还探讨了`const`修饰符在成员变量和函数中的作用,以及如何通过`static`关键字实现类中的资源共享。文章还介绍了单例模式的设计思路,并讨论了指向类成员(数据成员和函数成员)的指针的使用方法。最后,还讲解了指向静态成员的指针的相关概念和应用示例。通过这些内容,帮助读者更好地理解和掌握C++面向对象编程的核心概念和技术细节。
|
Java Maven Android开发
Android组件化开发(四)--进程保活组件的封装
前面文章我们封装了网络请求组件`lib_nework`和图片加载组件`lib_image_loader`,今天我们来封装一个进程保活的组件`lib_pull_alive`
|
Java Maven Android开发
Android组件化开发(五)--完整版音乐播放组件的封装
前面几篇系列文章我们讲解了`组件化开发`中几个常用功能组件的开发,包括:`网络请求组件`,`图片加载请求组件`,`应用保活组件`。今天我们来封装一个`音乐播放组件`。
|
前端开发
封装库/工具库中重要概念之组件库
前端开发中,封装库和工具库是非常重要的组成部分。它们可以帮助我们提高代码复用性和可维护性,从而缩短开发周期和降低维护成本。在封装库和工具库中,组件库是其中最为重要和常用的一种,因为它们可以帮助我们快速构建复杂的 UI 界面。
263 0
|
前端开发 JavaScript 开发者
封装库/工具库中重要概念之UI框架
UI(User Interface)框架是前端开发中十分重要的一部分,它提供了各种组件和样式,用于构建页面和用户界面。在前端开发中,封装库/工具库可以帮助我们更加高效地使用 UI 框架。
200 0
|
移动开发
ReactNative入门教程-组件生命周期函数
ReactNative入门教程-组件生命周期函数
111 0
ReactNative入门教程-组件生命周期函数
|
JavaScript 前端开发
十二、面向对象实战之封装拖拽对象【上】
前面几篇文章,我跟大家分享了JavaScript的一些基础知识,这篇文章,将会进入第一个实战环节:利用前面几章的所涉及到的知识,封装一个拖拽对象。为了能够帮助大家了解更多的方式与进行对比,我会使用三种不同的方式来实现拖拽。 •不封装对象直接实现; •利用原生JavaScript封装拖拽对象; •通过扩展jQuery来实现拖拽对象。 本文的例子会放置于codepen.io[1]中,供大家在阅读时直接查看。如果对于codepen不了解的同学,可以花点时间稍微了解一下。
140 0
十二、面向对象实战之封装拖拽对象【上】
|
JavaScript 前端开发
十二、面向对象实战之封装拖拽对象【下】
前面几篇文章,我跟大家分享了JavaScript的一些基础知识,这篇文章,将会进入第一个实战环节:利用前面几章的所涉及到的知识,封装一个拖拽对象。为了能够帮助大家了解更多的方式与进行对比,我会使用三种不同的方式来实现拖拽。 •不封装对象直接实现; •利用原生JavaScript封装拖拽对象; •通过扩展jQuery来实现拖拽对象。 本文的例子会放置于codepen.io[1]中,供大家在阅读时直接查看。如果对于codepen不了解的同学,可以花点时间稍微了解一下。
220 0
使用Unity获取所有子对象及拓展方法的使用
这个问题还是比较简单的,无非就是一个for循环就可以全部获取到了,但是我喜欢简单直达,有没有直接就能获取到所有的子对象函数呢,搜了好久都没有,所以我准备写一个扩展函数,来自己补充这个函数,一起来看一下吧。