ParentWindow属性及其一系列函数的作用——适合于那些不需要父控件管理内存释放的子控件
TWinControl = class(TControl)
property ParentWindow: HWnd read FParentWindow write SetParentWindow;
// 注意它的参数是windoows句柄,而不是Win控件,适合于那些不需要父控件管理内存释放的子控件
 // 哲学,这个函数极少被用到(它是Delphi的写属性),只有菜单,ActiveX,THintWindow,TOpenPictureDialog,TOleForm和TShadowWindow用到
  // important 重点是,研究一下,它与SetParent有什么区别就知道了:不加入父控件的子控件列表。而且此函数的参数是一个Windows句柄,不是Win控件
constructor CreateParented(ParentWindow: HWnd); // 创建一个没有Parent的Windows控件,只有ActiveX里有一处调用
    class function CreateParentedControl(ParentWindow: HWnd): TWinControl; // 搜遍所有VCL源代码,它从未被使用
// 另外还想到一点:之所以这样做,也许为了让Application专门管理所有TForm的实例,别的Windows句柄可以挂载到Application.Handle,但不允许让别的Win控件挂载到Application(待验证)
function TWinControl.GetParentHandle: HWnd;
begin
if Parent <> nil then // important 优先使用Parent属性,然后再检查ParentWindow属性
Result := Parent.Handle
else
Result := ParentWindow; // 类函数,没有句柄会在这个函数里申请
end; function TWinControl.GetTopParentHandle: HWnd;
var
C: TWinControl;
begin
C := Self;
while C.Parent <> nil do
C := C.Parent;
Result := C.ParentWindow;
if Result = 0 then Result := C.Handle;
end; procedure TWinControl.SetFocus;
var
Parent: TCustomForm;
begin
Parent := GetParentForm(Self);
if Parent <> nil then
Parent.FocusControl(Self) // Form类函数,让Form来设置键盘焦点
else if ParentWindow <> 0 then
Windows.SetFocus(Handle) // API,为了避免混淆函数名,加上了单元名
else
ValidParentForm(Self); // 全局函数
end; constructor TWinControl.CreateParented(ParentWindow: HWnd);
begin
// Creates and initializes a control as the child of a specified non-VCL container.
// Call CreateParented to embed a new control in a non-VCL parent window.
// TActiveXControl objects call CreateParented to create an ActiveX control as a child of the host application's client site window.
// 外部传入句柄后,不重新创建,但覆盖属性(没彻底搞清楚)
FParentWindow := ParentWindow; // 给类实例添加句柄后重新创建?这里不是Delphi属性赋值,不会引起连锁反应
Create(nil); // fixme 貌似把之前的许多属性给弄没了,重新创建并赋值一份。这样调用构造函数,有意思。
end; class function TWinControl.CreateParentedControl(ParentWindow: HWnd): TWinControl;
begin
// 外部传入句柄后,重新创建
// 类函数,所以完全重新创建一个类实例,并返回这个类实例
Result := TWinControl(NewInstance); // 有实例(内存)不一定有句柄,仍需要外部传入
Result.FParentWindow := ParentWindow;
Result.Create(nil);
end;
最关键的函数:
procedure TWinControl.SetParentWindow(Value: HWnd);
begin
// 哲学,这个函数极少被用到(它是Delphi的写属性),只有菜单,ActiveX,THintWindow,TOpenPictureDialog,TOleForm和TShadowWindow用到
// important 重点是,研究一下,它与SetParent有什么区别就知道了:不加入父控件的子控件列表。而且此函数的参数是一个Windows句柄,不是Win控件 // 只有在Parent等于空的情况才使用
if (FParent = nil) and (FParentWindow <> Value) then
begin
// fixme 不清楚各种控件是如何使用这个函数的
// 当前句柄不为空,当前父窗口不为空,传来新的值要进行更换
if (FHandle <> 0) and (FParentWindow <> 0) and (Value <> 0) then
begin
// 简单更换父句柄
FParentWindow := Value; // 简单赋值,因为是变量,不是Delphi属性,不会引起连锁反应
Windows.SetParent(FHandle, Value); // API
// 通知一下
if (Win32MajorVersion >= 5) and (Win32Platform = VER_PLATFORM_WIN32_NT) then
Perform(WM_CHANGEUISTATE, MakeWParam(UIS_INITIALIZE, UISF_HIDEACCEL or UISF_HIDEFOCUS), 0);
end
// 如果Fhandle等于0,FParentWindow等于0(不可能,因为有外部if语句控制),或者Value=0(关键是这句,一旦新值是0,就要销毁句柄),就会执行:
else
begin
DestroyHandle; // 区别就在这句
FParentWindow := Value; // 简单赋值,因为是变量,不是Delphi属性,不会引起连锁反应
end;
// 最后都要更新状态
UpdateControlState; // 更新状态,没有句柄就会申请句柄(销毁后,马上申请新的句柄)
end;
end;
使用ParentWindow的所有代码都在这里了(这里没有列出ActiveX里的情况,因为实在太特殊,也很少用到):
procedure THintWindow.ActivateHint(Rect: TRect; const AHint: string);
type
TAnimationStyle = (atSlideNeg, atSlidePos, atBlend);
const
AnimationStyle: array[TAnimationStyle] of Integer = (AW_VER_NEGATIVE, AW_VER_POSITIVE, AW_BLEND);
var
Animate: BOOL;
Style: TAnimationStyle;
begin
FActivating := True;
try
Caption := AHint;
Inc(Rect.Bottom, 4);
UpdateBoundsRect(Rect);
if Rect.Top + Height > Screen.DesktopHeight then
Rect.Top := Screen.DesktopHeight - Height;
if Rect.Left + Width > Screen.DesktopWidth then
Rect.Left := Screen.DesktopWidth - Width;
if Rect.Left < Screen.DesktopLeft then Rect.Left := Screen.DesktopLeft;
if Rect.Bottom < Screen.DesktopTop then Rect.Bottom := Screen.DesktopTop;
SetWindowPos(Handle, HWND_TOPMOST, Rect.Left, Rect.Top, Width, Height, SWP_NOACTIVATE); // API
if (GetTickCount - FLastActive > 250) and (Length(AHint) < 100) and Assigned(AnimateWindowProc) then
begin
SystemParametersInfo(SPI_GETTOOLTIPANIMATION, 0, @Animate, 0);
if Animate then
begin
SystemParametersInfo(SPI_GETTOOLTIPFADE, 0, @Animate, 0);
if Animate then
Style := atBlend
else
if Mouse.GetCursorPos.Y > Rect.Top then
Style := atSlideNeg
else
Style := atSlidePos;
AnimateWindowProc(Handle, 100, AnimationStyle[Style] or AW_SLIDE);
end;
end;
ParentWindow := Application.Handle; // important 真是猛!
ShowWindow(Handle, SW_SHOWNOACTIVATE); // API
Invalidate;
finally
FLastActive := GetTickCount;
FActivating := False;
end;
end; procedure TCustomActionPopupMenu.Popup(X, Y: Integer);
begin
if ItemCount = 0 then exit;
ParentWindow := Application.Handle;
FRootMenu := Self;
if FindFirstVisibleItem = nil then
Expand(False);
SetBounds(X, Y, Width, Height);
PersistentHotKeys := True;
Visible := True;
TrackMenu;
end; constructor TShadowWindow.Create(AOwner: TComponent);
begin
inherited;
Side := csRight;
FDeskTop := TBitmap.Create;
FDesktop.HandleType := bmDDB;
FDesktop.PixelFormat := pf24bit;
Hide;
FCachedclr := 0;
FCachedFade := 0;
ParentWindow := Forms.Application.Handle;
end; procedure TCustomActionBar.WMContextMenu(var Message: TWMContextMenu);
var
PopupMenu: TCustomActionPopupMenu;
begin
inherited;
if Assigned(ActionClient) and (ActionClient.ContextItems.Count > 0) then
begin
PopupMenu := GetPopupMenuClass.Create(Owner) as TCustomActionPopupMenu;
PopupMenu.ContextBar := True;
PopupMenu.ParentWindow := Application.Handle;
PopupMenu.Parent := Self;
PopupMenu.ActionClient := ActionClient;
PopupMenu.Popup(Message.XPos, Message.YPos);
PopupMenu.Free;
end;
end; procedure TOpenPictureDialog.DoShow;
var
PreviewRect, StaticRect: TRect;
begin
{ Set preview area to entire dialog }
GetClientRect(Handle, PreviewRect);
StaticRect := GetStaticRect;
{ Move preview area to right of static area }
PreviewRect.Left := StaticRect.Left + (StaticRect.Right - StaticRect.Left);
Inc(PreviewRect.Top, 4);
FPicturePanel.BoundsRect := PreviewRect;
FPreviewButton.Left := FPaintPanel.BoundsRect.Right - FPreviewButton.Width - 2;
FImageCtrl.Picture := nil;
FSavedFilename := '';
FPaintPanel.Caption := srNone;
FPicturePanel.ParentWindow := Handle;
inherited DoShow;
end; function TOleForm.SetActiveObject(const ActiveObject: IOleInPlaceActiveObject;
pszObjName: POleStr): HResult;
var
Window, ParentWindow: HWnd;
begin
Result := S_OK;
FActiveObject := ActiveObject;
if FActiveObject = nil then Exit;
if FActiveObject.GetWindow(Window) = 0 then
while True do
begin
ParentWindow := GetParent(Window);
if ParentWindow = 0 then Break;
if FindControl(ParentWindow) <> nil then
begin
SetWindowPos(Window, HWND_TOP, 0, 0, 0, 0,
SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE);
Break;
end;
Window := ParentWindow;
end;
FSaveWidth := FForm.ClientWidth;
FSaveHeight := FForm.ClientHeight;
end;
---------------------------------------------------------------------------------------
而且还牵扯到了WM_CHANGEUISTATE消息,搜遍所有VCL源码,没有发现哪个控件响应此消息:
procedure TWinControl.UpdateUIState(CharCode: Word);
var
Form: TCustomForm;
begin
Form := GetParentForm(Self); // 全局函数
if Assigned(Form) then
case CharCode of
VK_LEFT..VK_DOWN, VK_TAB:
Form.Perform(WM_CHANGEUISTATE, MakeLong(UIS_CLEAR, UISF_HIDEFOCUS), 0);
VK_MENU:
Form.Perform(WM_CHANGEUISTATE, MakeLong(UIS_CLEAR, UISF_HIDEACCEL), 0);
end;
end;
就此打个伏笔,也许将来哪天会用到~
ParentWindow属性及其一系列函数的作用——适合于那些不需要父控件管理内存释放的子控件的更多相关文章
- WPF布局控件与子控件的HorizontalAlignment/VerticalAlignment属性之间的关系
		
WPF布局控件与子控件的HorizontalAlignment/VerticalAlignment属性之间的关系: 1.Canvas/WrapPanel控件: 其子控件的HorizontalAlign ...
 - python描述符(descriptor)、属性(property)、函数(类)装饰器(decorator )原理实例详解
		
1.前言 Python的描述符是接触到Python核心编程中一个比较难以理解的内容,自己在学习的过程中也遇到过很多的疑惑,通过google和阅读源码,现将自己的理解和心得记录下来,也为正在为了该问题 ...
 - super函数的作用
		
super函数的作用super().__init__()当子类重写父类的方法时,会覆盖父类方法,super此举是保留父类 如果属性名跟方法名相同,属性会覆盖方法 方法必须要有实例才能被调用,这叫做绑定
 - MFC中的Invalidate、OnDraw、OnPaint函数的作用
		
MFC中的Invalidate.OnDraw.OnPaint函数的作用 CWnd::Invalidate voidInvalidate( BOOL bErase = TRUE ); 该函数的作用是使 ...
 - Python的特殊属性和魔法函数
		
python中有很多以下划线开头和结尾的特殊属性和魔法函数,它们有着很重要的作用. 1.__doc__:说明性文档和信息,python自建,不需要我们定义. # -*- coding:utf- -*- ...
 - python中的 dir()内置函数的作用以及使用方法
		
dir() 内置函数的作用 python 内置方法有很多,无论是初学者还是精通python 的程序员都不能全部即住所有的方法,这时候 dir() 方法就非常有用了,使用 dir()函数可以查看对象内的 ...
 - 15-static和extern关键字1-对函数的作用
		
一.extern与函数 如果一个程序中有多个源文件(.c),编译成功会生成对应的多个目标文件(.obj),这些目标文件还不能单独运行,因为这些目标文件之间可能会有关联,比如a.obj可能会调用c.ob ...
 - PHP通用的XSS攻击过滤函数,Discuz系统中 防止XSS漏洞攻击,过滤HTML危险标签属性的PHP函数
		
XSS攻击在最近很是流行,往往在某段代码里一不小心就会被人放上XSS攻击的代码,看到国外有人写上了函数,咱也偷偷懒,悄悄的贴上来... 原文如下: The goal of this function ...
 - C++之虚函数的作用和使用方法
		
在同一类中是不能定义两个名字相同.参数个数和类型都相同的函数的,否则就是“重复定义”.但是在类的继承层次结构中,在不同的层次中可以出现名字相同.参数个数和类型都相同而功能不同的函数.例如在例12.1( ...
 
随机推荐
- Android 图标上面添加提醒(一)使用Canvas绘制
			
版权声明:本文为博主原创文章,未经博主允许不得转载. 在我们开发一些如通讯录.社交等应用或者应用添加新功能模块时,会考虑在对应的图标上加上未读信息的数量,或者是新功能提醒的图标,这样不占太大空间还能达 ...
 - [转] 使用Git进行小项目代码管理
			
http://www.uml.org.cn/pzgl/201206155.asp 之前在公司使用过SVN(无甚感觉)和ClearCase(把人恶心死的东西)两种版本控制工具,都不满意.后来想自己写点东 ...
 - 移动端 设置 小于12px 字体 初探
			
1.移动端字号规范 2. 百度字号调研 3. 绕过12px 限制 4. 缩放 5. chrome 字号
 - git常见指令
			
master : 默认开发分支: origin : 默认远程版本库 初始化操作 $ git config -global user.name <name> #设置提交者名字 $ ...
 - json、xml ---- 数据格式生成类
			
自己写的一个生成json/xml 格式数据的类,可用于api数据传输: <?php class Response{ /** *生成指定数据格式 *@param intval $code 状态码 ...
 - linux进程地址空间详解(转载)
			
linux进程地址空间详解(转载) 在前面的<对一个程序在内存中的分析 >中很好的描述了程序在内存中的布局,这里对这个结果做些总结和实验验证.下面以Linux为例(实验结果显示window ...
 - cas sso单点登录系列8_抛弃Https让Cas以Http协议提供单点登录服务
			
转:http://blog.csdn.net/ycyk_168/article/details/18668951 本文环境: 1.apache-tomcat-7.0.50-windows-x86 2. ...
 - 中级Perl 第三章课后习题
			
3. 10. 1. 练习1 [25 分钟] 读当前目录的文件列表并转换成全路径.不能用shell 命令或外部程序读当前目 录.Perl 的File::Spec 和Cwd 两个模块对这个程序有帮助.每个 ...
 - 常见ORACLE错误,及解决方案(遇则即时更新)
			
1.当登陆时提示“ORA-03113:通信通道的文件结束”时: 解决方案: 需在X:\oraclexe\app\oracle\product\10 ...
 - ZOJ3870 Team Formation
			
/** Author: Oliver ProblemId: ZOJ3870 Team Formation */ /* 思路 1.异或运算,使用^会爆,想到二进制: 2.我们可以试着从前往后模拟一位一位 ...