如何保留 TControlCanvas 的所有属性并在以后恢复它们?

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

我正在尝试为 TDBGridEh 编写自定义绘制单元格方法。问题是当我改变钢笔、画笔的属性时,……绘画变得凌乱。这是因为控件在调用事件处理程序后会自行进行一些额外的绘制。所以我必须保留所有道具,然后在我自己的画完成后重新设置它们。

我试图创建自己的 TControlCanvas 并将网格的一个分配给它,但我收到 运行 次异常消息:

Cannot assign a TControlCanvas to a TControlCanvas

,表示 AssignTo 方法没有为 TControlCanvas 实现,也没有为它的祖先实现。所以我的问题是:

  1. 为什么 TControlCanvas 没有 AssignTo 方法?有什么问题吗?

  2. 如何保留和恢复 TControlCanvas 的所有属性?我的意思是比创建 TPenTBrushTFont 等更方便。

不确定这是否符合您的预期,但是有 TPenRecallTBrushRecallTFontRecall 可以在 [=30= 中保存和恢复这三个属性的设置] 方式。

处理非常简单:使用相应的属性作为参数创建这些 类 的实例,然后用 Pen、[=24= 做任何你想做的事]画笔和字体。最后释放那些实例,这将恢复设置。

结合 TObjectList 和一些内部引用计数,可以将保存和恢复这些 canvas 属性所需的工作量减少到一行。

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;

用法:

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;

虽然TCanvas实际上并没有封装这些API函数,但是可以使用SaveDCRestoreDC来做你需要的。来自 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.

可能的代码示例:

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

编辑:
我意识到仅此一项可能不是答案。这将处理 Windows 端的设备上下文,它由 Delphi 的 VCL 端的 TCanvas /TControlCanvas 对象表示。 但它不会改变 VCL 持有的任何 TFontTBrushTPen 对象。从测试看来,例如每当Delphi使用Canvas的Brush.GetHandleFillRectFrameRect)时,它仍然是改变后的画笔。

所以最好的办法是将 SaveDCRestoreDC 与存储和恢复 PenFontBrush 结合使用,就像答案中一样来自 Uwe Raabe。