Do you want to use FMX dialogs in Delphi applications? Read all about how to do this in this article from Tech Partner Softacom.
Table of Contents
How to Use FMX Dialogs Correctly in Delphi Apps
Delphi FMX provides support for customized native dialog boxes (like those used for confirmations and warnings), which are available on all supported target platforms. This article will show the basics and the rules of using these dialogs correctly in your Delphi FMX apps.
What Dialogs Are Available?
There are 3 kinds of native dialogs available in FMX for every OS – Windows, Linux, macOS, iOS, and Android:
- MessageDialog: a dialog box of a special kind (Error, Confirmation, Information, Warning). It contains text and a custom set of buttons, with every button returning a response as an integer to the calling app.
- InputQuery: a dialog box that contains a custom number of text input fields (TEdit controls). This dialog will return an array of strings to the calling app.
- ShowMessage: a simpler dialog box, used only for information with text and an “OK” button. Nothing is returned to the calling app.
Basics of FMX Dialogs
Dialog behavior
According to the platform, dialogs can have two different behaviors:
- Synchronous: the app is blocked by the dialog box until it is closed by the user. This behavior is supported on Windows, MacOS, and iOS. But it is not supported on Android (because the OS does not support blocking of the app’s main thread).
- Asynchronous: the app is not blocked by the dialog box. The app remains accessible for user interactions. This behavior is supported on Windows, MacOS, iOS and Android.
The methods for calling Sync dialogs are functions that return responses to the executing app, while the methods for calling Async dialogs are procedures containing anonymous methods (with the dialog responses as parameters) that are executed when the dialog is closed.
Both behaviors are implemented in FMX platform services.
The actual FMX architecture provides two concepts for using these dialogs, you can choose between:
- Using a platform service from the “FMX.Platform” unit, by declaring an instance of the service interface (as a variable) and the “Supports” routine. Those service interfaces are:
- Or, using the helper class that provides class methods and functions for calling the dialog boxes without the need for an instance of the corresponding dialog box platform service. Those helper classes are:
Dialog type
For the “MessageDialog” kind, you have to define the type of your dialog box, which will affect its appearance. Mostly the title of the dialog box (which is localized to the underlying OS language).
The type of the dialog box is set by a value of “TMsgDlgType”:
TMsgDlgType = (mtWarning, mtError, mtInformation, mtConfirmation, mtCustom);
| Value | Meaning | Dialog box title |
| mtWarning | Warns the user about a potential issue. | Warning |
| mtError | Informs the user of an error that occurred. | Error |
| mtInformation | Provides information to the user. | Information |
| mtConfirmation | Asks the user for confirmation. | Confirm |
| mtCustom | None of the above. | Your project name |
The possible values of “TMsgDlgType”
Dialog box buttons
For the “MessageDialog,” you can choose a set of buttons (their type is “TMsgDlgBtn”) to appear at the MessageDialog bottom. Also, the captions on these buttons are localized according to the OS language.
Each of these buttons, when clicked, will close the dialog box and return a corresponding integer value as “TModalResult”. Because, in fact, the dialog box is a modal form, and setting the value of its TModalResult property to a no-zero integer value will close the modal form.
The returned TModalResult value can be used in the caller app, either assigned directly to a variable (in case of Sync dialogs) or in an anonymous method to perform custom actions after closing the dialog box (in case of Async dialogs).
The table below enumerates the MessageDialog buttons and their corresponding returned ModalResult (as named constants and integers):
| TMsgDlgBtn value | Corresponding returned value | ModalResult corresponding integer value |
| mbOK | mrOk | 1 |
| mbCancel | mrCancel | 2 |
| mbAbort | mrAbort | 3 |
| mbRetry | mrRetry | 4 |
| mbIgnore | mrIgnore | 5 |
| mbYes | mrYes | 6 |
| mbNo | mrNo | 7 |
| mbClose | mrClose | 8 |
| mbHelp | mrHelp | 9 |
| mbAll | mrAll | 12 |
| mbNoToAll | mrNoToAll | 13 |
| mbYesToAll | mrYesToAll | 14 |
Also, the “FMX.Dialogs” unit defines constants that you can use to represent some predefined sets of buttons, as shown in the following table:
| mbYesNo | TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo |
| mbYesNoCancel | TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo, TMsgDlgBtn.mbCancel |
| mbYesAllNoAllCancel | TMsgDlgBtn.mbYes, TMsgDlgBtn.mbYesToAll, TMsgDlgBtn.mbNo, TMsgDlgBtn.mbNoToAll, TMsgDlgBtn.mbCancel |
| mbOKCancel | TMsgDlgBtn.mbOK, TMsgDlgBtn.mbCancel |
| mbAbortRetryIgnore | TMsgDlgBtn.mbAbort, TMsgDlgBtn.mbRetry, TMsgDlgBtn.mbIgnore |
| mbAbortIgnore | TMsgDlgBtn.mbAbort, TMsgDlgBtn.mbIgnore |
How to use the different FireMonkey FMX dialogs?
Now, let’s see how to use each kind of these native dialogs.
What is the difference between the Sync and Async versions of message dialogs in FireMonkey FMX?
There are two versions – “sync” and “async”. The sync version is more like the kind of message dialog that you may have used with VCL apps on Windows. You can read the full details in the Embarcadero DocWiki entry here: https://docwiki.embarcadero.com/Libraries/en/FMX.DialogService.TDialogService.ShowMessage
Briefly, from that page here is an explanation of the differences between Sync and Async:
- On desktop platforms (Windows, macOS, Linux), ShowMessage behaves synchronously. The call finishes only when the user closes the dialog box.
- On mobile platforms (Android and iOS), ShowMessage behaves asynchronously. The call finishes instantaneously, it does not wait for the user to close the dialog box.
How to use the FireMonkey FMX “ShowMessage” version of a dialog
In the simplest kind of dialog, you only need to specify the text that will be shown. The code for calling ShowMessage Async:
procedure TForm1.Button1Click(Sender: TObject);
var
AsyncService: IFMXDialogServiceAsync;
begin
if TPlatformServices.Current.SupportsPlatformService (IFMXDialogServiceAsync, IInterface(AsyncService)) then
begin
AsyncService.ShowMessageAsync(‘This is ShowMessage Async’);
end;
end;
The code for calling ShowMessage Sync:
procedure TForm1.Button1Click(Sender: TObject);
var
SyncService: IFMXDialogServiceSync;
begin
if TPlatformServices.Current.SupportsPlatformService(IFMXDialogServiceSync, IInterface(SyncService)) then
begin
SyncService.ShowMessageSync(‘This is ShowMessage Sync’);
end;
end;
How to use the FireMonkey FMX “MessageDialog” version of a dialog
As explained above, to use MessageDialog you need to specify:
- The message (text of the dialog)
- The dialog type (mtWarning, mtError, mtInformation, mtConfirmation, mtCustom)
- The set of buttons
- The default button will have the initial focus when the dialog is shown
- The help context (0 if no help is used in the app)
For Async MessageDialog, you need, as an additional parameter, an anonymous method to handle the dialog result like the following code snippet:
procedure(const AResult: TModalResult)
begin
case AResult of
mrYES:
Label1.Text := 'YES';
end;
end
But for Sync MessageDialog, the dialog result is returned as an integer from the dialog calling method.
This is how to call MessageDialog Async:
procedure TForm1.Button1Click(Sender: TObject);
var
AsyncService: IFMXDialogServiceAsync;
begin
if TPlatformServices.Current.SupportsPlatformService(IFMXDialogServiceAsync, IInterface(AsyncService)) then
begin
AsyncService.MessageDialogAsync('Question ?', TMsgDlgType.mtConfirmation, [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo], TMsgDlgBtn.mbNo, 0,
procedure(const AResult: TModalResult)
begin
case AResult of
mrYES:
Label1.Text := 'YES';
end;
end);
end;
end;
Here’s the code for calling MessageDialog Sync:
procedure TForm1.Button1Click(Sender: TObject);
var
SyncService: IFMXDialogServiceSync;
rValue: integer;
begin
if TPlatformServices.Current.SupportsPlatformService(IFMXDialogServiceSync, IInterface(SyncService)) then
begin
rValue := SyncService.MessageDialogSync('Question ?', TMsgDlgType.mtConfirmation, [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo], TMsgDlgBtn.mbNo, 0);
Label1.Text := rValue.ToString;
end;
end;
How to use the FireMonkey FMX “InputQuery” version of a dialog
To use InputQuery, you need to specify:
- The caption of the dialog box.
- The prompt of each input field (a declared variable of an array of strings), and if you want to mask a field as a password, add ‘#1’ before the prompt (like #1 + ‘Password’).
- The values of each input field: A declared variable of an array of strings. It can be filled at runtime (before calling the dialog) to serve as default values.
For Async InputQuery, you need, as an additional parameter, an anonymous method to handle dialog result (clicked button) and the user input values, like the following code snippet:
procedure (const AResult: TModalResult; const AValues: array of string)
begin
case AResult of
mrOk:
Label1.Text := AValues[0] + '/' + AValues[1];
end;
end
But for Sync InputQuery, the dialog calling function returns a boolean value (true if the user clicks the OK button). Then the user input values can be accessed through the Values variable.
The code for calling InputQuery Async:
procedure TForm1.Button1Click(Sender: TObject);
var
AsyncService: IFMXDialogServiceAsync;
caption, inData: array[0..1] of string;
begin
caption[0] := 'Name :';
caption[1] := #1 + 'Pass :'; // Masking function when #1 attached in front
// Setting initial values
inData[0] := 'admin';
inData[1] := '1234';
if TPlatformServices.Current.SupportsPlatformService(IFMXDialogServiceAsync, IInterface(AsyncService)) then
begin
AsyncService.InputQueryAsync('Input login credentials', caption, inData,
procedure (const AResult: TModalResult; const AValues: array of string)
begin
case AResult of
mrOk:
Label1.Text := AValues[0] + '/' + AValues[1];
end;
end);
end;
end;
The code for calling InputQuery Sync:
procedure TForm1.Button1Click(Sender: TObject);
var
SyncService: IFMXDialogServiceSync;
caption, inData: array[0..1] of string;
begin
caption[0] := 'Name :';
caption[1] := #1 + 'Pass :'; // Masking function when #1 attached in front
// Setting initial values
inData[0] := 'admin';
inData[1] := '1234';
if TPlatformServices.Current.SupportsPlatformService(IFMXDialogServiceSync, IInterface(SyncService)) then
begin
if SyncService.InputQuerySync('Input login credentials', caption, inData)
then
Label1.Text := inData[0] + '/' + inData[1];
end;
end;
That’s it! Are you ready to use the power of FireMonkey FMX dialogs in your apps?
Like any other component in the Delphi frameworks, knowing the mechanism and the basics behind native dialogs allows the developer to use them correctly and effectively in their projects.

If you want to try out projects of your own and you don’t have a copy of the latest version of RAD Studio with Delphi, you can go to the products page and download a free fully working trial version.
Reduce development time and get to market faster with RAD Studio, Delphi, or C++Builder.
Design. Code. Compile. Deploy.
Free Delphi Community Edition Free C++Builder Community Edition








if TPlatformServices.Current.SupportsPlatformService(
What to do if it is not supported? The message is probably important.
Generally, it is supported on all the platforms we target so it should not normally occur – however, if it is not supported for some reason the alternative is to either show some kind of on-screen label or if the error is serious enough then terminate the program appropriately, writing to the relevant debug log at the same time.
It could be but I think there’s something missing in the async approach.
Suppose the user has opted to delete very important files on mobile. I want to put up an async dialog that says, “Are you sure you want to wipe out all the data?”
There is nothing else my mobile app can/should do until it gets an answer to this question. What is my app capable of doing while this dialog is displayed? Does the anonymous method have to do the deleting of the files? (My app may have even more questions for the user based on their answer to this dialog.)
The way I’ve been handling this on mobile is creating a unique state for the app as it waits on the async dialog to complete. It then goes to a different state depending on how the dialog was handled (Yes, No, Cancel, etc.)
Yes, Async is a bit of conundrum – when to use it, when not to use it. More often than not waiting for an answer from the user to a question you have asked is going to be a blocking interaction – “well, should I do X or not?”
Manh mobile apps handle this by using a modal style of user interface where the question appears on a screen from which there is, in effect, no choice but to answer, or cancel/back. This takes the form of almost a mini-wizard visual paradigm where the path through the app is determined by the user’s choice. The idea of a message box is a little bit alien to mobile apps and often looks out of place when the choice is made to use one.
You might find ModalFrame from https://github.com/Zoomicon/Zoomicon.Media.FMX.Delphi useful
>Does the anonymous method have to do the deleting of the files?
Yes. You need to delete the files later, after the user clicks “Yes”. The problem is that since the program is not blocked by your message box, some other parts of the program might have already been deleted those files.
These are the questions that the article should have been answered. But the article is very very superficial. It treats the code, not the architecture 🙁