Delphi - 在 FireMonkey 中正确显示消息对话框并返回模态结果

Delphi - Correctly displaying a Message Dialog in FireMonkey and returning the Modal Result

我有一个要移植到 FireMonkey 的 VCL 应用程序。我 运行 感兴趣的一件事是 MessageDlg(...) 在 FireMonkey 中被弃用。进一步挖掘,我明白我必须使用 FMX.DialogService.MessageDialog 方法。所以我创建了一个显示对话框的函数:

function TfMain.GetDeleteConfirmation(AMessage: String): String;
var
  lResult: String;
begin
  lResult:='';
  TDialogService.PreferredMode:=TDialogService.TPreferredMode.Platform;
  TDialogService.MessageDialog(AMessage, TMsgDlgType.mtConfirmation,
    [ TMsgDlgBtn.mbYes, TMsgDlgBtn.mbCancel ], TMsgDlgBtn.mbCancel, 0,
    procedure(const AResult: TModalResult)
    begin
      case AResult of
        mrYes:    lResult:='Y';
        mrCancel: lResult:='C';
      end;
    end);

  Result:=lResult;
end;

我不认为我这样做是正确的,因为我不确定我是否可以在匿名方法中设置局部变量,但它仍然可以编译。

我是这样称呼它的:

  if GetDeleteConfirmation('Are you sure you want to delete this entry?')<>'Y' then
    exit;

当我运行它时,显示的消息对话框是这样的:

它不显示 2 个按钮(是,取消)。有人可以帮我解决这个问题 - 即正确显示带有 2 个按钮的消息对话框,并将消息对话框的模态结果作为函数的结果发回。

我正在使用 Delphi 10.1 柏林更新 2。

非常感谢!

编辑 20170320: 我根据下面@LURD 的正确答案更正了我的代码,为了完整起见,我将其包含在此处:

function TfMain.GetDeleteConfirmation(AMessage: String): String;
var
  lResultStr: String;
begin
  lResultStr:='';
  TDialogService.PreferredMode:=TDialogService.TPreferredMode.Platform;
  TDialogService.MessageDialog(AMessage, TMsgDlgType.mtConfirmation,
    FMX.Dialogs.mbYesNo, TMsgDlgBtn.mbNo, 0,
    procedure(const AResult: TModalResult)
    begin
      case AResult of
        mrYes: lResultStr:='Y';
        mrNo:  lResultStr:='N';
      end;
    end);

  Result:=lResultStr;
end;

嗨朋友试试这个代码:

function myMessageDialog(const AMessage: string; const ADialogType: TMsgDlgType;
  const AButtons: TMsgDlgButtons; const ADefaultButton: TMsgDlgBtn): Integer;
var
  mr: TModalResult;
begin
  mr:=mrNone;
  // standart call with callback anonimous method
  TDialogService.MessageDialog(AMessage, ADialogType, AButtons,
    ADefaultButton, 0,
    procedure (const AResult: TModalResult) 
    begin 
      mr:=AResult 
    end);

  while mr = mrNone do // wait for modal result
    Application.ProcessMessages;
  Result:=mr;
end;

或者这样:

function MsgBox(const AMessage: string; const ADialogType: TMsgDlgType; const AButtons: TMsgDlgButtons;
    const ADefaultButton: TMsgDlgBtn ): Integer;
var
    myAns: Integer;
    IsDisplayed: Boolean;
begin
    myAns := -1;
    IsDisplayed := False;

While myAns = -1 do
Begin
    if IsDisplayed = False then
    TDialogService.MessageDialog(AMessage, ADialogType, AButtons, ADefaultButton, 0,
            procedure (const AResult: TModalResult)
            begin
                myAns := AResult;
                IsDisplayed := True;
            end);

    IsDisplayed := True;
    Application.ProcessMessages;
End;

Result := myAns;

end;

尽情享受吧!

问题:

It does not show the 2 buttons (Yes, Cancel). Could someone please help me get this right - i.e. correctly show the message dialog with the 2 buttons and send the modal result of the message dialog back as the Result of the function.

Fmx.TDialogService.MessageDialog 不支持对话框按钮的任意组合。

查看源代码 (Fmx.Dialogs.Win.pas) 揭示了这些有效组合(mbHelp 可以包含在所有组合中):

  • mbOk
  • mbOk,mbCancel
  • mbYes,mbNo,mbCancel
  • mbYes, mbYesToAll, mbNo, mbNoToAll, mbCancel
  • mbAbort, mbRetry, mbIgnore
  • mbAbort, mbIgnore
  • mbYes, mbNo
  • mbAbort, mbCancel

这意味着 [mbYes,mbCancel] 不是一个有效的组合,例如使用 [mbOk,mbCancel] 代替。


关于 Fmx.TDialogService.MessageDialog 的最后说明。它通常是桌面应用程序上的同步对话框,但在移动平台上是异步的。根据这些条件,用例看起来会有些不同,因此对于多平台应用程序,请检查 TDialogService.PreferredMode 的值。

在移动设备上 OS 就像 android 没有模态对话框这样的东西。

VCL版本是这样的: case MessageDlg(.....) 的 mrOk : DoOkStuff; 取消先生:DoCancelStuff 结束;

FMX 版本必须是这样的: TDialogService.MessageDialog(......., 程序(常量 AResult:TModalResult) 开始 案例A的结果 mrOk : DoOkStuff; 取消先生:DoCancelStuff 结尾; 结束);

关闭后必须完成的所有事情都应该在这个匿名过程中。

不要试图模仿 VCL 的 MessageDialog 并且不要使用 Application.ProcessMessages。

这是我的答案,Firemonkey delphi Rio 10.3 的完美功能。
代码在组件按钮内。

procedure TForm3.Button4Click(Sender: TObject);
begin

    MessageDlg('seleccione un boton',System.UITypes.TMsgDlgType.mtConfirmation,
    [System.UITypes.TMsgDlgBtn.mbYes,
    System.UITypes.TMsgDlgBtn.mbNo],0,prozedurale (const AResult:     System.UITypes.TModalResult)
    begin
        if  AResult=mrYES Then
          ShowMessage('tu seleccioneste YES')
        else
           ShowMessage('tu seleccioneste No');

    end);   //Fin de MesageDlg

end;

此方法在 Android SDK 29 中部署的 Alexandria 11.0 应用程序中运行良好。

procedure TForm1.Button1Click(Sender: TObject);
begin

  TDialogService.MessageDialog('Select a button:',
                TMsgDlgType.mtConfirmation,
                FMX.Dialogs.mbYesNoCancel,
                TMsgDlgBtn.mbNo,
                0,
      procedure(const AResult: System.UITypes.TModalResult)
      begin
        if AResult = mrYES Then
          ShowMessage('Yes was selected')
        else if AResult = mrNo Then
          ShowMessage('No was selected')
        else if AResult = mrCancel Then
          ShowMessage('Cancel was selected');

      end); 

end;

根据其他答案,我在 TApplicationHelper (which also has some extra methods, removed here), currently maintained under the READCOM_App 存储库

中添加了一个“确认”class 函数(适用于 return 布尔值)
unit Zoomicon.Helpers.FMX.Forms.ApplicationHelper;

interface
uses
  FMX.Forms; //for TApplication

type
  TApplicationHelper = class helper for TApplication
  public
    class function Confirm(Prompt: String): Boolean;
  end;

implementation
  uses
    System.UITypes, //for TMsgDlgType
    FMX.DialogService, //for TDialogService
    FMX.Dialogs; //for mbYesNo

{$region 'TApplicationHelper'}  

//based on 
class procedure TApplicationHelper.Confirm(const Prompt: String; const SetConfirmationResult: TProc<Boolean>); //based on 
begin
  with TDialogService do
  begin
    PreferredMode := TPreferredMode.Platform;
    MessageDialog(Prompt, TMsgDlgType.mtConfirmation, mbYesNo, TMsgDlgBtn.mbNo, 0,
      procedure(const AResult: TModalResult)
      begin
        //Note: assuming this is executed on the main/UI thread later on, so we just call the "SetResult" callback procedure passing it the dialog result value
        case AResult of
          mrYes: SetConfirmationResult(true);
          mrNo:  SetConfirmationResult(false);
        end;
      end
    );
  end;
end;

{$endregion}

end.

用法示例在READCOM_App

MainForm
procedure TMainForm.HUDactionNewExecute(Sender: TObject);
begin
  TApplication.Confirm(MSG_CONFIRM_CLEAR_STORY, //confirmation done only at the action level //Note: could also use Application.Confirm since Confirm is defined as a class function in ApplicationHelper (and those can be called on object instances of the respective class too)
  procedure(Confirmed: Boolean)
  begin
    if Confirmed and (not LoadDefaultDocument) then
      NewRootStoryItem;
  end
);

结束;

其中 MSG_CONFIRM_CLEAR_STORY 声明为

resourcestring
  MSG_CONFIRM_CLEAR_STORY = 'Clearing story: are you sure?';