Mike mik
Mike mik

Reputation: 91

Show asynchronous 'wait' window

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?

Upvotes: 1

Views: 1779

Answers (1)

J...
J...

Reputation: 31403

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.

Upvotes: 5

Related Questions