views:

1289

answers:

4

I need to draw on a TPanel, ideally directly so I don't have another component on top of it getting in the way of mousevent-event trapping (I want to draw a little "size-grip" on it). How should I go about doing this?

+2  A: 

The simplest way to do it is to just put a TImage on the panel. But if you really don't want to do that, type TCanvas into the code editor, hit F1, and have fun learning about how it works under the hood. (Don't say I didn't warn you...)

Mason Wheeler
I actually think I can do the TCanvas part, given some sample code I have to work from... BUT how do I expose the TCanvas of the Panel itself? I found this, but it sounds like something is missing from it: http://www.mail-archive.com/[email protected]/msg00582.html
Jamo
The TMyPanel(panel) code from that link is the key. If you define an inherited class in the same unit where you want to access the canvas, you can access any protected property of the class. It's part of Delphi's "classes within a unit are friends" principle. It's also a hack. :-)
Paul-Jan
When you say "It's also a hack" -- is that to mean this approach is inappropriate in your opinion? (Just trying to learn all I can from these great answers I'm getting on this question).
Jamo
+6  A: 

This is one of the many many ways that Raize Components can make your life easier. I just go into Delphi, drop on a TRzPanel, and type:

RzPanel1.Canvas.Rectangle...

I'm sure there are other solutions - but I don't have to look for them with Raize.

(just a satisfied customer for about 10 years...)

EDIT: Given your goal, and your statement that you have Raize Components already, I should also point out that TRzSizePanel handles resizing of the panel and useful events like OnCanResize (to determine whether you want to allow resizing to a particular new width or height).

Argalatyr
Great -- I *HAVE* Raize, so will look into this (didn't before for reasons having to do with wanting to start "as simple/low-level as practical," but this might be best route). Thanks for taking the time to post this.
Jamo
How does Raize make that any easier than drawing on an ordinary TPanel? Unless the Raize controls make the Canvas property public instead of protected, for some reason.
Rob Kennedy
From the code example, it would seem that that's exactly the reason why.
Mason Wheeler
I find that using Raize components simplifies my coding and makes my applications visually consistent. Ray has thought through almost everything, and his support on the raize.public.rzcomps.support newsgroup is outstanding - I read everything he writes in that newsgroup because his coding practice is so clean. His source code is worth reading for the same reason.
Argalatyr
+2  A: 

How to Add Size Handles to Controls being Resized at Run-Time: http://delphi.about.com/library/weekly/aa110105a.htm

TAdvPanel: http://www.tmssoftware.com/site/advpanel.asp

Bob S
Great links - thanks!
Jamo
+5  A: 

To really do it right, you should probably write a descendant class. Override the Paint method to draw the sizing grip, and override the MouseDown, MouseUp, and MouseMove methods to add resizing functionality to the control.

I think that's a better solution than trying to draw onto a TPanel in your application code for a couple of reasons:

  1. The Canvas property is protected in TPanel, so you have no access to it from outside the class. You can get around that with type-casting, but that's cheating.
  2. The "resizability" sounds more like a feature of the panel than a feature of the application, so put it in code for the panel control, not in your application's main code.

Here's something to get you started:

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 Kennedy
Thanks Rob! What you are describing (and have generously provided some "starter code" for) is what I am trying to do, but I realized I needed to figure out the drawing / Canvas part, so was exploring that directly in app code. Thanks so much for your input here -- super helpful! :-)
Jamo
Note that this is also wise because e.g. Lazarus on non-windows(and maybe Kylix too), don't allow painting to the canvas outside the .PAINT event.So it can be smart to directly enforce a structure that makes sure the only drawing is done in the paint event.
Marco van de Voort