【问题标题】:delphi XE-5 cross platform development - calling a showmodal formdelphi XE-5跨平台开发——调用showmodal形式
【发布时间】:2023-03-26 08:59:01
【问题描述】:

我目前正在使用 Delphi XE-5 在我的 Windows 桌面上开发跨平台应用程序。我有一个单元我想用于跨平台开发(对于这个例子,让我们只使用 Windows 和 Android)。

在我的 Windows 和 Android 应用程序中,我都有一个带有以下 onClick 事件的按钮

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;

在下面的执行例程中,向用户显示一个 ShowModal 对话框(取决于平台的不同形式)

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;

这对 Windows 非常有用,但在 Android 上,我会在获得模式对话框之前获得我的结果(即显示消息)。不知道为什么?

【问题讨论】:

标签: delphi firemonkey delphi-xe5


【解决方案1】:

拿走你的IFDEF 声明,看看你实际上在做什么:

窗户:

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

这在功能上是可以的(尽管它没有根据对话框的ModalResult 分配Result)。

安卓:

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;

这不符合 Embarcadero 文档中概述的规则:

ShowModal Dialogs in FireMonkey Mobile Apps

更糟糕的是,采用匿名过程的ShowModal() 的重载版本异步运行,因此您的Execute() 将在模态表单关闭之前退出。它不能在退出前等待表单。所以你必须重新设计你的代码来处理它。

试试这样的:

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;

或者,您可以通过使用 ARC interface 在 Windows 上摆脱 try/finally

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;

【讨论】:

  • 抱歉,此例程不适用于 windows 或 android!没有实例化 frmLoginW(Windows 对话框表单)的位置,只有 android 实例(frmLoginM)。我相信它们各自的 IFDEF 中的每个对象都应该有一个 var。使用 android 部分,它会在显示对话框之前显示结果 - 就像我发布的另一个问题一样,您发布了一个答案。另一方面,我能够通过添加用于实例化的代码并将其放置在适当的 IFDEF 中来编辑代码并让 Windows 版本正常工作
  • 您对节目信息的权利。我什至无法在主窗体端使用它们。如果我摆脱那里的 ShowMessages,那么首先显示模态表单。我不明白为什么会这样,但我想我可以不用它们。
  • @LuvRAD:关于frmLoginW 的好消息,我什至没有注意到这两个平台使用不同的类类型。我已经调整了答案。
  • ShowModal 在移动编译器上没有阻塞。最后一个示例在 Android 中不起作用,因为执行退出时 zResult 未初始化。 DisposeOf 调用也是错误的,因为匿名框架依赖于在返回 ModalResult 时有效的对象。即使已修复此问题,也不建议在移动编译器上使用 ShowModal。请改用Show
  • 我使用ShowModal()更新了我的答案以采取不同的方法。
猜你喜欢
  • 2010-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多