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