Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I keep all properties of a TControlCanvas and restore them later?

I'm trying to write a custom draw cell method for a TDBGridEh. The problem is when I change properties of pen, brush, ... the painting becomes messy. That's because the control does some extra painting itself after it calls the event handler. So I have to keep all props and then reset them when my own painting was finished.

I tried to create my own TControlCanvas and assign grid's one to it, but I get a run-time exception with message:

Cannot assign a TControlCanvas to a TControlCanvas

, that indicates the AssignTo method is not implemented for TControlCanvas nor for its ancestors. So my questions are:

  1. Why TControlCanvas does not have an AssignTo method? What is the problem?

  2. How can I keep and restore all properties of a TControlCanvas? And by that I mean something more convenient than creating TPen, TBrush, TFont, etc. .

like image 209
saastn Avatar asked Dec 19 '22 00:12

saastn


2 Answers

While TCanvas does not actually encapsulate these API functions, it is possible to use SaveDC and RestoreDC to do what you need. From the MSDN:

The SaveDC function saves the current state of the specified device context (DC) by copying data describing selected objects and graphic modes (such as the bitmap, brush, palette, font, pen, region, drawing mode, and mapping mode) to a context stack.

[...]

The RestoreDC function restores a device context (DC) to the specified state. The DC is restored by popping state information off a stack created by earlier calls to the SaveDC function.

A possible code sample:

uses
  Winapi.Windows;
...
var
  SavedDC: Integer;
begin
  SavedDC := SaveDC(Canvas.Handle);
  try
   // Painting code
  finally
    RestoreDC(Canvas.Handle, SavedDC);
  end;
end;

Edit:
I realized that this alone will likely not be the answer. This will handle the Device Context on the Windows side that is represented by the TCanvas /TControlCanvas object on Delphi's VCL side. But it will not alter any of the TFont, TBrush or TPen objects that the VCL holds. From tests it looks like that e.g. whenever Delphi uses the Canvas's Brush.GetHandle (FillRect, FrameRect), it is still the changed Brush.

So the best bet is to use SaveDC and RestoreDC in combination with storing and restoring Pen, Font and Brush like in the answer from Uwe Raabe.

like image 154
nil Avatar answered Dec 24 '22 01:12

nil


Not sure if this fits what your expects, but there are TPenRecall, TBrushRecall and TFontRecall to save and restore the settings of these three properties in a semi-automatic way.

The handling is pretty simple: Create an instance of these classes with the corresponding properties as the parameter and do whatever you want with Pen, Brush and Font. In the end free those instances, which will restore the settings.

In combination with a TObjectList and some internal reference counting the effort needed to save and restore these canvas properties can be reduced to a one liner.

type
  TCanvasSaver = class(TInterfacedObject)
  private
    FStorage: TObjectList<TRecall>;
  public
    constructor Create(ACanvas: TCanvas);
    destructor Destroy; override;
    class function SaveCanvas(ACanvas: TCanvas): IInterface;
  end;

constructor TCanvasSaver.Create(ACanvas: TCanvas);
begin
  inherited Create;
  FStorage := TObjectList<TRecall>.Create(True);
  FStorage.Add(TFontRecall.Create(ACanvas.Font));
  FStorage.Add(TBrushRecall.Create(ACanvas.Brush));
  FStorage.Add(TPenRecall.Create(ACanvas.Pen));
end;

destructor TCanvasSaver.Destroy;
begin
  FStorage.Free;
  inherited;
end;

class function TCanvasSaver.SaveCanvas(ACanvas: TCanvas): IInterface;
begin
  Result := Self.Create(ACanvas);
end;

Usage:

procedure TForm274.DoYourDrawing(ACanvas: TCanvas);
begin
  TCanvasSaver.SaveCanvas(ACanvas);
  { Change Pen, Brush and Font of ACanvas and do whatever you need to do.
    Make sure that ACanvas is still valid and the same instance as at the entry of this method. }
end;
like image 38
Uwe Raabe Avatar answered Dec 24 '22 01:12

Uwe Raabe