Mengapa Canvas "tersembunyi" di semua kontrol VCL?
Saya ingin melakukan prosedur dasar yang menggambar sesuatu (katakanlah segitiga, untuk kesederhanaan) pada kanvas kontrol (tombol, panel, dll):
procedure DrawTriangle(Control: TCustomControl);
Dalam fungsi ini saya perlu menggunakan Control.Width & Control.Height untuk mengetahui seberapa besar kontrolnya. Ternyata lebih sulit dari yang dibayangkan karena Canvas dilindungi.
Solusinya adalah mendapatkan kanvas kontrol di dalam prosedur:
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;
Tapi sepertinya membuang-buang CPU untuk membuat dan menghancurkan kanvas setiap kali saya ingin melukis sesuatu ...
Jadi, pertanyaan saya adalah:
- Mengapa kanvas disembunyikan (dilindungi) oleh desain?
- Bagaimana mengatasi ini dengan elegan (satu parameter tunggal) dan tanpa membuang CPU?
Sekarang saya mengganti metode Paint, tetapi ini berarti duplikasi kode lukisan di beberapa tempat. Tentu saja, DrawTriangle dapat menerima lebih banyak parameter (Canvas, Control Width / Height dll), .... tapi yah ... dengan metode Paint yang terbuka, semuanya akan jauh lebih elegan.
Jawaban
Dalam komentar atas pertanyaan itu ternyata
- solusi ini cukup dibatasi untuk
TCustomControl
keturunan, dan - itu cukup "elegan" jika prosedur menggambar bisa mendapatkan kanvas dari kontrol argumen dengan pemanggilan fungsi sederhana.
Jika demikian, solusi berikut mungkin dilakukan:
//
// 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;
Perhatikan bahwa DrawFrog
hanya membutuhkan satu parameter, kontrol itu sendiri. Dan kemudian dapat memperoleh kanvas kontrol menggunakan panggilan fungsi sederhana dengan overhead CPU yang sangat sedikit.
Contoh lengkap:
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.
Semua ini dikatakan, bagaimanapun, saya pribadi akan tetap menggunakan pendekatan standar melewati TCanvas
(atau bahkan a HDC
) alih-alih kontrol, bersama dengan beberapa dimensi:
procedure DrawFrog(ACanvas: TCanvas; const ARect: TRect);
Ini akan memungkinkan saya untuk menggunakannya untuk kontrol lain juga (tidak hanya TCustomControl
turunan), serta kanvas printer, dll.
Mengapa kanvas disembunyikan oleh desain?
Tidak benar-benar tersembunyi, tetapi di bagian terlindungi. Untuk mengaksesnya, Anda harus mendapatkan kelas baru dari yang Anda minati dan menyatakan Canvas sebagai publik.
Ini pribadi karena Anda tidak seharusnya mengaksesnya di tingkat aplikasi.
Anda tidak perlu menginstal komponen Anda jika Anda menggunakan interposer
kelas di sumber yang Anda butuhkan.
Anda juga dapat mempertimbangkan untuk mengganti Paint
metode dan meletakkan kode gambar Anda di sana.