1.TGraphicControl/TcustomControl 与画布(Canvas)
    VCL中,TCotnrol之下的组件分两条路各行其道。一条为图形组件,这类组件并非窗口,职责只在于显示图形、图像,其基类是TGraphicControl;另一条为窗口组件,这类组件本身是一个Windows窗口(有窗口句柄),其基类是TWinControl。
    TGraphicControl作为显示图形、图像的组件分支,从其开始就提供了一个TCanvas类型的Canvas属性,以便在组件上绘制图形、显示图像。
    对于窗口组件的分支,TWinControl并没有提供Canvas属性,而在其派生类TCustomControl才开始提供Canvas属性。
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
                       控件类分支
    TGraphicControl与TCustomControl的实现都在Controls.pas单元中,它们的声明看上去也是如此相似:

核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  TGraphicControl = class(TControl)
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
private
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    FCanvas: TCanvas;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  protected
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    procedure Paint; virtual;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    
property Canvas: TCanvas read FCanvas;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
public
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    constructor Create(AOwner: TComponent); override;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    destructor Destroy; override;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  TCustomControl 
= class(TWinControl)
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
private
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    FCanvas: TCanvas;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  protected
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    procedure Paint; virtual;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    procedure PaintWindow(DC: HDC); override;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    
property Canvas: TCanvas read FCanvas;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
public
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    constructor Create(AOwner: TComponent); override;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    destructor Destroy; override;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
end;

       它们提供了Canvas属性,只不过此时Canvas属性被隐藏在protected节中,它们的派生类可以选择性地将其publish。
由于TGraphicControl与TCustomControl在有关Canvas熟悉的实现上也非常相似,在此只以TGraphicControl的实现来讲解“画布”属性。
由TGraphicControl的声明中的
property Canvas: TCanvas read FCanvas;
可知Canvas是一个只读属性,其载体是private的成员对象FCanvas。FCanvas在TGraphicControl的构造函数中被创建:

核心库类之TGraphicControl/TcustomControl 与画布(Canvas){ TGraphicControl }
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)constructor TGraphicControl.Create(AOwner: TComponent);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)begin
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  inherited Create(AOwner);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  FCanvas :
= TControlCanvas.Create;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  TControlCanvas(FCanvas).Control :
= Self;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)destructor TGraphicControl.Destroy;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)begin
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
if CaptureControl = Self then SetCaptureControl(nil);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  FCanvas.Free;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  inherited Destroy;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)procedure TGraphicControl.WMPaint(var Message: TWMPaint);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)begin
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
if Message.DC <> 0 then
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  begin
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    Canvas.Lock;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    try
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)      Canvas.Handle :
= Message.DC;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)      try
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)        Paint;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)      finally
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)        Canvas.Handle :
= 0;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)      
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    finally
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)      Canvas.Unlock;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)procedure TGraphicControl.Paint;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)begin
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)

       在此需要注意的是,FCanvas在声明时,是被声明为TCanvas类型的,而在创建时,却创建了TControlCanvas的示例。其实,TControlCanvas是TCanvas的派生类,它提供了一些额外的属性和事件来辅助在Control(控件)上提供“画布”属性。
这里暂停一下,先来看一下TcontrolCanvas:
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  TControlCanvas = class(TCanvas)
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
private
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    FControl: TControl;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    FDeviceContext: HDC;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    FWindowHandle: HWnd;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    procedure SetControl(AControl: TControl);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  protected
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    procedure CreateHandle; override;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
public
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    destructor Destroy; override;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    procedure FreeHandle;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    procedure UpdateTextFlags;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    
property Control: TControl read FControl write SetControl;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
end;
       TControlCanvas将Canvas绑定到一个TControl实例上,其内部的FControl指针即指向Canvas所属的TControl实例。
      TCanvas提供了一个空的虚方法CreateHandle()。这个虚方法在TControlCanvas中被覆盖重新实现:
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)procedure TControlCanvas.CreateHandle;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)begin
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
if FControl = nil then inherited CreateHandle else
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  begin
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    
if FDeviceContext = 0 then
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    begin
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)      
with CanvasList.LockList do
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)      try
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)        
if Count >= CanvasListCacheSize then FreeDeviceContext;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)        FDeviceContext :
= FControl.GetDeviceContext(FWindowHandle);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)        Add(Self);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)      finally
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)        CanvasList.UnlockList;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)      
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    Handle :
= FDeviceContext;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    UpdateTextFlags;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
end;

在CreateHandle()方法中,如果FControl是TWinControl或其派生类的实例,即控件本身是窗口,则取得该窗口的设备上下文句柄赋给Handle属性;如果FControl非TWinControl或其派生类的实例,即控件本身并非窗口,则将其父窗口的设备上下文句柄赋给Handle。这些都是通过TControl声明的虚函数GetDeviceContext()实现的,因为TWinControl覆盖重新实现了GetDeviceContext()。
说完TControlCanvas,下面继续刚才的话题。TGraphicControl的构造函数中创建了TControlCanvas实例并赋给FCanvas。构造函数的最后一行代码
TControlCanvas(FCanvas).Control := Self;
将Canvas属性绑定到了控件本身。
然后,TGraphicControl定义了一个处理WM_PAINT Windows消息的消息处理函数:
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
在WMPaint()方法中,根据接受到的消息的参数所给出的窗口的设备上下文句柄,给Canvas属性的Handle重新赋值,并且调用虚方法Paint():
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)procedure TGraphicControl.WMPaint(var Message: TWMPaint);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)begin
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
if Message.DC <> 0 then
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  begin
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    Canvas.Lock;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    try
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)      Canvas.Handle :
= Message.DC;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)      try
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)        Paint;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)      finally
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)        Canvas.Handle :
= 0;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)      
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    finally
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)      Canvas.Unlock;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
end;

虚方法Paint()可以被TGraphicCotnrol的派生类所覆盖,重新定义并实现绘制图形、图像的方法,并且TGraphicControl的派生的实例总是可以放心使用其Canvas属性,而不必自行获得窗口的设备上下文句柄。而虚方法Paint()在TGraphicControl中的实现也只是一个空方法而已。

2.TCustomPanel与窗口重绘
TCustomPanel派生自TCustomControl,是所有Panel类组件的基类。TCustomPanel与4.8节中所述的TGraphicControl非常类似,只是TCustomControl派生自TWinControl,所以它的实例是一个窗口。
TCustomControl与TGraphicControl一样,拥有一个空的虚方法Paint(),以便让派生类决定如何重绘窗口。
现在就来看一下TcustomPanel。它从TCustomControl派生,并且覆盖重新实现了Paint()方法。在此,我们不关心TCustomPanel所实现的其他特性,而只关注其实现的Paint()方法。TCustomPanel实现的Paint()方法负责将组件窗口绘制出一个Panel效果(边框、背景和标题)。先来看一下Paint()方法:
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)procedure TCustomPanel.Paint;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
const
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  Alignments: 
array[TAlignment] of Longint = (DT_LEFT, DT_RIGHT, DT_CENTER);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)var
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  Rect: TRect;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  TopColor, BottomColor: TColor;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  FontHeight: 
Integer;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  Flags: Longint;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  procedure AdjustColors(Bevel: TPanelBevel);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  begin
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    TopColor :
= clBtnHighlight;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    
if Bevel = bvLowered then TopColor := clBtnShadow;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    BottomColor :
= clBtnShadow;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    
if Bevel = bvLowered then BottomColor := clBtnHighlight;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)begin
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  Rect :
= GetClientRect;
      // 画边框
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
if BevelOuter <> bvNone then
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  begin
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    AdjustColors(BevelOuter);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    Frame3D(Canvas, Rect, TopColor, BottomColor, BevelWidth);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  Frame3D(Canvas, Rect, Color, Color, BorderWidth);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
if BevelInner <> bvNone then
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  begin
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    AdjustColors(BevelInner);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    Frame3D(Canvas, Rect, TopColor, BottomColor, BevelWidth);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
with Canvas do
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  begin
        // 画背景
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    Brush.Color :
= Color;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    FillRect(Rect);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    Brush.Style :
= bsClear;
        // 写标题
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    Font :
= Self.Font;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    FontHeight :
= TextHeight('W');
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
    with Rect do
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    begin
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)      Top :
= ((Bottom + Top) - FontHeight) div 2;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)      Bottom :
= Top + FontHeight;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    Flags :
= DT_EXPANDTABS or DT_VCENTER or Alignments[FAlignment];
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    Flags :
= DrawTextBiDiModeFlags(Flags);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)    DrawText(Handle, PChar(Caption), 
-1, Rect, Flags);
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)  
end;
核心库类之TGraphicControl/TcustomControl 与画布(Canvas)
end;

Paint()方法含有一个内嵌函数AdjustColors(),其作用是确定边框的上下线条颜色(一条边框由两个像素宽度的直线构成,形成立体效果)。
TCustomPanel使用其基类(TCustomControl)提供的Canvas属性,覆盖其基类定义的虚方法Paint(),完成了窗口重绘过程。
在自己编写组件时,如果需要在组件表面绘制图形、图像的话,就可以如同TCustomPanel一样,覆盖重新实现Paint()方法。同时,使用基类提供的Canvas属性,对于绘图过程来说,也是非常简单的。

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-08-24
  • 2022-12-23
  • 2021-11-19
  • 2021-12-15
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-11-06
  • 2022-12-23
相关资源
相似解决方案