모든 VCL 컨트롤에서 Canvas가 "숨겨진"이유는 무엇입니까?
모든 컨트롤 (버튼, 패널 등) 캔버스에 무언가를 그리는 기본 절차를 수행하고 싶습니다 (단순화를 위해 삼각형이라고합시다).
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를 낭비하지 않고 우아하게 (하나의 단일 매개 변수) 해결하는 방법
이제 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.
그러나이 모든 것은 개인적 으로 컨트롤 대신 a TCanvas
(또는 a HDC
) 를 전달하는 표준 접근 방식을 일부 차원과 함께 계속 사용할 것입니다.
procedure DrawFrog(ACanvas: TCanvas; const ARect: TRect);
이렇게하면 다른 컨트롤 ( TCustomControl
하위 항목뿐만 아니라)뿐만 아니라 프린터 캔버스 등에 사용할 수 있습니다.
캔버스가 디자인에 의해 숨겨진 이유는 무엇입니까?
실제로 숨겨진 것은 아니지만 보호 된 섹션에 있습니다. 액세스하려면 관심있는 클래스에서 새 클래스를 파생하고 Canvas를 공용으로 선언해야합니다.
응용 프로그램 수준에서 액세스 할 수 없기 때문에 비공개입니다.
필요한 interposer
소스에서 클래스 를 사용하는 경우 구성 요소를 설치할 필요가 없습니다 .
Paint
메서드를 재정의하고 거기에 그리기 코드를 넣을 수도 있습니다 .