Question

I am currently developing cross platform applications on my Windows desktop using Delphi XE-5. I have a unit I want to use for cross platform development (for this example, lets just work with Windows and Android).

In both my Windows and Android apps, I have a button with the following onClick event

procedure TfrmMain.Button1Click(Sender: TObject);
begin
LPSystem:=  TLPSystem.Create;
 try
   if LPSystem.execute then
   begin
    ShowMessage('Logged in!');
   end
   else
   ShowMessage('Not logged in!');
 finally
   LPSystem.Free;
 end;
end;

In the execute routine below, a ShowModal dialog is displayed to the user ( a different form depending on platform)

function TLPSystem.execute: Boolean;

var
 {$IFDEF MSWINDOWS}
  frmLoginW: TfrmLoginW;
 {$ENDIF MSWINDOWS}
 {$IFDEF Android}
 zResult: Boolean;
 frmLoginM: TfrmLoginM;
 {$ENDIF Android}
begin
  result:= False;
  {$IFDEF MSWINDOWS}
  frmLoginW:= TfrmLoginW.Create(nil);
  if frmLoginW.ShowModal = mrOK then
    begin
  {$ENDIF MSWINDOWS}

  {$IFDEF Android}
  frmLoginM:= TfrmLoginM.Create(nil);
  frmLoginM.ShowModal (procedure(ModalResult: TModalResult)
  begin
   if ModalResult = mrOK then
  begin
  {$ENDIF Android}


  {$IFDEF MSWINDOWS}
  end;
  frmLoginW.Free;
  {$ENDIF MSWINDOWS}

  {$IFDEF Android}
   end;
   end);
    if zResult then result:= zResult;
    frmLoginM.Free;
   {$ENDIF Android}
end;

This works great for windows, but on Android, I get my result (i.e., the showmessage) before I get the modal dialog box. Not sure why?

Was it helpful?

Solution

Take away your IFDEF statements and look at what you are actually doing:

Windows:

function TLPSystem.execute: Boolean;
var
  frmLoginW: TfrmLoginW;
begin
  Result := False;
  frmLoginW := TfrmLoginW.Create(nil);
  if frmLoginW.ShowModal = mrOK then
  begin
  end;
  frmLoginW.Free;
end;

This is functionally OK (though it is not assigning the Result based on the dialog's ModalResult).

Android:

function TLPSystem.execute: Boolean;
var
 zResult: Boolean;
 frmLoginM: TfrmLoginM;
begin
  Result := False;
  frmLoginM := TfrmLoginM.Create(nil);
  frmLoginM.ShowModal (
    procedure(ModalResult: TModalResult)
    begin
      if ModalResult = mrOK then
      begin
      end;
    end
  );
  if zResult then Result := zResult;
  frmLoginM.Free;
end;

This is not following the rules outlined in Embarcadero's documentation:

ShowModal Dialogs in FireMonkey Mobile Apps

To make matters worse, the overloaded version of ShowModal() that takes an anonymous procedure runs asynchronously, so your Execute() would exit before the modal Form is closed. It cannot wait on the Form before exiting. So you will have to redesign your code to handle that.

Try something more like this:

type
  TLPSystemLoginEvent = procedure(LoggedIn: Boolean) of object;

  TLPSystem = class
  private
    procedure DoLoginResult(LoggedIn: Boolean);
  public
    OnLoginResult: TLPSystemLoginEvent;
    procedure Execute;
  end;

procedure TLPSystem.DoLoginResult(LoggedIn: Boolean);
begin
  if Assigned(OnLoginResult) then OnLoginResult(LoggedIn);
end;

procedure TLPSystem.Execute;
var
  {$IFDEF MSWINDOWS}
  frmLoginW: TfrmLoginW;
  {$ENDIF}
  {$IFDEF Android}
  frmLoginM: TfrmLoginM;
  LSelf: TLPSystem;
  {$ENDIF}
begin
  {$IFDEF MSWINDOWS}
  frmLoginW := TfrmLoginW.Create(nil);
  try
    DoLoginResult(frmLoginW.ShowModal = mrOK);
  finally
    frmLoginW.Free;
  end;
  {$ENDIF}

  {$IFDEF Android}
  frmLoginM := TfrmLoginM.Create(nil);
  LSelf := Self;
  frmLoginM.ShowModal(
    procedure(ModalResult: TModalResult)
    begin
      LSelf.DoLoginResult(ModalResult = mrOK);
    end
  );
  {$ENDIF}
end;

procedure TfrmLoginM.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := TCloseAction.caFree;
end;

procedure TfrmMain.Button1Click(Sender: TObject);
var
  LPSystem: TLPSystem;
begin
  LPSystem := TLPSystem.Create;
  {$IFDEF MSWINDOWS}
  try
  {$ENDIF}
    LPSystem.OnLoginResult := LoginResult;
    LPSystem.Execute;
  {$IFDEF MSWINDOWS}
  finally
    LPSystem.Free;
  end;
  {$ENDIF}
end;

procedure TfrmMain.LoginResult(LoggedIn: Boolean);
begin
  if LoggedIn then
    ShowMessage('Logged in!')
  else
    ShowMessage('Not logged in!');
end;

Alternatively, you can get rid of the try/finally on Windows by utilizing an ARC interface:

type
  TLPSystemLoginEvent = procedure(LoggedIn: Boolean) of object;

  ILPSystem = interface
    procedure Execute;
  end;

  TLPSystem = class(TInterfacedObject, ILPSystem)
  private
    fOnLoginResult: TLPSystemLoginEvent;
    procedure DoLoginResult(LoggedIn: Boolean);
  public
    constructor Create(ALoginResult: TLPSystemLoginEvent);
    procedure Execute;
  end;

constructor TLPSystem.Create(ALoginResult: TLPSystemLoginEvent);
begin
  inherited Create;
  fOnLoginResult := ALoginResult;
end;

procedure TLPSystem.DoLoginResult(LoggedIn: Boolean);
begin
  if Assigned(fOnLoginResult) then fOnLoginResult(LoggedIn);
end;

procedure TLPSystem.Execute;
var
  {$IFDEF MSWINDOWS}
  frmLoginW: TfrmLoginW;
  {$ENDIF}
  {$IFDEF Android}
  frmLoginM: TfrmLoginM;
  LSelf: ILPSystem;
  {$ENDIF}
begin
  {$IFDEF MSWINDOWS}
  frmLoginW := TfrmLoginW.Create(nil);
  try
    DoLoginResult(frmLoginW.ShowModal = mrOK);
  finally
    frmLoginW.Free;
  end;
  {$ENDIF}

  {$IFDEF Android}
  frmLoginM := TfrmLoginM.Create(nil);
  LSelf := Self;
  frmLoginM.ShowModal(
    procedure(ModalResult: TModalResult)
    begin
      (LSelf as TLPSystem).DoLoginResult(ModalResult = mrOK);
    end
  );
  {$ENDIF}
end;

procedure TfrmLoginM.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := TCloseAction.caFree;
end;

procedure TfrmMain.Button1Click(Sender: TObject);
var
  LPSystem: ILPSystem;
begin
  LPSystem := TLPSystem.Create(LoginResult);
  LPSystem.Execute;
end;

procedure TfrmMain.LoginResult(LoggedIn: Boolean);
begin
  if LoggedIn then
    ShowMessage('Logged in!')
  else
    ShowMessage('Not logged in!');
end;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top