Recent

Author Topic: Calling a procedure on form first appearance  (Read 17915 times)

TS-MPS

  • New Member
  • *
  • Posts: 35
Calling a procedure on form first appearance
« on: November 24, 2009, 12:43:48 am »
I'm trying to convert an old Delphi program into Lazarus and having a few problems with a progress form it uses.

What happens is that user enters a bunch of info into form1, then clicks the "Start" button. This launches form2 which is basically just a bunch of text labels that update every few records.

The way the program works in Delphi is that the form2.activate procedure launches the actual procedure that does the work (reading an input file, processing etc).

The problem I have is that in Lazarus, the form2.activate procedure is called every time the form gets focus - this is different to Delphi which just calls it the first time.

I tried to use the form2.show procedure, which does see to work just the once but the code triggers before the form has appeared on screen.

I did think that maybe I could setup a timer on form2 in the form2.show procedure and use this to trigger the code that does the work...it's a bit messy though...

Does anyone have any good method of doing this sort of thing i.e showing another child form which then does the work instead of the main form ?


cdbc

  • Hero Member
  • *****
  • Posts: 2686
    • http://www.cdbc.dk
Re: Calling a procedure on form first appearance
« Reply #1 on: November 24, 2009, 10:48:31 am »
Hi.

Have you tried Form2.FormShow event?

Otherwise:

Code: [Select]
Form2.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled:= false;
  Call_Your_Start_Procedure_Here;
end;

Form2.FormShow(Sender: TObject);
begin
  Timer1.Interval:= 100;
  Timer1.OnTimer:= @Timer1Timer;
  Timer1.Enabled:= true; 
end;

This would also do the trick...

HTH
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

TS-MPS

  • New Member
  • *
  • Posts: 35
Re: Calling a procedure on form first appearance
« Reply #2 on: November 24, 2009, 12:23:48 pm »
Hi.

Have you tried Form2.FormShow event?

Otherwise:

Code: [Select]
Form2.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled:= false;
  Call_Your_Start_Procedure_Here;
end;

Form2.FormShow(Sender: TObject);
begin
  Timer1.Interval:= 100;
  Timer1.OnTimer:= @Timer1Timer;
  Timer1.Enabled:= true;  
end;

This would also do the trick...

HTH
Regards Benny

Yes, I tried form2.formshow event and this is the cause of my current problem with the form not showing :-) The formshow event seems to execute before the form is actually displayed (this is the same behaviour as Delphi though, so not a new feature). This is why I had my code in the formactivate event instead.

To show what I'm trying to do, consider the following simple app:

Create a new app with 2 forms - form1 and form2. On form 1 place a button that executes form2.showmodal. On form2, place a random selection of controls (they don't have to do anything, it's just so you can see if the form has fully painted or not)

Now in the form2.formshow event, put some code, for example 'messagdlg ('hello world',mtinformation,[mbok],0);'

When you click the button on form1, the messagedlg appears. Form2 does not appear. When you close the messagedlg, then form2 appears. I did think at first it just needed some application.processmessages commands before the messagedlg, but these make no difference. However, this behaviour is the same as Delphi, so this would suggest that formshow is not designed to be used for this sort of thing, and that formactivate is the right place for this sort of stuff.

Now, if you put the messagedlg code in the form2.formactivate event instead, then it all displays fine, exactly as it does in Delphi. Yay ! However, there is a big problem in that the formactivate event triggers each time the form gets focus (unlike Delphi which just runs it when the form is first called)

So I can't use formshow because it runs before painting the form, and I can't use formactivate because it runs every time the form gets focus.

I tried the timer suggestion and it works but I don't like using a timer on the form to kick off the code, as this just seems "wrong" to me. The app doesn't "know" that the form has displayed correctly, it just assumes it is as there as the delay is "long enough" for it to have finsihed displaying. Yuk ! :-)

The good news is that I've worked around the strange behaviour of formactivate by wrapping execution of the code around a control variable 'is_running'. I set this variable to false in form1 when the button which executes the form2.showmodal is clicked. The form2.formactivate will only run my code if the value is false, and the first thing the code block does is set it to true. For example:

Code: [Select]
procedure TForm1.Button1Click(Sender: TObject);
begin
  is_running := false ;
  form2.showmodal ;
end;

procedure TForm2.FormActivate(Sender: TObject);
begin
  if is_running = false then
  begin
       is_running := true ;
       messagedlg ( 'hello world',mtinformation,[mbok],0);
  end;
end;    

I still don't like that, but it's less of a horrible bodge than using a timer I think.

I wonder if there are any plans to change formactivate to mirror the Delphi mode of operation ?

Edit: Reworded post a bit to make more sense !!
« Last Edit: November 24, 2009, 01:03:05 pm by TS-MPS »

Bart

  • Hero Member
  • *****
  • Posts: 5706
    • Bart en Mariska's Webstek
Re: Calling a procedure on form first appearance
« Reply #3 on: November 24, 2009, 04:54:19 pm »
Define a method like
procedure DoSomething(DummyParam: PtrInt);

then in OnActivate do
  Application.QueueAsyncCall(@DoSomething, 0);

This will make sure that DoSomething is called after pending messages are handled, in effect this means that first the form is Shown and after that has been handled DoSomething is executed.

Bart

Troodon

  • Sr. Member
  • ****
  • Posts: 484
Re: Calling a procedure on form first appearance
« Reply #4 on: November 24, 2009, 05:39:57 pm »
The problem I have is that in Lazarus, the form2.activate procedure is called every time the form gets focus - this is different to Delphi which just calls it the first time.

Perhaps this should be filed as a bug.
Lazarus/FPC on Linux

TS-MPS

  • New Member
  • *
  • Posts: 35
Re: Calling a procedure on form first appearance
« Reply #5 on: November 24, 2009, 07:52:51 pm »
I did find this article about Delphi forms on the Internet:
http://delphi.about.com/od/formsdialogs/a/delphiformlife.htm

Code: [Select]
Birth
OnCreate
The OnCreate event is fired when a TForm is first created, that is, only once. The statement responsible for creating the form is in the project's source (if the form is set to be automatically created by the project). When a form is being created and its Visible property is True, the following events occur in the order listed: OnCreate, OnShow, OnActivate, OnPaint.

You should use the OnCreate event handler to do, for example, initialization chores like allocating string lists.

Any objects created in the OnCreate event should be freed by the OnDestroy event.

    OnCreate -> OnShow -> OnActivate -> OnPaint -> OnResize -> OnPaint ...

OnShow
This event indicates that the form is being displayed. OnShow is called just before a form becomes visible. Besides main forms, this event happens when we set forms Visible property to True, or call the Show or ShowModal method.

OnActivate
This event is called when the program activates the form - that is, when the form receives the input focus. Use this event to change which control actually gets focus if it is not the one desired.

OnPaint, OnResize
Events like OnPaint and OnResize are always called after the form is initially created, but are also called repeatedly. OnPaint occurs before any controls on the form are painted (use it for special painting on the form).

The description for the OnActivate event could be ambiguous I think but at first reading it would seem to mean that the event is called each time the form receives input focus. Also the life cycle at the start of the article would seem to suggest that OnActivate is called after OnShow.

However, when you read the description for OnPaint & OnResize they mention that they are called repeatedly, but there is no mention that OnShow or OnActivate can be called again. So is it safe to assume that the description for OnActivate should say "that is, when the form receives the input focus [for the first time]" ?

I'm not really sure if there is a "bug" in Lazarus/FPC in that it's not behaving as an exact Delphi clone, or if actually Delphi is doing it "wrong" in the first place and we're now doing things the correct way.

Really I'd rather somebody who has more experience than me of coding in other languages or the Windows API make that call !!  ::)


Troodon

  • Sr. Member
  • ****
  • Posts: 484
Re: Calling a procedure on form first appearance
« Reply #6 on: November 24, 2009, 08:18:14 pm »
Workaround: use a FirstTime variable with OnActivate:

Code: [Select]
type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormActivate(Sender: TObject);
  private
  public
    FirstTime: Boolean;
  end;

var
  Form1: TForm1;

implementation

procedure TForm1.FormCreate(Sender: TObject);
begin
  FirstTime := True;
end;

procedure TForm1.FormActivate(Sender: TObject);
begin
  if FirstTime then begin
    FirstTime := False;
    ShowMessage('OnActivate called for the first time');
  end;
end;

Lazarus/FPC on Linux

 

TinyPortal © 2005-2018