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

Posted

技术标签:

【中文标题】如何保留 TControlCanvas 的所有属性并在以后恢复它们?【英文标题】:How can I keep all properties of a TControlCanvas and restore them later? 【发布时间】:2018-03-27 07:46:00 【问题描述】:

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

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

无法将 TControlCanvas 分配给 TControlCanvas

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

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

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

【问题讨论】:

Graphics.pasSaveDC/RestoreDC 中有TFontRecall 及其兄弟姐妹Windows.pas 【参考方案1】:

虽然TCanvas 实际上并没有封装这些API 函数,但是可以使用SaveDCRestoreDC 来做你需要的事情。来自 MSDN:

SaveDC 函数通过复制描述选定对象的数据和 图形模式(如位图、画笔、调色板、字体、钢笔、区域、 绘图模式和映射模式)到上下文堆栈。

[...]

RestoreDC 函数将设备上下文 (DC) 恢复到 指定的状态。 DC通过弹出状态信息来恢复 由先前调用 SaveDC 函数创建的堆栈。

一个可能的代码示例:

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.GetHandle (FillRect, FrameRect) 时,它仍然是更改后的 Brush。

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

【讨论】:

Tnx,它解决了所有刷机问题。但是我的笔还是有一些问题。最后我使用了TRecalldescendants。 嗯,你画画的时候还在新建一个临时的TControlCanvas吗? RestoreDC 之后调用Canvas.Refresh; 时,笔问题会消失吗? 是的! Refresh 已修复。有什么问题? @saa Here 是一篇试图解释刷新的文章。虽然我对用法和解释都不太相信......【参考方案2】:

不确定这是否符合您的期望,但有TPenRecallTBrushRecallTFontRecall 以半自动方式保存和恢复这三个属性的设置。

处理非常简单:使用相应的属性作为参数创建这些类的实例,然后使用 PenBrushFont 做任何你想做的事情。最后释放这些实例,这将恢复设置。

结合TObjectList 和一些内部引用计数,保存和恢复这些画布属性所需的工作可以减少到一个衬里。

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;

【讨论】:

TCanvasSaver.Create(ACanvas); 线是否因电弧而工作?在 D2007 中,不调用析构函数。此外,构造函数中的 inherited; 行无法编译 - 在 D2007 中它必须是 inherited Create; @UliGerhardt,感谢您的提示!似乎我在剪贴板中有一个过时的代码版本。 TCanvasSaver.SaveCanvas 返回一个在方法结束之前隐式保持的接口。

以上是关于如何保留 TControlCanvas 的所有属性并在以后恢复它们?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过保留除一个以外的所有属性来随机打乱种群?

如何使用 PHP 删除所有 xml 属性值?

如何在保留其他样式属性的同时更改 Android Button 波纹颜色?

在delphi原有控件基础上画图

Object.assign() 保留所有未更改的值[重复]

如何在 Subversion 存储库之间复制保留属性但不保留历史记录