如何在TPanel上绘图

6

我需要在TPanel上绘制图形,最好是直接绘制,这样就不会有其他组件覆盖它,阻碍鼠标事件的捕获(我想在其上绘制一个小的“大小调整手柄”)。我该如何做到这一点?


我想知道你是如何创建组件标签的?;) 像你的问题+1 - Peter Perháč
感谢@MasterPeter的微笑和@Mason Wheeler的修复。 - Jamo
我在这里发起了一个类似的问题(更全局,适用于VCL中的任何控件):https://stackoverflow.com/questions/65050463/why-canvas-is-hidden-in-all-vcl-controls - Gabriel
4个回答

10

要做到真正正确,你应该编写一个子类。覆盖Paint方法以绘制大小调整柄,并覆盖MouseDownMouseUpMouseMove方法以添加调整大小功能到控件中。

我认为这比尝试在应用程序代码中画一个TPanel更好,原因如下:

  1. TPanel中的Canvas属性是受保护的,因此您无法从类外访问它。您可以通过类型转换来绕过它,但那是作弊。
  2. "可调整大小"听起来更像是面板而不是应用程序的特性,因此将其放入面板控件的代码中,而不是放在应用程序的主要代码中。

这里有一些开始的东西:

type
  TSizablePanel = class(TPanel)
  private
    FDragOrigin: TPoint;
    FSizeRect: TRect;
  protected
    procedure Paint; override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
  end;

procedure TSizeablePanel.Paint;
begin
  inherited;
  // Draw a sizing grip on the Canvas property
  // There's a size-grip glyph in the Marlett font,
  // so try the Canvas.TextOut method in combination
  // with the Canvas.Font property.
end;

procedure TSizeablePanel.MouseDown;
begin
  if (Button = mbLeft) and (Shift = []) 
      and PtInRect(FSizeRect, Point(X, Y)) then begin
    FDragOrigin := Point(X, Y);
    // Need to capture mouse events even if the mouse
    // leaves the control. See also: ReleaseCapture.
    SetCapture(Handle);
  end else inherited;
end;

谢谢Rob!您所描述的(并慷慨地提供了一些“起始代码”)正是我想做的,但我意识到我需要弄清楚绘图/画布部分,因此直接在应用程序代码中探索它。非常感谢您在这里的帮助-超级有用! :-) - Jamo
请注意,这样做是明智的,因为例如在非Windows环境下的Lazarus(也许Kylix也是如此),不允许在.PAINT事件之外向画布绘制。因此,直接强制执行一种结构,以确保所有绘图都在paint事件中完成,这可能是明智的选择。 - Marco van de Voort

7
这是 Raize Components 可以让您的生活更轻松的众多方法之一。我只需要进入 Delphi,放置一个 TRzPanel,并输入:RzPanel1.Canvas.Rectangle... 我相信有其他解决方案 - 但使用 Raize,我不必寻找它们。(大约满意地使用了10年...) 编辑:考虑到您的目标以及您已经拥有 Raize 组件,我还应该指出,TRzSizePanel 处理面板大小调整和可用事件(如 OnCanResize)等有用事件,以确定是否允许将其调整为特定的新宽度或高度。

太好了--我Raize,所以会研究一下这个(之前由于想从“尽可能简单/低级别”开始,所以没有考虑过,但这可能是最好的路线)。感谢您抽出时间发布这个。 - Jamo
Raize是如何比在普通的TPanel上绘画更加简便的呢?除非Raize控件将Canvas属性公开而不是保护的,出于某种原因。 - Rob Kennedy
1
从代码示例来看,似乎正是这个原因。 - Mason Wheeler
我发现使用Raize组件简化了我的编码过程,并使我的应用程序在视觉上更加一致。Ray几乎考虑到了所有的细节,他在raize.public.rzcomps.support新闻组中提供的支持非常出色 - 我阅读他在该新闻组中写的所有内容,因为他的编码实践非常干净。出于同样的原因,他的源代码值得一读。 - Argalatyr

4

最简单的方法就是在面板上放置一个TImage。但如果你真的不想这样做,可以在代码编辑器中键入TCanvas,然后按F1键,享受学习它如何在幕后工作的乐趣。(别说我没警告过你...)


我其实认为我可以完成TCanvas部分,因为我有一些样例代码可供参考...但是如何暴露面板自身的TCanvas呢?我找到了这个链接,但它似乎缺少某些东西:http://www.mail-archive.com/delphi@elists.org/msg00582.html - Jamo
1
那个链接中的TMyPanel(panel)代码是关键。如果您在同一单元中定义了一个继承类,您可以访问该类的任何受保护属性。这是Delphi的“单元内类是友元”的原则的一部分。这也是一种技巧。 :-) - Paul-Jan
当你说“这也是一种hack”的时候,是指你认为这种方法不合适吗?(只是想从这些很棒的答案中学到尽可能多的东西)。 - Jamo

2

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