Solved

TPanel.Canvas?

Posted on 2002-03-10
10
3,222 Views
Last Modified: 2012-08-14
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.
0
Comment
Question by:d32coder
  • 6
  • 3
10 Comments
 

Expert Comment

by:Kelmi
ID: 6854582
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
 
LVL 9

Expert Comment

by:ginsonic
ID: 6854649
listening
0
 

Expert Comment

by:Kelmi
ID: 6854717
I mean: "You can make and INSTALL a new..."


0
Free Tool: Postgres Monitoring System

A PHP and Perl based system to collect and display usage statistics from PostgreSQL databases.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
LVL 8

Expert Comment

by:TOndrej
ID: 6854811
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
 

Expert Comment

by:Kelmi
ID: 6854842
Useful tip, TOndrej! But if you want to paint on panel's canvas, you should also override its Paint procedure, or...?
0
 

Expert Comment

by:Kelmi
ID: 6854846
Useful tip, TOndrej! But if you want to paint on panel's canvas, you should also override its Paint procedure, or...?
0
 
LVL 8

Accepted Solution

by:
TOndrej earned 50 total points
ID: 6854995
> 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
 

Expert Comment

by:Kelmi
ID: 6855077
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
 
LVL 8

Expert Comment

by:TOndrej
ID: 6855106
> 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
 

Expert Comment

by:Kelmi
ID: 6855128
Thanks for the information. That trick is pretty useful.
0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
Although Jacob Bernoulli (1654-1705) has been credited as the creator of "Binomial Distribution Table", Gottfried Leibniz (1646-1716) did his dissertation on the subject in 1666; Leibniz you may recall is the co-inventor of "Calculus" and beat Isaac…

807 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question