我的项目中有一个框架(TFrame
的后代),并希望在其上绘制一些内容。
正如我在论坛上看到的那样,常见的方法是覆盖PaintWindow
方法。
我在一个干净的项目上试过这个:
type
TMyFrame = class(TFrame)
private
FCanvas: TCanvas;
protected
procedure PaintWindow(DC: HDC); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy(); override;
end;
implementation
{$R *.dfm}
constructor TMyFrame.Create(AOwner: TComponent);
begin
inherited;
FCanvas := TCanvas.Create();
end;
destructor TMyFrame.Destroy();
begin
FCanvas.Free();
inherited;
end;
procedure TMyFrame.PaintWindow(DC: HDC);
begin
inherited;
FCanvas.Handle := DC;
FCanvas.Pen.Width := 3;
FCanvas.Pen.Color := clRed;
FCanvas.MoveTo(0, 0);
FCanvas.LineTo(ClientWidth, ClientHeight);
FCanvas.Pen.Color := clGreen;
FCanvas.MoveTo(ClientWidth, 0);
FCanvas.LineTo(0, ClientHeight);
end;
但是,在将我的框架放在主窗体上后,调试器从未输入此方法,直到我在框架属性中启用DoubleBuffered
。 ParentBackground
的任何值都不会影响结果。
覆盖WM_PAINT
处理程序也解决了问题:
type
TMyFrame = class(TFrame)
protected
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
...
procedure TMyFrame.WMPaint(var Message: TWMPaint);
begin
inherited;
FCanvas.Handle := GetDC(Handle);
FCanvas.Pen.Width := 3;
FCanvas.Pen.Color := clRed;
FCanvas.MoveTo(0, 0);
FCanvas.LineTo(ClientWidth, ClientHeight);
FCanvas.Pen.Color := clGreen;
FCanvas.MoveTo(ClientWidth, 0);
FCanvas.LineTo(0, ClientHeight);
ReleaseDC(Handle, FCanvas.Handle);
end;
此代码始终绘制交叉线,无论将哪个值分配给DoubleBuffered
或ParentBackground
。
但是,当我尝试使用BeginPaint
/ EndPaint
代替GetDC
/ ReleaseDC
时,问题仍然存在:
procedure TMyFrame.WMPaint(var Message: TWMPaint);
var
PS: PAINTSTRUCT;
begin
inherited;
FCanvas.Handle := BeginPaint(Handle, PS);
FCanvas.Pen.Width := 3;
FCanvas.Pen.Color := clRed;
FCanvas.MoveTo(0, 0);
FCanvas.LineTo(ClientWidth, ClientHeight);
FCanvas.Pen.Color := clGreen;
FCanvas.MoveTo(ClientWidth, 0);
FCanvas.LineTo(0, ClientHeight);
EndPaint(Handle, PS);
end;
FCanvas.Handle非零,但结果是空白帧。在这种情况下,请设置DoubleBuffered
或ParentBackground
不要更改任何内容。
也许我误称他们错了?
现在我将WM_PAINT
处理程序与GetDC
/ ReleaseDC
一起使用,因为我不想在此框架上启用DoubleBuffered
。另外,我担心其他程序员在将我的框架放入他们的项目后会意外禁用DoubleBuffered
并且会遇到与我相同的头痛。
但也许有更安全和正确的方法在画面上画画?
答案 0 :(得分:5)
如果我没有对测试框架进行任何控制,我可以复制你的问题(这也可能是我们没有人可以复制你的问题的原因 - fi抛出一个控件,以确保框架在窗体上)。
当没有控件时调用PaintHandler
的原因,以及设置DoubleBuffered
时调用它的原因,虽然它没有控件,只是{{1}设计WM_PAINT
的消息处理程序:
TWinControl
正如您所看到的,当未设置procedure TWinControl.WMPaint(var Message: TWMPaint);
var
..
begin
if not FDoubleBuffered or (Message.DC <> 0) then
begin
if not (csCustomPaint in ControlState) and (ControlCount = 0) then
inherited
else
PaintHandler(Message);
end
else
begin
..
且没有控件时,DoubleBuffered
未被调用(毕竟没有任何颜色可绘:我们不是自定义绘图(没有csCustomPaint标志)并且没有控件可以显示)。设置PaintHandler
后,会跟随不同的代码路径,调用DoubleBuffered
,然后调用WMPrintClient
。
如果你最终会在没有任何控制的情况下使用框架(尽管不太可能),那么从上面的代码片段来看,修复是明显的(当你知道它时也很明智):include {{1} } PaintHandler
:
csCustomPaint
然后继承的ControlState
处理程序将调用type
TMyFrame = class(TFrame)
..
protected
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
..
procedure TMyFrame.WMPaint(var Message: TWMPaint);
begin
ControlState := ControlState + [csCustomPaint];
inherited;
ControlState := ControlState - [csCustomPaint];
end;
。
WM_PAINT
消息处理程序中使用PaintHandler
/ BeginPaint
进行绘制似乎不起作用,原因是绘制代码之前的EndPaint
调用验证了更新区域。致电WM_PAINT
后,请检查inherited
rcPaint
成员,您会发现它是(0,0,0,0)。
由于当时没有无效区域,操作系统只是忽略了以下的绘图调用。您可以在绘制画布之前通过使框架的客户端矩形无效来验证这一点:
PAINTSTRUCT
现在您将看到您的绘图将生效。当然,您可以选择不打电话给BeginPaint
,或者只取消您要吸引的部分。
答案 1 :(得分:0)
看起来它只在设计师
中被调用procedure TCustomFrame.PaintWindow(DC: HDC);
begin
// Paint a grid if designing a frame that paints its own background
if (csDesigning in ComponentState) and (Parent is TForm) then
with TForm(Parent) do
if (Designer <> nil) and (Designer.GetRoot = Self) and
(not StyleServices.Enabled or not Self.ParentBackground) then
Designer.PaintGrid
end;
进行特殊绘制的唯一方法是将WM_PAINT添加到框架中:
TFrame3 = class(TFrame)
protected
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
end;