alvaroc
alvaroc

Reputation: 469

Switching modal forms in Delphi

I have a modal form (A) that shows another modal form (B). B displays a dataset and allows the user to interact with it. My problem is that one action requires that A becomes again the focused form so the user can input certain values without closing B. I have tried A.BringToFront and A.SetFocus and it indeed is shown at front, but the input focus remains in B and any click or the like in A results in the windows "ding" when you click where you should not. The code is some how like

A.ShowModal; . . . inside an event of A: B.ShowModal(); . . . inside an event of B:

someobject.someMethodThatRequiresAFocused;

My guess is that some obscure and strange API call could make A modal again ¿Any ideas?

Regards

Upvotes: 1

Views: 2624

Answers (2)

Sertac Akyuz
Sertac Akyuz

Reputation: 54832

There's no API that toggles modality between windows. In any case the API you're looking for your case is EnableWindow. That's how modality works, windows other than the one that the user should be working with are disabled so that he/she cannot interact with them. This is also the reason for the 'ding' sound, to provide feedback to the user.

So while letting the user work with a window that's been disabled in favor of another modal window is technically easy, handling states may not be straight forward. I present a bare minimum example below for what it would seem to take.

'FormB' first. Lets suppose you pass a reference of 'FormA' in the 'Owner' parameter while 'FormA' is constructing 'FormB'. The below is what the code that should make 'FormA' modal again could look like:

procedure TFormB.BtnMakeFormAModalAgainClick(Sender: TObject);
begin
  Enabled := False;                  // so that 'A' will behave like it's modal
  EnableWindow(TFormA(Owner).Handle, True);  // so that 'A' could be interacted
  TFormA(Owner).SetFocus;
end;

When this code runs, what happens is 'FormA' is enabled and brought to front, and 'FormB' is disabled - will produce a 'ding' when clicked on.

But we're not done yet. Because we have modified the meaning of modality - now we don't want 'FormA' to be closed when the user is done with it. Below is how could the code in 'FormA's unit could look like:

type
  TFormA = class(TForm)
    BtnShowModalB: TButton;
    BtnOk: TButton;
    procedure BtnShowModalBClick(Sender: TObject);
    procedure BtnOkClick(Sender: TObject);
  private
    FModalB: TForm;
  end;

implementation

uses
  unitOfFormB;

{$R *.dfm}

procedure TFormA.BtnShowModalBClick(Sender: TObject);
begin
  FModalB := TFormB.Create(Self);  // so that FormB can find FormA from the Owner
  FModalB.ShowModal;
  FModalB.Free;
  FModalB := nil; // Need this if we're going to decide if FormB is showing 
                  // by testing against this reference
end;

procedure TFormA.BtnOkClick(Sender: TObject);
begin
  if Assigned(FModalB) then begin // is FormB the actual modal form?
    EnableWindow(Handle, False);  // disable this form so it would 'ding'
    FModalB.Enabled := True;      // enable FormB, so user can interact with it
    FModalB.SetFocus;
    ModalResult := mrNone;        // don't close, FormB is the first one to be closed
  end else
    ModalResult := mrOk;
end;


I'm nearly positive that this example is not complete, but here's the API that you're looking for.

Upvotes: 3

Remy Lebeau
Remy Lebeau

Reputation: 598279

When a modal form is shown, all currently visible forms including other modal forms are disabled. As such, it is not possible to switch between multiple modal forms. You need to re-think your UI design so that B does not go back to A for new input. At the very least, you could have B open a new modal form C that prompts the user for just the needed values and gives them to B, and then either B or C can update A with the new values afterwards.

Upvotes: 5

Related Questions