终于懂了:FWinControls子控件的显示是由Windows来管理,而不是由Delphi来管理(显示透明会导致计算无效区域的方式有所不同——透明的话应减少剪裁区域,所以要进行仔细计算)
在研究TCustomControl的显示过程中,怎么样都找不到刷新FWinControls并重新显示的代码:
procedure TWinControl.PaintHandler(var Message: TWMPaint);
var
I, Clip, SaveIndex: Integer;
DC: HDC;
PS: TPaintStruct;
begin
DC := Message.DC;
if DC = then DC := BeginPaint(Handle, PS);
try
if FControls = nil then PaintWindow(DC) else
begin
SaveIndex := SaveDC(DC);
Clip := SimpleRegion;
for I := to FControls.Count - do
with TControl(FControls[I]) do
if (Visible or (csDesigning in ComponentState) and
not (csNoDesignVisible in ControlStyle)) and
(csOpaque in ControlStyle) then
begin
Clip := ExcludeClipRect(DC, Left, Top, Left + Width, Top + Height);
if Clip = NullRegion then Break;
end;
if Clip <> NullRegion then PaintWindow(DC);
RestoreDC(DC, SaveIndex);
end;
PaintControls(DC, nil);
finally
if Message.DC = then EndPaint(Handle, PS);
end;
end; procedure TWinControl.PaintControls(DC: HDC; First: TControl);
var
I, Count, SaveIndex: Integer;
FrameBrush: HBRUSH;
begin
if DockSite and UseDockManager and (DockManager <> nil) then
DockManager.PaintSite(DC);
if FControls <> nil then
begin
I := ;
if First <> nil then
begin
I := FControls.IndexOf(First);
if I < then I := ;
end;
Count := FControls.Count;
while I < Count do
begin
with TControl(FControls[I]) do
if (Visible or (csDesigning in ComponentState) and
not (csNoDesignVisible in ControlStyle)) and
RectVisible(DC, Rect(Left, Top, Left + Width, Top + Height)) then
begin
if csPaintCopy in Self.ControlState then
Include(FControlState, csPaintCopy);
SaveIndex := SaveDC(DC);
MoveWindowOrg(DC, Left, Top);
IntersectClipRect(DC, , , Width, Height);
Perform(WM_PAINT, DC, );
RestoreDC(DC, SaveIndex);
Exclude(FControlState, csPaintCopy);
end;
Inc(I);
end;
end;
if FWinControls <> nil then
for I := to FWinControls.Count - do
with TWinControl(FWinControls[I]) do
if FCtl3D and (csFramed in ControlStyle) and
(Visible or (csDesigning in ComponentState) and
not (csNoDesignVisible in ControlStyle)) then
begin // 只是绘制边框而已
FrameBrush := CreateSolidBrush(ColorToRGB(clBtnShadow));
FrameRect(DC, Rect(Left - , Top - , Left + Width, Top + Height),
FrameBrush);
DeleteObject(FrameBrush);
FrameBrush := CreateSolidBrush(ColorToRGB(clBtnHighlight));
FrameRect(DC, Rect(Left, Top, Left + Width + , Top + Height + ),
FrameBrush);
DeleteObject(FrameBrush);
end;
end;
就连在TWinControl.UpdateShowing里也找不到相关代码:
procedure TWinControl.UpdateShowing;
var
ShowControl: Boolean;
I: Integer;
begin
ShowControl := (FVisible or (csDesigning in ComponentState) and
not (csNoDesignVisible in ControlStyle)) and
not (csReadingState in ControlState);
if ShowControl then
begin
if FHandle = then CreateHandle;
if FWinControls <> nil then
for I := to FWinControls.Count - do
TWinControl(FWinControls[I]).UpdateShowing;
end;
if FHandle <> then
if FShowing <> ShowControl then
begin
FShowing := ShowControl;
try
Perform(CM_SHOWINGCHANGED, , );
except
FShowing := not ShowControl;
raise;
end;
end;
end; procedure TWinControl.CMShowingChanged(var Message: TMessage);
const
ShowFlags: array[Boolean] of Word = (
SWP_NOSIZE + SWP_NOMOVE + SWP_NOZORDER + SWP_NOACTIVATE + SWP_HIDEWINDOW,
SWP_NOSIZE + SWP_NOMOVE + SWP_NOZORDER + SWP_NOACTIVATE + SWP_SHOWWINDOW);
begin
SetWindowPos(FHandle, , , , , , ShowFlags[FShowing]);
end;
后来在火车上想啊想,忽然灵机一动,明白了这些FWinControls是由Windows来管理,而不是Delphi管理。
一个具有Handle的窗口,不仅仅是Delphi的一部分,并且也是在整个Windows中挂了号的。除去首次显示之外(即上面的SetWindowPos,这个得另外研究),这个Windows窗口什么时候需要刷新显示,是由Windows说了算。而Windows只有发现这个Windows窗口具有无效区域的时候,才会对它进行刷新显示。即Windows系统直接对这个Windows窗口发送WM_PAINT消息,而不需要Delphi在VCL体系内部写代码发送WM_PAINT消息。这就是我始终找不到for I := 0 to FWinControls.Count - 1 do Perform(WM_PAINT, 0, 0);或者UpdateWindow()的原因。
话说是Windows自动判断无效区域才会决定是否刷新这个Windows控件,而造成无效区域的原因有2类:1.程序员调用Invalidate 这类API 2.用户实际操作,造成窗口移动/遮挡/显示等不同的情况。
---------------------------------------------------------------------------------------------------
补充:当一个TWinControl内部包含的图形控件的属性有变化而需要重新显示的时候,Windows就没法知道这些事情了,所以聪明的Delphi在属性变化的时候,就会手动执行:
procedure TControl.Repaint;
var
DC: HDC;
begin
if (Visible) and (Parent <> nil) and
Parent.HandleAllocated then
if csOpaque in ControlStyle then // 不透明(一般情况下)
begin
DC := GetDC(Parent.Handle);
try // 不透明的话,比较简单,使用一个API直接就可以获得新的无效剪裁区域
IntersectClipRect(DC, Left, Top, Left + Width, Top + Height); // API 从当前剪裁区域和指定矩形的交叉区域中,创建一个新的剪裁区域
Parent.PaintControls(DC, Self); // 不管是否具有无效区域,直接发送WM_PAINT要求重绘。我觉得换成调用Self.Update也可以,但是效率会比较低
finally
ReleaseDC(Parent.Handle, DC);
end;
end else // 透明会导致计算无效区域的方式不同
begin
Invalidate; // 透明的话,应减少剪裁区域,所以要进行仔细计算
Update;
end;
end;
这样就强迫父窗口刷新这个图形控件的显示。
如果是Invalidate和Update,其本质不变:
procedure TControl.Invalidate;
begin
InvalidateControl(Visible, csOpaque in ControlStyle);
end; procedure TControl.InvalidateControl(IsVisible, IsOpaque: Boolean);
var
Rect: TRect; function BackgroundClipped: Boolean;
var
R: TRect;
List: TList;
I: Integer;
C: TControl;
begin
Result := True;
List := FParent.FControls;
I := List.IndexOf(Self);
while I > do
begin
Dec(I);
C := List[I];
with C do
if C.Visible and (csOpaque in ControlStyle) then // 不透明需要计算,透明就不用计算了(我懂了,透明就是不用管这个控件所占用的整体区域,而是直接使用API绘制,这样不需要Delphi帮忙管其它东西了)
begin
IntersectRect(R, Rect, BoundsRect); // API 计算交叉区域,R是其返回值
if EqualRect(R, Rect) then Exit;
end;
end;
Result := False;
end; begin
if (IsVisible or (csDesigning in ComponentState) and
not (csNoDesignVisible in ControlStyle)) and (Parent <> nil) and
Parent.HandleAllocated then
begin
Rect := BoundsRect;
InvalidateRect(Parent.Handle, @Rect, not (IsOpaque or
(csOpaque in Parent.ControlStyle) or BackgroundClipped)); // API
end;
end; procedure TControl.Update;
begin
if Parent <> nil then Parent.Update;
end; procedure TWinControl.Update;
begin
if HandleAllocated then UpdateWindow(FHandle); // API
end;
终于懂了:FWinControls子控件的显示是由Windows来管理,而不是由Delphi来管理(显示透明会导致计算无效区域的方式有所不同——透明的话应减少剪裁区域,所以要进行仔细计算)的更多相关文章
- 五种情况下会刷新控件状态(刷新所有子FWinControls的显示)——从DFM读取数据时、新增加子控件时、重新创建当前控件的句柄时、设置父控件时、显示状态被改变时
五种情况下会刷新控件状态(刷新控件状态才能刷新所有子FWinControls的显示): 在TWinControls.PaintControls中,对所有FWinControls只是重绘了边框,而没有整 ...
- Duilib源码分析(五)UI布局—Layout与各子控件
接下来,继续分析duilib之UI布局Layout,目前提供的布局有:VerticalLayout.HorizontalLayout.TileLayout.TabLayout.ChildLayout分 ...
- 2、IOS开发--iPad之仿制QQ空间 (初始化HomeViewController子控件视图)
1.先初始化侧边的duck,效果图: 实现步骤: 2.然后初始化BottomMenu,效果: 步骤: 其实到这里,会出现一个小bug,那就是: 子控件的位置移高了,主要原因是: 逻辑分析图: 问题解决 ...
- OnClick事件的Sender参数的前世今生——TWinControl.WinProc优先捕捉到鼠标消息,然后使用IsControlMouseMsg函数进行消息转发给图形子控件(意外发现OnClick是由WM_LBUTTONUP触发的)
这是一个再普通不过的Button1Click执行体: procedure TForm1.Button1Click(Sender: TObject); begin ShowMessage('I am B ...
- Android 布局之LinearLayout 子控件weight权重的作用详析(转)
关于Android开发中的LinearLayout子控件权重android:layout_weigh参数的作用,网上关于其用法有两种截然相反说法: 说法一:值越大,重要性越高,所占用的空间越大: 说法 ...
- C# WPF 父控件通过使用可视化树找到子控件
在我们使用WPF设计前台界面时,经常会重写数据模板,或者把控件放到数据模板里.但是一旦将控件放到数据模板中,在后台就没有办法通过控件的名字来获取它了,更没办法对它进行操作(例如,隐藏,改变控件的某个值 ...
- 解决ListView中Item的子控件与Item点击事件冲突
常常会碰到在ListView中点击当中一个Item.会一并触发其子控件的点击事件.比如Item中的Button.ImageButton等.导致了点击Item中Button以外区域也会触发Button点 ...
- Android 布局之LinearLayout 子控件weight权重的作用详析
关于Android开发中的LinearLayout子控件权重android:layout_weigh参数的作用,网上关于其用法有两种截然相反说法: 说法一:值越大,重要性越高,所占用的空间越大: 说法 ...
- TCustomControl绘制自己和图形子控件共四步,TWinControl关键属性方法速记
TCustomControl = class(TWinControl) private FCanvas: TCanvas; procedure WMPaint(var Message: TWMPain ...
随机推荐
- (Problem 29)Distinct powers
Consider all integer combinations ofabfor 2a5 and 2b5: 22=4, 23=8, 24=16, 25=32 32=9, 33=27, 34=81, ...
- (C)高级排序法
1.快速排序法 //方法1 从大到小 #include <iostream.h> void run(int* pData,int left,int right) { int i,j; in ...
- ie条件注释还能这样写
通过条件注释给html开始标签定义不同的class, 来区分不同版本的IE,可以在样式表中避免 样式属性hack (如 _margin-top, *float:none ) 注意: IE10+不支持条 ...
- 【Java】:多线程下载
import java.io.InputStream; import java.io.RandomAccessFile; import java.net.URL; import java.net.UR ...
- 基于visual Studio2013解决C语言竞赛题之0404循环求和
题目 解决代码及点评 这道题考验for循环和一个简单的算法 因为每次累加的值有规律,后面一次累加是前面一次累加的两倍 所以可以用简单的循环,计算累加项和累加结果 /************ ...
- Kali Linux 安全渗透教程<第三更>1.2 安全渗透所需工具
了解了渗透測试的概念后.接下来就要学习进行渗透測试所使用的各种工具.在做渗透測试之前.须要先了解渗透所需的工具.渗透測试所需的工具如表1-1所看到的: 表1-1 渗透所需工具 splint unhi ...
- HDU 3060 多边形面积并
Area2 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Subm ...
- 【转】理解RESTful架构
[转]理解RESTful架构 越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这种"互联网软件"采用客户端/服务器模式,建立在分布式体系上,通过互联网通信,具有高延时( ...
- 【Maven】pom.xml 配置 eclipse错误
<!-- servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactI ...
- Android线程和handler
根据视频仿照着写了个demo: package com.wyl.wylthreadtest; import android.graphics.Color; import android.os.Bund ...