Mike K.
Mike K.

Reputation: 145

Delphi main form goes behind other applications after disable/enable

I have some vcl mdi application and I need to make some work inside it and show another not modal form of progress above it. And because this progress form is updated from some thread and have some buttons on it (to cancel operation), I update progress with application.ProcessMessages. During this process I need to disable all main form controls. I do it in this way:

MainForm.enabled := false;
... do some work here, update progress ...
MainForm.enabled := true;

Though, after this my main form goes under other windows applications which is not cool. If I remove this two lines enabled/disabled - it stays on top as it should be.

Any ideas how to fix it?

Upvotes: 0

Views: 1505

Answers (2)

W. Chang
W. Chang

Reputation: 502

A simple BringTofront works. It can be before or after setting enabled to be true.

MainForm.enabled := false;
// ... do some work here, update progress ...
MainForm.enabled := true;
MainForm.BringTofront;

or

MainForm.enabled := false;
// ... do some work here, update progress ...
MainForm.BringTofront;
MainForm.enabled := true;

Edit: As commented below, with the above code the main form will go behind the others before it becomes focused. This happens in Delphi 7 but seems not in Delphi 2009.

When the current active form is closed or becomes hidden, Delphi tries to switch to the main form. However, if the main form is disabled, Delphi cannot activate it and will move it to the back. The solution is fairly simple, just set Enabled to be true right before hiding or freeing the other form. There is no need to call BringTofront.

MainForm.enabled := false;
ProgressForm.Show;  // Or Application.CreateForm(...)
// ... do some work here, update progress ...
MainForm.enabled := true;
ProgressForm.Hide;  // Or ProgressForm.Free;

Edit: Here is the Delphi code that causes this behavior:

procedure TCustomForm.CMShowingChanged(var Message: TMessage);
  ....
begin
  ....
      NewActiveWindow := 0;
      if (GetActiveWindow = Handle) and not IsIconic(Handle) then
        NewActiveWindow := FindTopMostWindow(Handle);
      if NewActiveWindow <> 0 then
      begin
        SetWindowPos(Handle, 0, 0, 0, 0, 0, SWP_HIDEWINDOW or
          SWP_NOSIZE or SWP_NOMOVE or SWP_NOZORDER or SWP_NOACTIVATE);
        SetActiveWindow(NewActiveWindow);
      end else
        ShowWindow(Handle, SW_HIDE);
  ....
end;

another fix is to handle the CM_SHOWINGCHANGED message of the second form:

procedure CMSHOWINGCHANGED(var Message: TMessage); message CM_SHOWINGCHANGED;

procedure TProgressForm.CMSHOWINGCHANGED(var Message: TMessage);
begin
  if not visible then
     Application.MainForm.Enabled := True;
  inherited;
end;

Upvotes: 2

Mike K.
Mike K.

Reputation: 145

Ok, looks I found the solution. PopupMode of the progress form set to pmAuto. Main form stays on top in this case.

It works in this way:

procedure TMainForm.DoSomenthing;
begin
  enabled := false;
  try
    // open progress form
    Include(ProgressFrm.FFormState, fsModal);
    ProgressFrm.Show;

    .... do something, update progress ....

    // close progress form
    ProgressFrm.Hide;
    Exclude(ProgressFrm.FFormState, fsModal);
  finally
    enabled := true;
    BringToFront;
  end;
end;

Upvotes: 0

Related Questions