TPanel.Canvas?

I know how to draw on the canvas of a form but is it possible to do the same with a Panel (or Tabsheet)?

if so, give a short example please.
d32coderAsked:
Who is Participating?
 
TOndrejCommented:
> if you want to paint on panel's canvas, you should also
> override its Paint procedure, or...?

No, strictly speaking, to paint on a canvas you don't need anything else than to use TCanvas methods, or Windows GDI APIs with the DC handle.

A different problem is if you want to do your painting every time the panel repaints itself (ie. you don't want your painting to disappear the next time the control gets invalidated and repaints itself). In that case you need to make sure that your painting is done again _every time_ the control (re)paints itself.

In a TForm descendant, you can simply use the form's OnPaint event which is called for you automatically every time the form is painted.
For controls like TPanel which don't have an OnPaint event, you have two options:

1. Create a descendant and override its Paint method, as you have shown.
This one is useful when the descendant is going to be reused in other projects.

2. Subclass the panel to intercept WM_PAINT messages, for example:

type
  TFormTest = class(TForm)
    Panel1: TPanel;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FPanelWndProc: TWndMethod;

    procedure PanelWndProc(var Message: TMessage);
  public
  end;


procedure TFormTest.FormCreate(Sender: TObject);

begin
  // remember the panel's original window procedure
  FPanelWndProc := Panel1.WindowProc;
  // subclass the panel
  Panel1.WindowProc := PanelWndProc;
end;

procedure TFormTest.FormDestroy(Sender: TObject);
begin
  // restore the panel's original window procedure
  Panel1.WindowProc := FPanelWndProc;
  FPanelWndProc := nil;
end;

type
  THackPanel = class(TPanel);

procedure TFormTest.PanelWndProc(var Message: TMessage);
const
  GridSize = 8;
var
  X, Y: Integer;
begin
  // call the panel's original window procedure
  FPanelWndProc(Message);
  // do your own painting
  // this example draws a grid on the panel
  if Message.Msg = WM_PAINT then
    with THackPanel(Panel1) do
    begin
      X := 0;
      while X <= ClientWidth do
      begin
        Y := 0;
        while Y <= ClientHeight do
        begin
          Canvas.Pixels[X, Y] := clNavy;

          Inc(Y, GridSize);
        end;

        Inc(X, GridSize);
      end;
    end;
end;

This can come handy when you don't want to create a new descendant for just one very specific case; you want to create the control and set its properties at designtime but not register it into the component palette (because it's very special and will not be used in other projects).

HTH
TOndrej
0
 
KelmiCommented:
You can make and a new panel component which has an OnPaint event and Canvas property. Here is an example:

unit paintpanel;

interface

uses
  ExtCtrls, Classes;

type
  TPaintPanel = class(TPanel)
  private
    FOnPaint: TNotifyEvent;  
  protected
    procedure Paint; override;
  public
    property Canvas;
  published
    property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
  end;

  procedure Register;

implementation

{ Register component }

procedure Register;
begin
  RegisterComponents('Samples', [TPaintPanel]);
end;

{ TPaintPanel }

procedure TPaintPanel.Paint;
begin
  inherited Paint;
  if Assigned(FOnPaint) then FOnPaint(Self);
end;

end.
0
 
ginsonicCommented:
listening
0
Cloud Class® Course: Microsoft Windows 7 Basic

This introductory course to Windows 7 environment will teach you about working with the Windows operating system. You will learn about basic functions including start menu; the desktop; managing files, folders, and libraries.

 
KelmiCommented:
I mean: "You can make and INSTALL a new..."


0
 
TOndrejCommented:
All TControl descendants have Canvas property which is protected by default, although in some cases the descendant classes do promote its visibility to public.

To access a protected property of a class, you can use an old trick:

type
  THackPanel = class(TPanel);

with THackPanel(Panel1).Canvas do
  ...

in this example, Panel1 is a 'normal' TPanel. The typecast allows you to access its protected properties.

HTH
TOndrej
0
 
KelmiCommented:
Useful tip, TOndrej! But if you want to paint on panel's canvas, you should also override its Paint procedure, or...?
0
 
KelmiCommented:
Useful tip, TOndrej! But if you want to paint on panel's canvas, you should also override its Paint procedure, or...?
0
 
KelmiCommented:
I can see your point.

Do you know why protected properties become accessible when you define a new type like:

type
 THackPanel = class(TPanel);

If you want those protected properties to stay protected (I mean not accessible), is the only way to redeclare them as protected?
0
 
TOndrejCommented:
> Do you know why protected properties become accessible
> when you define a new type like:

> type
> THackPanel = class(TPanel);

The statement above declares a new descendant class.

By definition, a protected property is visible to the class in which it is declared and to all descendant classes; plus also to any code within the same unit after the declaration.

> If you want those protected properties to stay protected
> (I mean not accessible), is the only way to redeclare
> them as protected?

No need to do anything, see above.
In the example above, we have not changed visibility of any properties.
Our trick was to typecast Panel (an instance of TPanel) to THackPanel which is technically incorrect (Panel1 is _not_ THackPanel) but safe (because THackPanel does not introduce any changes) which allowed us to access the protected Canvas.
0
 
KelmiCommented:
Thanks for the information. That trick is pretty useful.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.