通常情况下使用GDI +在Delphi中你可以OnPaint事件中使用TPaintBox,和油漆:
procedure TForm1.PaintBox1Paint(Sender: TObject);
var
g: TGPGraphics;
begin
g := TGPGraphics.Create(PaintBox1.Canvas.Handle);
try
g.DrawImage(FSomeImage, 0, 0);
finally
g.Free;
end;
end;
这种范式的问题是,创建一个销毁Graphics对象每一次都是浪费,效果不好。 此外,也有一些在GDI速效结构+当你有一个持久的Graphics对象,你只能使用。
这个问题,当然,是我什么时候可以创建一个图形对象? 我需要知道当手柄变为可用,然后当它不再有效。 我需要这些信息,所以我可以创建和销毁我的Graphics对象。
解决方案尝试Nº1
我可以通过创建它,当真正需要它解决创新的问题-在第一次油漆周期被称为:
procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
if FGraphics = nil then
FGraphics := TGPGraphics.Create(PaintBox1.Canvas.Handle);
FGraphics.DrawImage(FSomeImage, 0, 0);
end;
但我知道什么时候该设备上下文不再有效,所以我可以毁了我的FGraphcis对象,因此它是重新创建需要它的下一次。 如果由于某种原因,TPaintBox的设备上下文被重建,我会在一个无效的设备上下文下一次的OnPaint被称为绘图。
在德尔福的预期机制,当TPaintBox的设备上下文句柄创建,销毁或重新创建我知道的是什么?
你不能用标准TPaintBox因为TPaintBox有型TControlCanvas,无有关此问题的成员是这些的画布:
TControlCanvas = class(TCanvas)
private
...
procedure SetControl(AControl: TControl);
protected
procedure CreateHandle; override;
public
procedure FreeHandle;
...
property Control: TControl read FControl write SetControl;
end;
问题是,FreeHandle和SetControl不虚。
但是:在TControlCanvas创建并分配在这里:
constructor TGraphicControl.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FCanvas := TControlCanvas.Create;
TControlCanvas(FCanvas).Control := Self;
end;
所以,你可以做的是创造一个下降TMyControlCanvas确实有虚方法,并指派画布这样的TMyPaintBox:
constructor TMyPaintBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FCanvas.Free;
FCanvas := TMyControlCanvas.Create;
TMyControlCanvas(FCanvas).Control := Self;
end;
然后你可以使用的方法TMyControlCanvas动态创建和摧毁你的TGPGraphics。
这应该让你去。
--jeroen
检测创作是容易的。 只是覆盖CreateHandle
在子TControlCanvas
,并把你的到位默认之一的吉荣的回答证明 。 检测破坏是困难。
为了避免这个问题的方法之一是检查TGpGraphics手柄是否等于漆箱的把手,这样,而不是检测时的瞬间设备上下文被释放,你只需选中你需要知道之前。
if not Assigned(FGraphics)
or (FGraphics.GetHDC <> PaintBox1.Canvas.Handle) then begin
FGraphics.Free;
FGraphics := TGpGraphics.Create(PaintBox1.Canvas.Handle);
end;
这可能是不可靠的,虽然, 办理值是容易被重复使用,所以虽然HDC值可能是两次检查之间的相同,也不能保证它仍然是指同一个操作系统的设备上下文对象。
该TCanvas
基类不会清除自己的Handle
属性,所以必须从外部进行任何无效的画布。 TControlCanvas
清除其Handle
物业公司当Control
属性,都会重新分配,但通常只有当创建了控制,因为发生TControlCanvas
实例很少共享。 然而, TControlCanvas
情况下,从设备上下文的区处理保持在工作CanvasList
。 当其中一人需要一个DC(在TControlCanvas.CreateHandle
),它调用FreeDeviceContext
,以腾出空间在画布缓存它要创建的句柄。 该函数调用(非虚拟) FreeHandle
方法。 缓存大小为4(见CanvasListCacheSize
),所以如果你有几个后代TCustomControl
或TGraphicControl
在你的程序中,机会是很高,你会得到更多的时候比他们四人需要一次重新绘制高速缓存未命中。
TControlCanvas.FreeHandle
不是虚拟的,它不调用任何虚方法。 虽然你可以作出这样的类的子类,并给它的虚拟方法时,VCL的其余部分将继续调用非虚方法,无视任何新增项目。
不要试图一个设备上下文被释放时进行检测,你可能会使用不同的TGpGraphics构造会更好。 使用的一个,需要一个窗口句柄而不是DC句柄,例如。 窗口句柄破坏是很容易被发现。 对于一次性的解决方案,指定自己的方法到TPaintBox.WindowProc
财产和监视wm_Destroy
消息。 如果你经常这样做,然后做一个子类,并覆盖DestroyWnd
。
性能打你需要创建/销毁图形对象是最小的。 它是由远在第一时间使用GDI +的绘图命令的性能损失抵消。 无论其中,国际海事组织,是值得担忧的,当涉及到绘图的用户界面,因为用户不会注意到反正。 坦率地说,它可以是非常不方便的,试图携带一个图形对象,跟踪变化的DC句柄(特别是如果你封装你自己的一组类中的图形程序)。
如果你需要缓存的位图,你可以考虑做的是创建要与GDI缓存位图+(让你想要的大小合适&W /不管反锯齿设置),将其保存到内存流,然后当你需要它,从流加载它,并使用好醇” BitBlt的绘制。 这将是多少,比使用Graphics.DrawImage快得多。 我说快几个数量级。
procedure TGraphicControl.WMPaint(var Message: TWMPaint);
begin
if Message.DC <> 0 then
begin
Canvas.Lock;
try
Canvas.Handle := Message.DC;
try
Paint;
finally
Canvas.Handle := 0;
end;
finally
Canvas.Unlock;
end;
end;
end;
Canvas.Handle := Message.DC;