1

In a Delphi VCL app I want to create a 'Wait' message window as a time consuming process is executed (a big-useless-loop for this example).

I have tried the following things to be executed before I start the time-consuming process.

-Create a new form of a simple window that has the message.

-Create a message with messagedlg.

-Even change a TLabel.Caption on the main Form (the one that does time consuming process).

unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, popUpMessage; type TForm1 = class(TForm) Button1: TButton; Label1: TLabel; procedure Button1Click(Sender: TObject); end; var Form1: TForm1; dialog : TForm; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var i, j, k :LongInt; begin {1} popUpMessage.Form2 := TForm2.Create(nil); //also tried with Create(self) Form2.show; {2} dialog := CreateMessageDialog ('Wait',TMsgDlgType.mtWarning, mbYesNoCancel); dialog.Show; {3} messagedlg('Wait',mtError, mbOKCancel, 0); {4} Label1.Caption := 'Wait'; //Time consuming process for i := 0 to 200000 do for j := 0 to 20000do k := i-j; end; end. 

In cases {1} and {2} the pop-up forms appear before the time-consuming process starts but their components are painted only after this has finished.

Case {3} holds the execution until modal dialog box is closed.

In case {4} the caption changes after the time-consuming process is finished.

How can I create a message asynchronously so it is completely draw without waiting its parent's processes?

4
  • 7
    You have to put your time-consuming process onto a separate thread. Commented Aug 13, 2019 at 11:48
  • @nolaspeaker , Isn't this the same with putting the pop-up window in a separate thread? Commented Aug 13, 2019 at 12:56
  • Since you may also be soon tempted by the dark demon of ProcessMessages, here is some additional relevant reading : I do not understand what Application.ProcessMessages in Delphi is doing Commented Aug 13, 2019 at 13:35
  • 1
    @Mikemik You can't put the popup window on a separate thread. In Delphi the UI (User-Interface) thread is where your "displays" are. That's why you must make a separate thread for everything else. And you should never do anything that "displays" in a thread. See J...'s answer below. Commented Aug 13, 2019 at 13:43

1 Answer 1

5

This is a bit of a broad question so I will provide a short example to demonstrate how you can move the long running task to a thread and safely provide feedback to the UI for progress and completion.

I won't bother with a second progress form, for this example I've simply added a button (Button1) and a progress bar (ProgressBar1) to a new TForm.

In the method below we start an anonymous thread to perform the long running operation and periodically notify the main thread at regular progress intervals. We also notify when the work is complete. To extend this you could also perform error checking/handling and could also notify on failure of the operation to complete, but for the sake of brevity I have restricted the example to something simple.

procedure TForm1.Button1Click(Sender: TObject); begin ProgressBar1.Position := 0; Button1.Enabled := false; Button1.Caption := 'Calculating...'; TThread.CreateAnonymousThread( procedure procedure PerformProgressStep; begin TThread.Queue(nil, procedure begin ProgressBar1.StepIt; end); end; procedure NotifyComplete; begin TThread.Queue(nil, procedure begin ShowMessage('done'); Button1.Enabled := true; Button1.Caption := 'Start Task'; end); end; var i, j, k : integer; begin for i := 0 to 199999 do begin for j := 0 to 20000 do k := i-j; if (i mod 20000) = 0 then PerformProgressStep; end; NotifyComplete; end).Start; end; 

Here all UI operations are marshalled to the main thread using TThread.Queue. .Synchronize is also an option if you must wait for the main thread to process the work before continuing with operations in the thread but you must take care to avoid deadlock situations in that case.

There isn't any error handling in this code, do take care also - this is just to demonstrate how you can move your work into a background thread. There are many other ways to do this, using an anonymous thread (as above), a custom TThread if you have a heavier implementation to encapsulate, using Tasks and the RTL threadpool, using the parallel loop operations in the Threading unit, etc. Whichever one you decide to use will depend on the requirements for your specific application.


For a more in-depth exploration of multithreading in Delphi, you can always follow up with Martin Harvey's excellent article here. This is quite an old article so it touches on the subject of multithreading at a somewhat lower level and uses rudimentary RTL and WinAPI constructs and does not include any newer language features included in more modern versions of Delphi. It is highly instructive, however, and is excellent reading for brushing up on fundamentals.

Sign up to request clarification or add additional context in comments.

6 Comments

In addition to @J...'s excellent answer, you might also take a look at this seminal paper: martincharvey.net/mch_custom_pages/Original/ToC.html
@MartynA Fantastic. Haven't seen that before - what a great reference.
It would be good if you could weave a link to it into your answer so it shows up in google searches when peope who end up here are searching the topic.
Reading that paper over and over again, I finally made a whole parallel threading library that solved all my existing and future needs.
@user30478, no it's not. My library is not published and will probably not ever be.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.