Board index » delphi » Event Handlers on dynamically created forms

Event Handlers on dynamically created forms

Hello,

I'd like to dynamically create forms from a formless unit.

The sample below does what I want, but I can only get it to work from
another "formed" unit.
If I try to achieve the same from a "non formed" unit I get stuck at a
"Incompatible types: method pointer and regular procedure" error.

Thanks

David

//--------------------------------------------------------------

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure OnTestButtonClick(Sender: Tobject);

  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var frmPTRQuery : TForm;
    TestButton  : TButton;

begin
    frmPTRQuery:=TForm.Create(Application); // Create frmPTRQuery
    frmPTRQuery.Height:=200;
    frmPTRQuery.Width:=200;
    TestButton:=TButton.Create(frmPTRQuery);
    TestButton.parent := frmPTRQuery;
    TestButton.OnClick:=OnTestButtonClick;
    TestButton.Caption:='Click here';
    TestButton.Left:=(frmPTRQuery.ClientWidth DIV 2) - (TestButton.Width Div
2);
    TestButton.Top:=(frmPTRQuery.ClientHeight DIV 2) - (TestButton.Height
Div 2);

    frmPTRQuery.Showmodal;
    frmPTRQuery.Free;// ....and free it!
end;

procedure TForm1.OnTestButtonClick(Sender: Tobject);
begin
   showmessage('Click');
end;

end.

 

Re:Event Handlers on dynamically created forms


"david" <da...@please.reply.to.list> skrev i melding
news:sBJA9.709$Li.100189@news02.tsnz.net...

Quote
> Hello,

> I'd like to dynamically create forms from a formless unit.

> The sample below does what I want, but I can only get it to work from
> another "formed" unit.
> If I try to achieve the same from a "non formed" unit I get stuck at a
> "Incompatible types: method pointer and regular procedure" error.

> Thanks

> David

[...]
> procedure TForm1.OnTestButtonClick(Sender: Tobject);
> begin
>    showmessage('Click');
> end;

You don't need a form to achieve this, as the method may recode in a custom
class of yours:

TMyHandler = class
private
  procedure OnButtonClick(Sender: TObject);
end;

..

procedure TMyHandler.OnButtonClick(Sender: TObject);
begin
  // Do something
end;

...now you're able to assign this method to the button's OnClick event:

    TestButton.OnClick:=MyHandler.OnTestButtonClick;

...you don't even need to create an instance of TMyHandler, as you don't
access any fields, and the method is static. The address of the method will
be resolved by a memory reference.

--
Bj?rge S?ther
bjorge@hahaha_itte.no

Re:Event Handlers on dynamically created forms


Here is one method :-

On Thu, 14 Nov 2002 21:59:05 +1300, "david"

Quote
<da...@please.reply.to.list> wrote:
>Hello,

>I'd like to dynamically create forms from a formless unit.

unit Unit2;
interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
  StdCtrls;

  Procedure Start;

implementation
  Type TInstance = Class( TObject )
       procedure AButtonClick(Sender: Tobject);
  End;

procedure Start;
var
  frmPTRQuery :TForm;
  btnTest  :TButton;
  Inst :TInstance;
begin
  frmPTRQuery:=TForm.Create(Application); // Create frmPTRQuery
  frmPTRQuery.Height:=200;
  frmPTRQuery.Width:=200;
  btnTest := TButton.Create(frmPTRQuery);
  btnTest.Parent := frmPTRQuery;
  btnTest.Caption:='Click here';
  btnTest.Left:=(frmPTRQuery.ClientWidth - btnTest.Width) Div 2;
  btnTest.Top:=(frmPTRQuery.ClientHeight - btnTest.Height) Div 2;
  // Create an Instance of the object
  Inst := TInstance.Create;
  // Assign the Event
  btnTest.OnClick := Inst.AButtonClick;
  //
  frmPTRQuery.Showmodal;
  //
  Inst.Free;
  frmPTRQuery.Free;// ....and free it!
end;

procedure TInstance.AButtonClick(Sender: Tobject);
begin
  showmessage('Click');
end;

end.

Re:Event Handlers on dynamically created forms


Thanks that worked a treat! One more question if I could.

I've added a timer to the form and when an OnTimer event triggers I'd like
to turn the Timer off, but how do I refer back to it?

unit Unit2;
interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,  extctrls,  StdCtrls;

  Procedure Start;

implementation
  Type TInstance = Class( TObject )
       procedure AButtonClick(Sender: Tobject);
       procedure OnTimer1(Sender: Tobject); <------------------ #########
See bottom of post

  End;

procedure Start;
var
  frmPTRQuery :TForm;
  btnTest  :TButton;
  Timer1   : TTimer;
  Inst :TInstance;
begin
  Inst := TInstance.Create;      // Create an Instance of the object
  frmPTRQuery:=TForm.Create(Application); // Create frmPTRQuery
  frmPTRQuery.Height:=200;
  frmPTRQuery.Width:=200;
  Timer1 := TTimer.Create(frmPTRQuery);
  Timer1.Enabled:=False;
  Timer1.Interval:=5000;
  Timer1.OnTimer:=Inst.OnTimer1;
  Timer1.Enabled:=True;
  btnTest := TButton.Create(frmPTRQuery);
  btnTest.Parent := frmPTRQuery;
  btnTest.Caption:='Click here';
  btnTest.Left:=(frmPTRQuery.ClientWidth - btnTest.Width) Div 2;
  btnTest.Top:=(frmPTRQuery.ClientHeight - btnTest.Height) Div 2;
  btnTest.OnClick := Inst.AButtonClick;       // Assign the Event
  frmPTRQuery.Showmodal;
  Inst.Free;
  frmPTRQuery.Free;
end;

 procedure TInstance.AButtonClick(Sender: Tobject);
begin
  showmessage('Click');
end;

procedure TInstance.OnTimer1(Sender: Tobject);
begin
  Timer1.Enabled:=False; <-------------------------------------
################
  showmessage('Timer');
end;

en

Re:Event Handlers on dynamically created forms


The Timer needs to be 'visible' to the procedure
TInstance.OnTimer1(Sender: Tobject);

Here I have put it in the TInstance Object :-

unit Unit2;
interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
  StdCtrls, ExtCtrls;

  Procedure Start;

implementation
  Type TInstance = Class( TObject )
       Timer1 :TTimer;
       procedure AButtonClick(Sender: Tobject);
       procedure OnTimer1(Sender: Tobject);
  End;

procedure Start;
var
  frmPTRQuery :TForm;
  btnTest  :TButton;
  Inst :TInstance;
begin
  frmPTRQuery:=TForm.Create(Application); // Create frmPTRQuery
  frmPTRQuery.Height:=200;
  frmPTRQuery.Width:=200;
  //
  btnTest := TButton.Create(frmPTRQuery);
  btnTest.Parent := frmPTRQuery;
  btnTest.Caption:='Click here';
  btnTest.Left:=(frmPTRQuery.ClientWidth - btnTest.Width) Div 2;
  btnTest.Top:=(frmPTRQuery.ClientHeight - btnTest.Height) Div 2;
  // Create an Instance of the object
  Inst := TInstance.Create;
  // Set up the Timer
  Inst.Timer1 := TTimer.Create(frmPTRQuery);
  Inst.Timer1.Enabled:=False;
  Inst.Timer1.Interval:=5000;
  Inst.Timer1.OnTimer:=Inst.OnTimer1;
  Inst.Timer1.Enabled:=True;

  // Assign the Event
  btnTest.OnClick := Inst.AButtonClick;
  //
  frmPTRQuery.Showmodal;
  //
  Inst.Free;
  frmPTRQuery.Free;// ....and free it!
end;

procedure TInstance.AButtonClick(Sender: Tobject);
begin
  showmessage('Click');
end;

procedure TInstance.OnTimer1(Sender: Tobject);
begin
  Timer1.Enabled:=False;
  showmessage('Timer');
end;

end.

Re:Event Handlers on dynamically created forms


It has just occurred to me that you might be better off simply doing
this - the Sender is of course the Timer itself
- although it would be wise to check ...

procedure TInstance.OnTimer1(Sender: Tobject);
begin
  TTimer(Sender).Enabled:=False;
  showmessage('Timer');
end;

- the Sender is of course the Timer itself
- although it would be wise to check ...

procedure TInstance.OnTimer1(Sender: Tobject);
begin
  If Not (Sender Is TTimer) Then
     Begin
       ShowMessage( 'cockup' );
       Exit;
     End;
  TTimer(Sender).Enabled:=False;
  showmessage('Timer');
end;

Re:Event Handlers on dynamically created forms


In article <XyLA9.741$Li.104...@news02.tsnz.net>, "david"

Quote
<da...@please.reply.to.list> writes:
>procedure TInstance.OnTimer1(Sender: Tobject);
>begin
>  Timer1.Enabled:=False; <-------------------------------------
>################
>  showmessage('Timer');
>end;

procedure TInstance.OnTimer1(Sender: Tobject);
begin
  TTimer(Sender).Enabled:=False; <-------------################
  showmessage('Timer');
end;

Whatever has triggered the event handler is passed as the Sender. But it is
passed as a TObject, which is the simplest object and so covers any descendant
object which may have triggered the event handler. But Delphi is strongly
typed, and the compiler, assuming it to be a TObject as it is so declared,
knows that TObjects do not have an Enabled property. So you typecast it to what
it really is, the compiler believes you, and accesses the Enabled property. But
it had better _have_ an enabled property else otherwise there will be a big GPF
or Access Violation.

This referencing of an object as being of ancestor type, is a fundamental
feature of object orientation which enable the implementation of polymorphism.

Typecasting does nothing to the memory or values of an object, it just fools
the compiler.

Alan Lloyd
alangll...@aol.com

Re:Event Handlers on dynamically created forms


Quote
AlanGLLoyd wrote in message

<20021114114400.21064.00000...@mb-mc.aol.com>...
[...]

Quote
>  TTimer(Sender).Enabled:=False; <-------------################
>                                                 ... So you typecast it to
what
>it really is, the compiler believes you, and accesses the Enabled
property. But
>it had better _have_ an enabled property else otherwise there will be a
big GPF
>or Access Violation.

And for this exact reason, "as" was invented. Which not only asks the
compiler
to treat Sender as a TTimer, but also the check that it really is.

(Nice bricktext. But did you have to make it 79 columns wide? You devil,
you.)

Quote
>Typecasting does nothing to the memory or values of an object, it just
fools
>the compiler.

It's not so much a matter of fooling as it is of overriding the compiler's
assumptions.

Groetjes,
Maarten Wiltink

Re:Event Handlers on dynamically created forms


In article <3dd4071d$0$46604$e4fe5...@news.xs4all.nl>, "Maarten Wiltink"

Quote
<maar...@kittensandcats.net> writes:
>And for this exact reason, "as" was invented. Which not only asks the
>compiler
>to treat Sender as a TTimer, but also the check that it really is.

But "as" is fairly slow, and where you are sure that you will get only what is
typecast its really painting the lily.

Quote
>(Nice bricktext. But did you have to make it 79 columns wide? You devil,
>you.)

I just copied it <g>

Quote
>It's not so much a matter of fooling as it is of overriding the compiler's
>assumptions.

overriding one's assumptions is as good a definition of "fooling" as I think I
could find <g>.

Alan Lloyd
alangll...@aol.com

Re:Event Handlers on dynamically created forms


Quote
AlanGLLoyd wrote in message

<20021114154833.29847.00000...@mb-fs.aol.com>...

Quote
>In article <3dd4071d$0$46604$e4fe5...@news.xs4all.nl>, "Maarten Wiltink"
><maar...@kittensandcats.net> writes:

>>And for this exact reason, "as" was invented. Which not only asks the
>>compiler
>>to treat Sender as a TTimer, but also the check that it really is.
>But "as" is fairly slow, and where you are sure that you will get only
what is
>typecast its really painting the lily.

Define "slow" in the context of a 2.8 GHz P4. (No, I don't really have
one.)
Seriously, I don't care for that argument. Better safe than sorry.

You may be sure that you will only get the right thing today, but perhaps
next week the code will get modified. By someone who does not have your
deep insight and instant overview of it. And that person is you, one week
from now.

(At least Assert(Sender is TTimer). That's what it's for.)

Groetjes,
Maarten Wiltink

Re:Event Handlers on dynamically created forms


Quote
AlanGLLoyd <alangll...@aol.com> wrote in message

news:20021114154833.29847.00000613@mb-fs.aol.com...

Quote
> In article <3dd4071d$0$46604$e4fe5...@news.xs4all.nl>, "Maarten Wiltink"
> <maar...@kittensandcats.net> writes:

> >And for this exact reason, "as" was invented. Which not only asks the
> >compiler
> >to treat Sender as a TTimer, but also the check that it really is.

> But "as" is fairly slow, and where you are sure that you will get only
what is
> typecast its really painting the lily.

Its a small price to pay for a nice lily.

Dave

Re:Event Handlers on dynamically created forms


You guys are way out of my league!

Can you include a code snippit on how to do this typecasting and what is
"as"?

Thanks

David
(Delphi Nubee!)

Quote
"David Reeve" <drscienti...@powerup.com.au> wrote in message

news:sVWA9.28197$Sr6.807025@ozemail.com.au...
Quote

> AlanGLLoyd <alangll...@aol.com> wrote in message
> news:20021114154833.29847.00000613@mb-fs.aol.com...
> > In article <3dd4071d$0$46604$e4fe5...@news.xs4all.nl>, "Maarten Wiltink"
> > <maar...@kittensandcats.net> writes:

> > >And for this exact reason, "as" was invented. Which not only asks the
> > >compiler
> > >to treat Sender as a TTimer, but also the check that it really is.

> > But "as" is fairly slow, and where you are sure that you will get only
> what is
> > typecast its really painting the lily.

> Its a small price to pay for a nice lily.

> Dave

Re:Event Handlers on dynamically created forms


Quote
david <da...@please.reply.to.list> wrote in message

news:Sc_A9.835$Li.122037@news02.tsnz.net...

Quote
> You guys are way out of my league!

> Can you include a code snippit on how to do this typecasting and what is
> "as"?

> Thanks

> David
> (Delphi Nubee!)

The code under scrutiny was

procedure TInstance.OnTimer1(Sender: Tobject);
begin
  TTimer(Sender).Enabled:=False; <-------------################
  showmessage('Timer');
end;

In the case of.... TTimer(Sender) ....we have what is known as a hard or
bruteforce cast. The compiler basically says 'sure boss if you want me to
offset into the virtual memory table of Sender using the offset derived from
Enabled in the TTimer class,  AND then to start executing what I find there,
THEN on your head be it'  Clear as mud?? .....Check out VMT in your delphi
help to get a good description of the virtual memory table.

To start executing a random piece of memory on the assumption it codes the
GetEnabled method (which the act of referencing Enabled does) is going to
end badly, but hopefully will trap out as an Access Violation before things
go too awry. Now If we ensure Sender is a TTimer before referencing the
enabled property, then all is sweet. The delphi 'as' keyword does just this.

If you use .....(Sender as TTimer).Enabled := False...... then all is well.
This 'smart' cast checks that Sender is of the same class as, or a
descendant of TTimer. It does this by traversing the VMT as a chain starting
at Senders class right back down to TObject if necessary. If TTimer is found
along the chain, the call will be made, if not an exception is generated.
Sure you still get an exception, but this one is orderly, and you are not
allowing the machine to run on until it AVs. Once again have a look at the
VMT documentation, and then some of TObjects class methods, and you'll see
how this is achieved........ you'll also see if my memory serves me
correctly ...:-)

Dave

Other Threads