我正在尝试为TDBGridEh
编写自定义绘制单元格方法。问题是当我更改笔,画笔,...这幅画变得凌乱。这是因为控件在调用事件处理程序后会自己进行一些额外的绘制。所以我必须保留所有道具,然后在我自己的绘画完成后重置它们。
我尝试创建自己的TControlCanvas
并为其分配网格,但是我收到带有消息的运行时异常:
无法将 TControlCanvas 分配给 TControlCanvas
,这表示AssignTo
方法未针对TControlCanvas
或其祖先实现。所以我的问题是:
-
为什么
TControlCanvas
没有AssignTo
方法?问题出在哪里? -
如何保留和恢复 TControlCanvas 的所有属性?我的意思是比创建
TPen
、TBrush
、TFont
等更方便的东西。
虽然TCanvas
实际上并没有封装这些 API 函数,但可以使用SaveDC
和RestoreDC
来执行您需要的操作。从 MSDN:
SaveDC 函数通过复制描述所选对象和 图形模式(如位图、画笔、调色板、字体、笔、区域、 绘制模式和映射模式)到上下文堆栈。
[...]
还原 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所持有的任何TFont
、TBrush
或TPen
对象。从测试来看,例如,每当德尔菲使用画布的Brush.GetHandle
(FillRect
,FrameRect
),它仍然是更改的画笔。
因此,最好的办法是将SaveDC
和RestoreDC
与存储和恢复Pen
结合使用,Font
和Brush
,就像Uwe Raabe的答案一样。
不确定这是否符合您的期望,但有TPenRecall
、TBrushRecall
和TFontRecall
以半自动方式保存和恢复这三个属性的设置。
处理非常简单:使用相应的属性作为参数创建这些类的实例,并使用笔、画笔和字体执行任何您想要的操作。最后释放这些实例,这将恢复设置。
结合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;