Jump to content
dormky

How do I execute code after FormShow ?

Recommended Posts

I have calculations that need to be executed when a window is opened, but those calculations take a bunch of time.

I'd like to run them after FormShow, so that the window is shown during the calculations. However, looking at the callsite of TCustomForm.DoShow, I don't see any event getting triggered after the show.

So how can I do this ? The problem with FormActivate is that it's called a bunch of times, and having a flag for this seems like a hacky way to do something the framework should have a feature for.

 

Thanks 🙂

Edited by dormky

Share this post


Link to post

How about testing .Visible in OnActivate?

 

Or starting a timer in OnShow and execute your code few ms later, when the form is already visible.

Edited by Zoran Bonuš

Share this post


Link to post
32 minutes ago, dormky said:

I'd like to run them after FormShow, so that the window is shown during the calculations.

In order to do that you have to move your calculation into a background thread. Otherwise your calculation will block the main thread.

Share this post


Link to post

Make an aftershow method,

See here: SwissDelphiCenter.ch : ...implement AfterShow, AfterCreate events?

 

I use this in baseforms, added a "FirstShow" boolean in the form, so you know if you have to do all calcs all the time or nly one time.

So in my base form i have  protected methods like

 

procedure DoBeforeShow(const AFirstshow : Boolean); virtual;

procedure DoAfterShow(const AFirstshow : Boolean); virtual;

 

Edited by mvanrijnen
  • Like 1

Share this post


Link to post

Those are not hacks - but techniques.

 

I try to avoid blocking the window thread when updating the contents, so I usually spin off queries and calculations in a biz.object.

 

I have a TriggerContentUpdate method that fetches/checks that I have the params I need, sets up the thread and starts it.

When the thread completes, it triggers a Window redraw which checks if there is data to update from in the biz object, otherwise painting a "No data" or "Processing..." message..

 

TriggerContentUpdate can then be called from Form.AfterShow, or whenever the parameter data changes, or by timer for automated refresh.

Share this post


Link to post

Use

TThread.ForceQueue

from inside the OnShow handler.

 

TThread.ForceQueue accepts an optional delay.

  • Like 1

Share this post


Link to post
59 minutes ago, dormky said:

All of these answers are exactly the kind of hack I'd hoped to avoid... Oh well.

In that case I do hope you use that single out of the box method provided by Embarcadero to do that one line of calculating you need, otherwise someone might be inclined to call your solution a hack... just saying.

The VCL framework like most other frameworks provide methods that should suffice for a very high number of use cases. But not all of them, this is to avoid feature creep, and other mishaps.    

Programming is not always straight forward. Sometimes you have to get creative. That is the reason I love it.

Share this post


Link to post

If it's a VCL ( or FMX ) application, I prefer using messages:

const
   _UM_AFTER_SHOW = WM_USER +$0001 ; /// SOme constant.. Be sure not to repeat the number


Type
   TForm1 = Class( TForm )
   private
       procedure event_AfterShow( var aMessage : TMessage ); message _UM_AFTERSHOW;
   end;


Implementation

procedure TForm1.OnShow( aSender : TObject );
begin
   Inherited;
  {.. some code }
  PostMessage( Handle, _UM_AFTERSHOW, 0 , 0 ); // This will post a message to itself
end;


procedure TForm1.event_AfterShow( var aMessage : TMessage );
begin
   // Run the code here
end;


 

 

 

Share this post


Link to post

The pros of using window messages is you can apply the same logic to TFrame-descendants too.

Share this post


Link to post
9 hours ago, dormky said:

...having a flag for this seems like a hacky way to do something the framework should have a feature for.

 

We've used a flag for years. It takes, what?, about one minute to implement and has an almost zero risk of being buggy.  Our advice is to take the easy road here: use a flag.

 

Share this post


Link to post
7 hours ago, dormky said:

All of these answers are exactly the kind of hack I'd hoped to avoid... Oh well.

Using a background thread is extremely normal.

Share this post


Link to post
8 hours ago, Clément said:

const _UM_AFTER_SHOW = WM_USER +$0001 ; /// SOme constant.. Be sure not to repeat the number

What's wrong with WM_USER + 0 ?

Share this post


Link to post
10 minutes ago, mvanrijnen said:

Already taken

Then it was a bad example.

No, my guess is that it's cargo cult; The +1 is so widespread that most people think that it's the first available value (which it isn't).

  • Like 1

Share this post


Link to post
16 hours ago, Anders Melander said:

Then it was a bad example.

No, my guess is that it's cargo cult; The +1 is so widespread that most people think that it's the first available value (which it isn't). 

But...
Message numbers in the second range (WM_USER through 0x7FFF) can be defined and used by an application to send messages within a private window class. These values cannot be used to define messages that are meaningful throughout an application because some predefined window classes already define values in this range. For example, predefined control classes such as BUTTON, EDIT, LISTBOX, and COMBOBOX may use these values. Messages in this range should not be sent to other applications unless the applications have been designed to exchange messages and to attach the same meaning to the message numbers.

Since I'm explicitly posting a message to a private window that is designed to handle it in a specific way and is not a subclass of a Windows control, it should be safe to use it. Isn't it?

Share this post


Link to post

You can find the WM_USER+xxxx used by Embarcadero components by running a

 grep -i -d WM_USER *.pas

from the "source" directory in your Delphi installation (at least for versions that also have the source distribution).
However, what should be clear is that Windows messages must be directed directly to a handle (therefore specific control). It is therefore absolutely not a given that a WM_USER+100 used for example in Vcl.DBGrids cannot be used within the application. And I would say that it would be somewhat "questionable" that WM messages are used internally by Embarcadero components in a generic manner and not "privately" solely and exclusively for that component.

Edited by DelphiUdIT

Share this post


Link to post
4 hours ago, Clément said:

Since I'm explicitly posting a message to a private window that is designed to handle it in a specific way and is not a subclass of a Windows control, it should be safe to use it. Isn't it?

You missed the point: The +$0001 is not necessary. The first available custom message ID is WM_USER, not WM_USER+1.

  • Like 1

Share this post


Link to post
4 hours ago, Clément said:

Message numbers in the second range (WM_USER through 0x7FFF) can be defined and used by an application to send messages within a private window class. These values cannot be used to define messages that are meaningful throughout an application

That is what the WM_APP range, and RegisterWindowMessage(), are meant for instead.

4 hours ago, Clément said:

Since I'm explicitly posting a message to a private window that is designed to handle it in a specific way and is not a subclass of a Windows control, it should be safe to use it. Isn't it?

Yes, it is perfectly safe to use the WM_USER range for your own messages within your own window.

Edited by Remy Lebeau
  • Like 1

Share this post


Link to post
On 10/13/2023 at 9:04 PM, Tom F said:

We've used a flag for years. It takes, what?, about one minute to implement and has an almost zero risk of being buggy.  Our advice is to take the easy road here: use a flag.

 

I consider it a "hacky way" of doing things because it pulls up state into a more global context, despite said state being single-use. That's a red flag to me, unless you are implementing this at framework level.

And I consider being able to say "execute this after you're done drawing" to be a very basic framework feature.

Edited by dormky

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×