CanvasがすべてのVCLコントロールで「非表示」になっているのはなぜですか?
コントロール(ボタン、パネルなど)のキャンバスに何か(簡単にするために三角形など)を描画する基本的な手順を実行したいと思います。
procedure DrawTriangle(Control: TCustomControl);
この関数では、Control.WidthとControl.Heightを使用して、コントロールの大きさを知る必要があります。Canvasが保護されているため、想像以上に難しいことがわかりました。
解決策は、プロシージャ内のコントロールのキャンバスを取得することです。
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の無駄のようです...
だから、私の質問は次のとおりです。
- なぜキャンバスは設計によって隠された(保護された)のですか?
- CPUを無駄にすることなく、これをエレガントに(1つのパラメーターで)解決するにはどうすればよいですか?
現在、Paintメソッドをオーバーライドしていますが、これは、ペイントコードをいくつかの場所で複製することを意味します。もちろん、DrawTriangleはより多くのパラメーター(Canvas、Control Width / Heightなど)を受け取ることができますが、....しかし、公開されたPaintメソッドを使用すると、すべてがはるかにエレガントになります。
回答
質問へのコメントでは、
- このソリューションを
TCustomControl
子孫に制限するだけで十分です。 - 描画プロシージャが単純な関数呼び出しで引数コントロールからキャンバスを取得できれば、それは十分に「エレガント」です。
その場合、次の解決策が可能です。
//
// 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オーバーヘッドが非常に少ない単純な関数呼び出しを使用して、コントロールのキャンバスを取得できます。
完全な例:
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
)を渡すという標準的なアプローチを、いくつかのディメンションとともに使用します。
procedure DrawFrog(ACanvas: TCanvas; const ARect: TRect);
これにより、他のコントロール(TCustomControl
子孫だけでなく)や、プリンターのキャンバスなどにも使用できるようになります。
なぜキャンバスはデザインによって隠されたのですか?
本当に隠されているわけではありませんが、保護されたセクションにあります。これにアクセスするには、関心のあるクラスから新しいクラスを派生させ、Canvasをパブリックとして宣言する必要があります。
アプリケーションレベルでアクセスすることは想定されていないため、プライベートです。
必要なinterposer
ソースでクラスを使用する場合は、コンポーネントをインストールする必要はありません。
Paint
メソッドをオーバーライドして、そこに描画コードを配置することも検討してください。