TForm的自绘过程

新建一个空窗体项目,然后运行,此时首先运行:

procedure TApplication.Run;
begin
  FRunning := True;
  try
    AddExitProc(DoneApplication);
    if FMainForm <> nil then
    begin
      case CmdShow of
        SW_SHOWMINNOACTIVE: FMainForm.FWindowState := wsMinimized;
        SW_SHOWMAXIMIZED: MainForm.WindowState := wsMaximized;
      end;
      if FShowMainForm then
        if FMainForm.FWindowState = wsMinimized then
          Minimize else
          FMainForm.Visible := True;
      repeat
        try
          HandleMessage;
        except
          HandleException(Self);
        end;
      until Terminated;
    end;
  finally
    FRunning := False;
  end;
end;

调用 MainForm.WindowState := wsMaximized;
其中 类属性WindowState调用SetWindowState
调用 FMainForm.Visible := True;
其中 类属性Visible调用SetVisible虚函数,间接调用TControl.SetVisible(相当于UpdateWindow API)

第一个步骤:

procedure TCustomForm.SetWindowState(Value: TWindowState);
const
  ShowCommands: array[TWindowState] of Integer = (SW_SHOWNORMAL, SW_MINIMIZE, SW_SHOWMAXIMIZED);
begin
  if FWindowState <> Value then
  begin
    FWindowState := Value;
    if not (csDesigning in ComponentState) and Showing then
      ShowWindow(Handle, ShowCommands[Value]);
  end;
end;

第二个步骤::

procedure TCustomForm.SetVisible(Value: Boolean);
begin
  if fsCreating in FFormState then
    if Value then
      Include(FFormState, fsVisible) else
      Exclude(FFormState, fsVisible)
  else
  begin
    if Value and (Visible <> Value) then SetWindowToMonitor;
    inherited Visible := Value;
  end;
end;

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

然后故事就长了:

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;

procedure TWinControl.UpdateControlState;
var
  Control: TWinControl;
begin
  Control := Self;
  while Control.Parent <> nil do
  begin
    Control := Control.Parent;
    if not Control.Showing then Exit;
  end;
  if (Control is TCustomForm) or (Control.FParentWindow <> 0) then UpdateShowing;
end;

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 = 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
        Perform(CM_SHOWINGCHANGED, 0, 0);
      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, 0, 0, 0, 0, 0, ShowFlags[FShowing]);
end;

---------------------------------------------------------------------------------------------------

调用了ShowWindow API和SetWindowPos API以后(不知道这两个API那个更重要),当系统空闲时(因为没发现调用UpdateWindow API),Windows向TForm1发WM_PAINT消息,由TCustomForm接收:

procedure TCustomForm.WMPaint(var Message: TWMPaint);
var
  DC: HDC;
  PS: TPaintStruct;
begin
  if not IsIconic(Handle) then
  begin
    ControlState := ControlState + [csCustomPaint];
    inherited;
    ControlState := ControlState - [csCustomPaint];
  end
  else
  begin
    DC := BeginPaint(Handle, PS);
    DrawIcon(DC, 0, 0, GetIconHandle);
    EndPaint(Handle, PS);
  end;
end;

在TWinControl.WMPaint函数里下调试点:

procedure TWinControl.WMPaint(var Message: TWMPaint);
var
  DC, MemDC: HDC;
  MemBitmap, OldBitmap: HBITMAP;
  PS: TPaintStruct;
begin
  if not FDoubleBuffered or (Message.DC <> 0) then
  begin
    if not (csCustomPaint in ControlState) and (ControlCount = 0) then
      inherited
    else
      PaintHandler(Message);
  end
  else
  begin
    DC := GetDC(0);
    MemBitmap := CreateCompatibleBitmap(DC, ClientRect.Right, ClientRect.Bottom);
    ReleaseDC(0, DC);
    MemDC := CreateCompatibleDC(0);
    OldBitmap := SelectObject(MemDC, MemBitmap);
    try
      DC := BeginPaint(Handle, PS);
      Perform(WM_ERASEBKGND, MemDC, MemDC);
      Message.DC := MemDC;
      WMPaint(Message);
      Message.DC := 0;
      BitBlt(DC, 0, 0, ClientRect.Right, ClientRect.Bottom, MemDC, 0, 0, SRCCOPY);
      EndPaint(Handle, PS);
    finally
      SelectObject(MemDC, OldBitmap);
      DeleteDC(MemDC);
      DeleteObject(MemBitmap);
    end;
  end;
end;

很明显执行的是 not FDoubleBuffered逻辑,说明TForm的双缓冲默认是关闭的。然后执行PaintHandler

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;

因为是空窗体,所以执行PaintWindow(如有子控件执行PaintControls),即:

procedure TCustomForm.PaintWindow(DC: HDC);
begin
  FCanvas.Lock;
  try
    FCanvas.Handle := DC;
    try
      if FDesigner <> nil then FDesigner.PaintGrid else Paint;
    finally
      FCanvas.Handle := 0;
    end;
  finally
    FCanvas.Unlock;
  end;
end;

最后执行Paint

procedure TCustomForm.Paint;
begin
  if Assigned(FOnPaint) then FOnPaint(Self);
end;
时间: 2024-10-31 04:32:00

TForm的自绘过程的相关文章

TGraphicControl和TCustomControl自绘过程

TGraphicControl = class(TControl) // 这个类实在是简单,因为所有事情都已经委托给它的父Win控件了,只要管自己即可 private FCanvas: TCanvas; // 私有内部画板,不用程序员申请就有了 // 注意区别,其实图形控件没有画自己控件一说(但仍然接受WM_PAINT消息),直接响应消息并绘画即可. // 其父类已经在VCL体系中搭好了框架绘制当前图形控件,它会调用Paint;函数执行程序员的画图动作. // Graphic控件自绘过程: //

[Android FrameWork 6.0源码学习] View的重绘过程

View绘制的三部曲,  测量,布局,绘画今天我们分析测量过程 view的测量是从ViewRootImpl发起的,View需要重绘,都是发送请求给ViewRootImpl,然后他组织重绘在重绘的过程中,有一步就是测量,通过代码来分析测量过程 private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp, final Resources res, final int desiredWind

从浏览器渲染过程看重绘回流

渲染过程 浏览器渲染过程如下: 解析 HTML,生成 DOM 树,解析 CSS,生成 CSSOM 树. 将 DOM 树和 CSSOM 树结合,生成渲染树(Render Tree). Layout(回流): 根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小). Painting(重绘): 根据渲染树以及回流得到的几何信息,得到节点的绝对像素. Display: 将像素渲染到屏幕上. 阻塞问题: 构建 CSSOM 会阻塞浏览器渲染,但不会阻塞解析 HTML 构建 DOM树.

高性能WEB开发:重排与重绘

DOM编程可能最耗时的地方,重排和重绘. 1.什么是重排和重绘 浏览器下载完页面中的所有组件--HTML标记.JavaScript.CSS.图片之后会解析生成两个内部数据结构--DOM树和渲染树. DOM树表示页面结构,渲染树表示DOM节点如何显示.DOM树中的每一个需要显示的节点在渲染树种至少存在一个对应的节点(隐藏的DOM元素disply值为none 在渲染树中没有对应的节点).渲染树中的节点被称为"帧"或"盒",符合CSS模型的定义,理解页面元素为一个具有填充

VC自绘按钮

看了吕鑫老师的视频,自己试着编写了一个自绘按钮控件.YuButton.h头文件如下: #pragma once   #include "afxwin.h"   class CYuButton :public CWnd   {       private:           BOOL  m_bIsDown;           BOOL  m_bIsMove;           BOOL  _bMouseTrack;//鼠标追踪状态              CString  m_s

[Android FrameWork 6.0源码学习] View的重绘过程之Draw

View绘制的三部曲,测量,布局,绘画现在我们分析绘画部分测量和布局 在前两篇文章中已经分析过了.不了解的可以去我的博客里找一下 下面进入正题,开始分析调用以及函数原理 private void performDraw() { if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) { return; } final boolean fullRedrawNeeded = mFullRedrawNeede

Repaints and Reflows 重绘和重排版

当浏览器下载完所有页面HTML标记,JavaScript,CSS,图片之后,它解析文件并创建两个内部数据 一棵DOM树 表示页面结构 Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-st

高性能JavaScript 重排与重绘

转自(学习之用) 作者:韩子迟 网址:http://www.cnblogs.com/zichi/p/4720000.html 先回顾下前文高性能JavaScript DOM编程,主要提了两点优化,一是尽量减少DOM的访问,而把运算放在ECMAScript这一端,二是尽量缓存局部变量,比如length等等,最后介绍了两个新的API querySelector()以及querySelectorAll(),在做组合选择的时候可以大胆使用.而本文主要讲的是DOM编程可能最耗时的地方,重排和重绘. 1.什

手绘在工业设计中的重要位置

手绘对于一个优秀的设计师来说十分重要,它可以在短时间内将设计师的创意表达出来,一个好的设计师应该善于运用手绘来表达自己的设计理念.下面我们听听设计师李培新为我们介绍手绘在工业设计中的重要性. 手绘的作用 一个好的手绘表达是一个优秀设计的开始.当然不是说好的手绘图就是漂亮的手绘预想图,有时侯手绘可能是简单笨拙的笔触,但也能将自己的设计理念表达的淋漓尽致.好的手绘是将设计师的创意想法很快表达出来的一种方式. 好的手绘可以完整的表达出设计师的理念,而不是简单的效果图.作为在校的大学生,学好手绘很关键,