如何访问TFrame的画布?

3
使用的工具:Delphi XE2,VCL 32位应用程序,Windows 8。
我想将框架的背景绘制到面板上(我正在使用TJvPanel,因为它公开了OnPaint事件),该面板是框架的子控件。
在阅读了this post并添加了画布作为字段后,我仍然没有成功。
调用ShowAddReceiptPanel之后,它应该在前景面板上绘制带有所有控件的框架(TfrmMyFrame)窗口内容(包括网格和页面控件),经过ProEffectImage方法处理后呈现为灰度,但实际上显示为不透明的白色背景。我漏掉了什么吗?
这是我的代码:
type
  TfrmMyFrame = class(TFrame)
    pnlHdr: TPanel;
    pnlAddNewBG: TJvPanel;
    procedure pnlAddNewBGPaint(Sender: TObject);
  private
    { Private declarations }
    FBGImg: TProEffectImage;
    Fcnvs: TCanvas;

    procedure PaintWindow(DC: HDC); override;
    procedure ShowAddReceiptPanel;
    procedure HideAddReceiptPanel;
    procedure ResizePanel_pnlAddNewBG;

  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

constructor TfrmMyFrame.Create(AOwner: TComponent);
begin
  inherited;

  FBGImg := TProEffectImage.Create(nil);
  Fcnvs := TCanvas.Create;

end;

destructor TfrmMyFrame.Destroy;
begin
  if Assigned(FBGImg) then
    FBGImg.Free;

  if Assigned(Fcnvs) then
    Fcnvs.Free;

  inherited;
end;

procedure TfrmMyFrame.ShowAddReceiptPanel;
begin
  ResizePanel_pnlAddNewBG;
  pnlAddNewBG.Visible := True;
end;

procedure TfrmMyFrame.PaintWindow(DC: HDC);
begin
  inherited;

  Fcnvs.Handle := DC;
end;

procedure TfrmMyFrame.pnlAddNewBGPaint(Sender: TObject);
var
  l, t, w, h: Integer;
  srct, drct: TRect;
begin

  // Copy Frame canvas to BGImg bitmap
  l := 0;
  t := pnlHdr.Height;
  w := ClientWidth;
  h := ClientHeight - t;

  srct := TRect.Create(l, t, w, h);
  FBGImg.Width := w;
  FBGImg.Height := h;
  drct := TRect.Create(l, t, w, h);
  FBGImg.Canvas.CopyMode := cmSrcCopy;
  FBGImg.Canvas.CopyRect(drct, Fcnvs, srct);
//  FBGImg.Picture.SaveToFile('c:\tmp\a.bmp');

  FBGImg.Effect_AntiAlias;
  FBGImg.Effect_GrayScale;

  // Draw BGImg onto Option panel
  TJvPanel(Sender).Canvas.CopyMode := cmSrcCopy;
  TJvPanel(Sender).Canvas.Draw(0, 0, FBGImg.Picture.Graphic);
end;

procedure TfrmMyFrame.ResizePanel_pnlAddNewBG;
var
  x1, y1, x2, y2: Integer;
  bmp: TBitmap;
begin
  x1 := 0;
  y1 := pnlHdr.Height;
  x2 := ClientWidth;
  y2 := ClientHeight - y1;

  pnlAddNewBG.SetBounds(x1, y1, x2, y2);
end;

您的析构函数中的 if Assigned 测试可以且应该被移除。只需调用 Free,它包括 Assigned 检查。 - David Heffernan
谢谢!Free版包括一个已分配检查 - 他们是什么时候引入这个功能的? - Steve F
在Delphi 1中,我向您推荐这个链接:https://dev59.com/Tmoy5IYBdhLWcg3wcNrh#8550628 - David Heffernan
https://stackoverflow.com/questions/65050463/why-canvas-is-hidden-in-all-vcl-controls - Gabriel
3个回答

2
您分配给画布句柄的DC仅在PaintWindow调用期间有效。当您在该函数之外使用它时,它是无效的,因此您观察到的行为是不正确的。
我认为您可以通过调用PaintTo方法来解决问题。创建正确尺寸的位图并将其画布传递给PaintTo即可。

您只想要绘制背景吗?还是包括框架的子项? - David Heffernan
需要绘制框架的子元素。前面板将显示一个黑白图像,揭示其下方的图像 - 这是整个目的。 - Steve F
我的第二段应该涵盖它。 - David Heffernan
有没有PaintTo的变体,只绘制矩形(TRect),而不是整个画布?我需要省略框架顶部的矩形区域(其中包含标题面板)。 - Steve F
我不这么认为。你需要获取整个图像,然后将不需要的部分剥离到第二个图像中。 - David Heffernan
显示剩余3条评论

1

TFrame没有画布。您可以像TCustomControl一样创建/添加一个,但不必这样做。画布只是Windows设备上下文的便捷包装器。每当需要(部分)重绘框架时,都会调用PaintWindow例程。参数展示了DC,或者您可以使用GetDC获取一个。

那么伪代码如下:

procedure TfrmMyFrame.PaintWindow(DC: HDC);
begin
  - Resize BG image and hide it (otherwise image itself will be copied too)
  - Paint the frame's contents to the image with:
    Self.PaintTo(FBGImg.Canvas.Handle, 0, 0)
  - Process the special effects on FBGImg
  - Paint the image onto DC with:
    BitBlt(DC, 0, 0, ClientWidth, ClientHeight, FBGImage.Canvas.Handle, 0, 0, SRCCOPY);
end;

GetWindowDC 不总是返回相同的 DC。 - David Heffernan
糟糕,GetWindowDC 确实返回非客户端部分。 - NGLN
不仅如此,DC 还可以由用户提供。例如,“在我的位图上绘制”。 - David Heffernan

0

获取TFrame上的Canvas的简单方法是在其顶部添加一个TPaintBox,并使用Align := alClient和其Canvas属性。

我预计这种方法适用于任何版本的Delphi,包括未来版本,并因此使用它代替似乎有些棘手的PaintWindow方法。


然而,这并不是获得TFrame上的画布(Canvas)- 它是获得TFrame上的TPaintBox,这完全不同。 - Ken White
TPaintBox.Canvas 是一个画布,我认为它和在 PaintWindow 中创建的 Canvas 一样。我最初使用了 PaintWindow 方法,但遇到了字体自动更改的问题。因此,我转而使用 TPaintBox,它简单易用,并且不会对 Delphi 代码周围的 Windows API 进行任何操作。 - Marthein

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接