DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: iwithu
今日帖子: 23
在线用户: 13
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 nevergrief (孤独骑士) ★☆☆☆☆ -
盒子活跃会员
2014/8/17 22:12:01
标题:
TControl.InvalidateControl和TWinControl.PaintHandler的两个不懂之处 浏览:1878
加入我的收藏
楼主: 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 > 0 do
    begin
      Dec(I); // 这里!为什么是DEC函数?只比较了一小部分兄弟图形控件?但是FControls的内部元素应该是无序的,应该全部比较才对,所以这样写让我觉得无法理解。
      C := List[I];
      with C do
        if C.Visible and (csOpaque in ControlStyle) then
        begin
          IntersectRect(R, Rect, BoundsRect);
          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)); // 这里!
  end;
end;

另外一个问题,通过实验发现TForm并不默认包含csOpaque,这意味着它是透明的?那对于TForm来说什么叫做透明呢?

==========
procedure TWinControl.PaintHandler(var Message: TWMPaint);
var
  I, Clip, SaveIndex: Integer;
  DC: HDC;
  PS: TPaintStruct;
begin
  DC := Message.DC;
  if DC = 0 then DC := BeginPaint(Handle, PS);
  try
    if FControls = nil then PaintWindow(DC) else // 没有图形子控件直接自绘
    begin
      SaveIndex := SaveDC(DC);
      Clip := SimpleRegion;
      for I := 0 to FControls.Count - 1 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 = 0 then EndPaint(Handle, PS);
  end;
end;

这里PaintHandler里父控件自绘的时候,先要计算所有图形子控件的剪裁区域,这个没问题。但是我注意到,它不需要计算所有FWinControls子控件的剪裁区域,那么这些FWinControls的自绘是如何与父控件联系起来的?还是完全不用管理它们的自绘,因为它们自己也可直接接受Windows的WM_PAINT消息。但至少它们也占了父控件的一部分显示空间,难道就完全放任不管了吗?其实也可减去它们的显示区域提高效率啊。还是有哪个地方我没有理解?
----------------------------------------------
只有偏执狂才能生存!
作者:
男 nevergrief (孤独骑士) ★☆☆☆☆ -
盒子活跃会员
2014/8/18 16:08:56
1楼: 自己顶贴求帮忙解答!
----------------------------------------------
只有偏执狂才能生存!
作者:
男 greenleaf (dudu) ★☆☆☆☆ -
普通会员
2014/8/18 17:12:11
2楼: 第一个问题

   TWinControl.FControl :TList;

   跟踪TWinControl.Insert方法可以看到TWinControl.ListAdd里面的实现就是:

   if List = nil then List := TList.Create
   List.Add(Item);

   测试也可以看出List是有序的. 这里只不过保存的是内存指针(任何类型都可以)

   TControl.FParent 是一个TWinControl对象. 所以引用的:

    List := FParent.FControls;
    I := List.IndexOf(Self);

    这里的I就是按先后顺序加载到WinControl里面的Controls. 为什么使用Dec而不是搜索全部?  

    WinControl上的Controls绘制的时候, 实际上是WinControl按照子Control的顺序要求他们一个一个绘制而成. 假如10个人在一张纸上画图. 你需要管你后面的人会画什么么?
----------------------------------------------
美好生活
作者:
男 greenleaf (dudu) ★☆☆☆☆ -
普通会员
2014/8/18 17:35:25
3楼: 第二个问题

    你说的我理解不是很透.

    在窗口上使用透明, 技术有两种:
    1.使窗口的某个指定颜色全部透明. 穿透.
    2.使窗口的扩展属性增加Layered. 这样可以对颜色的alpha属性识别透明.

    而组件要使用透明属性并不能像窗口一样设置属性, 组件透明其实不是真的透明, 而是把组件父窗的"子窗口区域"的背景复制到子窗口的窗体上.

    再回到你提的问题, 区域裁剪.

    TWinControl的FControls和FWinControls保存的东西为什么要加以区分?  那是因为delphi的组件有一部分并不是真正的窗口组件. 他们没有句柄. 只是普通的TControl. 当然也包括真正由HWND构成的窗口组件.

    至于为什么要裁减Controls的区域你已经明白了(它不是真正的窗口需要手工计算裁减区域提高效率), 你想知道的是为什么没有对FWinControls的区域裁减. 因为TWinControl继承的组件是真正的windows窗口. 这些窗口可以自己完成绘制功能. 你可以在创建一个窗口时, 指定style为WS_CLIPCHILDREN. 这样就会自动在绘制的时候, 裁减掉所有子窗口的区域进行绘制.
----------------------------------------------
美好生活
作者:
男 nevergrief (孤独骑士) ★☆☆☆☆ -
盒子活跃会员
2014/8/18 17:37:49
3楼: 不错,感谢greenleaf的回答。但还是有问题,FControls之间的顺序是添加时候的顺序,但FControls的显示Z轴顺序可不一定是添加顺序,我的问题来源于这里。还望继续执教。
----------------------------------------------
只有偏执狂才能生存!
作者:
男 greenleaf (dudu) ★☆☆☆☆ -
普通会员
2014/8/18 17:56:02
4楼: TWinControl.PaintHandler中调用了PaintControls(DC, nil);

TWinControl.PaintControls代码你跟进去看一看.
// 绘制代码就是先保存已经绘制好的DC,然后裁减掉子组件的区域, 要求它自己绘制. 循环完所有的子组件. 这个主DC只不过是父窗口的内存DC. 全部绘制完成后贴到实际的表层DC上.


他是按照你指定的FControls的自然顺序开始绘制. 默认的是从第一个开始绘制.

显示顺序就是添加的顺序. 
 
   你的所有组件在父组件中, 都是有自然顺序的. 通过InsertControl和RemoveControl来操作.  实际看你也可以看到, 后加进去的组件总是覆盖前加入的组件.
----------------------------------------------
美好生活
作者:
男 nevergrief (孤独骑士) ★☆☆☆☆ -
盒子活跃会员
2014/8/18 17:56:30
4楼: 不好意思,我的问题有点混乱。透明不透明(我很奇怪,为什么TForm并不默认包含csOpaque),是另外附带的一个问题,不是我的第二个问题。

我的第二个问题是,为了画某个WinControl,要减去所有FControls的区域,但是为什么不减去FWinControls的区域?至少这样可以提高效率。
你的理解好像有点错误,PaintHandler函数计算半天,只是为了画某一个WinControl(注意不是复数),画其FControls在PaintControls函数里实现。
----------------------------------------------
只有偏执狂才能生存!
作者:
男 nevergrief (孤独骑士) ★☆☆☆☆ -
盒子活跃会员
2014/8/18 18:04:05
5楼: PaintControls InsertControl 这些代码都看了。但是比如,我先后添加了Label1和Label2,而且有部分重叠或者完全重合,但是我在代码里写了一句Label1.BringToFront 此时,BackgroundClipped不就出错了?
----------------------------------------------
只有偏执狂才能生存!
作者:
男 greenleaf (dudu) ★☆☆☆☆ -
普通会员
2014/8/18 18:10:45
6楼: BringToFront 只不过是调整了FList的顺序, 先删除掉它的当前位置. 然后插入到你指定的Position的位置.

TList的实现, 难道控制一个插入和删除顺序都不容易吗? 用一个链表太容易实现了.

你在调用BringToFront时, 这个时候会先调整顺序, 不会进行绘制. 根本不会掉你调用BackgroundClipped的检测.
----------------------------------------------
美好生活
作者:
男 nevergrief (孤独骑士) ★☆☆☆☆ -
盒子活跃会员
2014/8/18 18:15:47
7楼: 好吧,我懂了。第一个问题,我追踪了一大堆Paint的源码,但是没有追踪BringToFront导致不理解。其实BringToFront调用了SetZOrder,SetZOrder调用了SetZOrderPosition,后者当场调整了FControls的顺序。现在明白了,多谢。
----------------------------------------------
只有偏执狂才能生存!
作者:
男 greenleaf (dudu) ★☆☆☆☆ -
普通会员
2014/8/18 18:27:43
7楼: TWinControl.PaintHandler是绘制处理.

它不是为了绘制某个单个WinControl. 而是绘制全部的子组件, 具体的说是绘制FControls. 而FWinControls的处理由它自己处理. 你再好好看看吧.
----------------------------------------------
美好生活
作者:
男 nevergrief (孤独骑士) ★☆☆☆☆ -
盒子活跃会员
2014/8/18 19:43:59
8楼: 我之前说的不准确,TWinControl.PaintHandler是绘制全部的子组件和组件自己。但是计算Clip 只是为了绘制组件自己,这样何不把FWinControls的区域也减去呢?
----------------------------------------------
只有偏执狂才能生存!
作者:
男 hs_kill (lzl_17948876) ★☆☆☆☆ -
普通会员
2014/8/19 9:03:25
9楼: 不包含FWinControls首先是因为Win窗体本身有句柄, 可以自己响应WM_PAINT消息实现自绘, 而FControls默认是不包含句柄的, 所以需要其父WinControl去通知自绘(PaintControls函数的作用)

其次, 不包含FWinControls区域是因为无法得知其自绘方式以及是否有透明等csOpaque只是VCL自绘时是否刷新背景的控制, 而WinControl除了这个还有window style/WM_ERASEBKGND等几个方式来控制是否重绘背景, 所以不能直接就减去其区域, 否则如果某个WinControl需要父窗体背景 就画不出来了

而FControl的背景透明控制完全是通过csOpaque来得到的, 所以你应该会发现
if (Visible or (csDesigning in ComponentState) and
          not (csNoDesignVisible in ControlStyle)) and
          (csOpaque in ControlStyle) then
这里只减去了重绘背景的Control
----------------------------------------------
http://www.cnblogs.com/lzl_17948876/
作者:
男 nevergrief (孤独骑士) ★☆☆☆☆ -
盒子活跃会员
2014/8/19 13:04:16
10楼: 嗯,感谢hs_kill兄的精彩回复。看来我有空还得仔细看看Windows编程基础书籍。
----------------------------------------------
只有偏执狂才能生存!
作者:
男 nevergrief (孤独骑士) ★☆☆☆☆ -
盒子活跃会员
2014/8/19 13:17:11
11楼: 干脆,继续厚着脸皮提问,我注意到TControl.InvalidateControl函数里,第三个参数的第二种条件:
InvalidateRect(Parent.Handle, @Rect, not (IsOpaque or
      (csOpaque in Parent.ControlStyle) or BackgroundClipped)); 
即 csOpaque in Parent.ControlStyle; 有什么意义,什么情况下会出现这种情况(这个条件会成立)?
我查了一下,整个Forms单元里都没有用到csOpaque。如果一个TForm是“透明”的,那它代表了什么含义呢?
----------------------------------------------
只有偏执狂才能生存!
作者:
男 nevergrief (孤独骑士) ★☆☆☆☆ -
盒子活跃会员
2014/8/19 14:27:35
12楼: 不好意思,忽然明白了。主要是每次做实验,都是TForm做Parent,所以思维僵化了。Parent不一定是TForm,而且可能透明也可能不透明,原因就是你说的:WinControls区域是因为无法得知其自绘方式以及是否有透明等。再次感谢。
----------------------------------------------
只有偏执狂才能生存!
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行101.5625毫秒 RSS