dormky 2 Posted October 13, 2023 (edited) 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 October 13, 2023 by dormky Share this post Link to post
Zoran Bonuš 12 Posted October 13, 2023 (edited) 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 October 13, 2023 by Zoran Bonuš Share this post Link to post
Lajos Juhász 295 Posted October 13, 2023 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
mvanrijnen 123 Posted October 13, 2023 (edited) 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 October 13, 2023 by mvanrijnen 1 Share this post Link to post
BerndS 0 Posted October 13, 2023 I usually use for this: TThread.ForceQueue(nil, FormProcName, 1); Share this post Link to post
dormky 2 Posted October 13, 2023 All of these answers are exactly the kind of hack I'd hoped to avoid... Oh well. Share this post Link to post
Lars Fosdal 1793 Posted October 13, 2023 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
pyscripter 694 Posted October 13, 2023 Use TThread.ForceQueue from inside the OnShow handler. TThread.ForceQueue accepts an optional delay. 1 Share this post Link to post
Sherlock 663 Posted October 13, 2023 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
Clément 148 Posted October 13, 2023 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
aehimself 399 Posted October 13, 2023 The pros of using window messages is you can apply the same logic to TFrame-descendants too. Share this post Link to post
Tom F 85 Posted October 13, 2023 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
Dave Nottage 563 Posted October 13, 2023 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
Anders Melander 1815 Posted October 13, 2023 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
mvanrijnen 123 Posted October 13, 2023 Just now, Anders Melander said: What's wrong with WM_USER + 0 ? Already taken 😎 Share this post Link to post
Anders Melander 1815 Posted October 13, 2023 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). 1 Share this post Link to post
Clément 148 Posted October 14, 2023 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
DelphiUdIT 188 Posted October 14, 2023 (edited) 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 October 14, 2023 by DelphiUdIT Share this post Link to post
Anders Melander 1815 Posted October 14, 2023 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. 1 Share this post Link to post
Remy Lebeau 1436 Posted October 14, 2023 (edited) 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 October 14, 2023 by Remy Lebeau 1 Share this post Link to post
dormky 2 Posted October 16, 2023 (edited) 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 October 16, 2023 by dormky Share this post Link to post
Pat Foley 52 Posted October 16, 2023 (edited) deleted Edited October 17, 2023 by Pat Foley Not needed. Share this post Link to post