为什么“隐藏"画布?在所有VCL控件中? [英] Why Canvas is "hidden" in all VCL controls?

查看:49
本文介绍了为什么“隐藏"画布?在所有VCL控件中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想做一个基本的过程,在任何控件(按钮,面板等)的画布上绘制一些东西(为简单起见,假设是一个三角形):

I want to do a basic procedure that draws something (let's say a triangle, for simplicity) on any control's (button, panel, etc) canvas:

procedure DrawTriangle(Control: TCustomControl);

在此功能中,我需要使用Control.Width&Control.Height知道控件有多大.事实证明,这比想象中的要困难得多,因为Canvas受了保护.

In this function I need to use Control.Width & Control.Height to know how big is the control. Turns out to be more difficult than imagined because Canvas is protected.

一种解决方案是在程序内部获取控件的画布:

A solution would be to obtain the canvas of the control inside the procedure:

VAR
   ParentControl: TWinControl;
   canvas: TCanvas;
begin
 ParentControl:= Control.Parent;
 Canvas:= TCanvas.Create;
 TRY
  Canvas.Handle:= GetWindowDC(ParentControl.Handle);
  WITH Canvas DO
    xyz
 FINALLY
   FreeAndNil(canvas);
 END;
end;

但是每次我想画一些东西时,似乎浪费了CPU来创建和销毁画布...

But seems such a waste of CPU to create and destroy a canvas each time I want to paint something...

所以,我的问题是:

  1. 为什么画布被设计隐藏(保护)?
  2. 如何优雅地解决这个问题(一个参数)而又不浪费CPU?

现在,我将重写Paint方法,但这意味着在几个地方重复绘制代码.当然,DrawTriangle可以接收更多参数(画布,控制宽度/高度等),....但是,好吧……使用公开的Paint方法,一切都会变得更加优雅.

Now I am overriding the Paint method, but this means duplication the painting code in several places. Of course, the DrawTriangle could receive more parameters (Canvas, Control Width/Height etc), .... but well... with an exposed Paint method, everything would have been so much more elegant.

推荐答案

在对该问题的评论中,事实证明

In a comment to the question it turns out that

  1. 此解决方案仅限于 TCustomControl 后代,并且
  2. 这是优雅"的,如果绘图过程可以通过简单的函数调用从参数控件中获取画布,则足够了.

如果是这样,则可以采用以下解决方案:

If so, the following solution is possible:

//
// Infrastructure needed
//

type
  TCustomControlCracker = class(TCustomControl)
  end;

function CustomControlCanvas(AControl: TCustomControl): TCanvas;
begin
  Result := TCustomControlCracker(AControl).Canvas;
end;

//
// My reusable drawing functions
// (Can only be used in TCustomControl descendants)
//

procedure DrawFrog(AControl: TCustomControl);
var
  Canvas: TCanvas;
begin
  Canvas := CustomControlCanvas(AControl);
  Canvas.TextOut(10, 10, 'Frog');
end;

请注意, DrawFrog 仅采用单个参数,即控件本身.然后,它可以使用简单的函数调用来获得控件的画布,而CPU开销却很少.

Notice that DrawFrog only takes a single parameter, the control itself. And it can then obtain the control's canvas using a simple function call with extremely little CPU overhead.

完整示例:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type
  TTestControl = class(TCustomControl)
  protected
    procedure Paint; override;
  end;

type
  TCustomControlCracker = class(TCustomControl)
  end;

function CustomControlCanvas(AControl: TCustomControl): TCanvas;
begin
  Result := TCustomControlCracker(AControl).Canvas;
end;

procedure DrawFrog(AControl: TCustomControl);
var
  Canvas: TCanvas;
begin
  Canvas := CustomControlCanvas(AControl);
  Canvas.TextOut(10, 10, 'Frog');
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  with TTestControl.Create(Self) do
  begin
    Parent := Self;
    Top := 100;
    Left := 100;
    Width := 400;
    Height := 200;
  end;
end;

{ TTestControl }

procedure TTestControl.Paint;
begin
  inherited;
  Canvas.Brush.Color := clSkyBlue;
  Canvas.FillRect(ClientRect);
  DrawFrog(Self); // use my reusable frog-drawing function
end;

end.


尽管如此,我个人仍然会使用传递 TCanvas (甚至是 HDC )而不是控件的标准方法,以及一些尺寸:


All this being said, however, I would personally still use the standard approach of passing a TCanvas (or even a HDC) instead of a control, together with some dimensions:

procedure DrawFrog(ACanvas: TCanvas; const ARect: TRect);

这将使我也可以将其用于其他控件(不仅是 TCustomControl 后代),还可以用于打印机画布等.

This will allow me to use it for other controls as well (not only TCustomControl descendants), as well as printer canvases etc.

这篇关于为什么“隐藏"画布?在所有VCL控件中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆