6

In my application, I have a main form, with the ability to load some images in database. While images is loading, I want to show a form, with the progress indicator (with bsNone border style).

But, if I show with form with ShowModal, execution of main form is stopped, so I can't to that.

If I call Show, user have access to all other form components, and it can be dangerous, while photo is not loaded completely.

I need to get the way, to disable everything on main form, while loading isn't completed.

Please, advice me, how it is possible.

Andrew Barber
  • 37,547
  • 20
  • 91
  • 118
Andrey
  • 2,419
  • 3
  • 25
  • 51
  • 1
    If you' d like to keep a modal form, you could call the routine from your modal shown form. e.g. by handing over the method to call within the constructror of the modal shown form. – bummi Jul 06 '13 at 16:14
  • Apart from disabling the form, you can also hide the main form. Other possibilities include calling the Load-procedure from the modal progress dialog. – GolezTrol Jul 06 '13 at 16:14

5 Answers5

16

Set the MainForm as the PopupParent for the progress form so that the MainForm can never appear on top of the progress form. Then simply set MainForm.Enabled := False while the progress form is open and set MainForm.Enabled := True when the progress form is closed.

procedure TMainForm.ShowProgressForm;
begin
  with TProgressForm.Create(nil) do
  begin
    PopupParent := Self;
    OnClose := ProgressFormClose;
    Show;
  end;
  Enabled := False;
end;

procedure TMainForm.ProgressFormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  Enabled := True;
end;

This simulates howShowModal() behaves to the user (the MainForm is not user-interactive while the progress form is open), but without blocking the code.

Remy Lebeau
  • 454,445
  • 28
  • 366
  • 620
6

First of all, the direct answer to your question.

I need to get the way, to disable everything on main form.

Set MainForm.Enabled to False to disable the window associated with the main form. And to re-enable set it to True instead.


Your fundamental problem however, is that you are executing long running tasks in the GUI thread. That's always a bad idea and the way out is to execute those long running tasks in a separate thread.

Once you move the long running tasks to a separate thread then you will find that ShowModal is exactly what you need to show your progress form.

David Heffernan
  • 572,264
  • 40
  • 974
  • 1,389
2

As I explained in my other answer, putting the long running task into a thread other than the GUI thread is the ideal solution. The GUI thread should handle short running tasks so that it is always able to service the message queue in a timely fashion.

However, if you already have code that assumes that the long running task runs on the GUI thread, you may prefer to take a more expedient approach and postpone the re-factoring to threaded code. In which case, in my view, it is still better to use ShowModal to display your progress form.

But in order to make that work, you need to find a let the long running task execute inside the ShowModal call. You can do that as follows:

  1. Before you call ShowModal, pass the task to the form. For example, pass a TProc to the constructor of the progress form.
  2. Override the progress form's Activate method. This will get executed just before the ShowModal function starts its modal message loop. In the implementation of Activate, post a message to the form.
  3. When the form handles that message, invoke the task that was passed to the constructor.

Obviously you'll need to call ProcessMessages in your long running task, in order to keep the main GUI thread message queue serviced. Clearly you must already be doing that.

David Heffernan
  • 572,264
  • 40
  • 974
  • 1,389
1

You may want to use DisableTaskWindows and EnableTaskWindows functions.

If you use simple ParentForm.Enabled := False for the parent form, you can still access all other forms, like the main form if it differs from ParentForm. It is obviously still dangerous.

Here is short sample:

interface

uses
  Vcl.Forms, Winapi.Windows, ...;

type
  TPleaseWait = class(TObject)
  private
    fWindowList: TTaskWindowList;
    fActiveWindow: HWND;
    fDialog: TPleaseWaitForm;
  public
    constructor Create;
    destructor Destroy; override;
  end;

implementation

constructor TPleaseWait.Create;
begin
  // Disable all displayed windows
  fWindowList := DisableTaskWindows(0);

  // Save the last active window
  fActiveWindow := GetActiveWindow;

  fDialog := TPleaseWaitForm.Create(nil);
  fDialog.Show;
end;

destructor TPleaseWait.Destroy;
const
  INVALID_HANDLE = 0;
begin
  fDialog.Close;
  fDialog.Free;

  // All windows are enabled now
  EnableTaskWindows(fWindowList);

  // That helps by enabling the last one form
  if (fActiveWindow <> INVALID_HANDLE) and IsWindow(fActiveWindow) then
    SetActiveWindow(fActiveWindow);
  inherited;
end;

end.
Jacek Krawczyk
  • 1,505
  • 1
  • 13
  • 22
1

Set the PopupParent of child form = ParentForm

procedure TParentForm.Button1Click(Sender: TObject);
begin
ParentForm.Enabled:=False;
with Tform1.create(nil) do show;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
ParentForm.Enabled := true;
form1.free;
end;
LW001
  • 1,812
  • 4
  • 19
  • 28
Asad Alamdar
  • 128
  • 8