Archive
HowTo: show a confirmation dialog with Delphi FireMonkey UI library
Here’s my answer at https://stackoverflow.com/a/71728787/903783 on how to show a confirmation dialog with Delphi’s cross-platform FireMonkey UI library.
Based on other answers, I’ve added a “Confirm” class function (adapted to return a Boolean) to TApplicationHelper (which also has an untested function to get the executable filename), currently maintained under the READCOM_App repository.
Update: had to modify the answer to use a callback so that it also works on platforms where FMX supports only asynchronous dialogs, like Android
unit Zoomicon.Helpers.FMX.Forms.ApplicationHelper;
interface
uses
FMX.Forms; //for TApplication
type
TApplicationHelper = class helper for TApplication
public
function ExeName: String;
class function Confirm(Prompt: String): Boolean;
end;
implementation
uses
{$IFDEF ANDROID}
System.SysUtils, //TODO: is this needed?
Androidapi.Helpers, //for TAndroidHelper, JStringToString
Androidapi.Jni.JavaTypes, //to avoid "H2443 Inline function 'JStringToString' has not been expanded"
{$ENDIF}
System.UITypes, //for TMsgDlgType
FMX.DialogService, //for TDialogService
FMX.Dialogs; //for mbYesNo
{$region 'TApplicationHelper'}
//from https://gist.github.com/freeonterminate/2f7b2e29e40fa30ed3c4
function TApplicationHelper.ExeName: String;
begin
Result := ParamStr(0);
{$IFDEF ANDROID}
if (Result.IsEmpty) then
Result := JStringToString(TAndroidHelper.Context.getPackageCodePath);
{$ENDIF}
end;
//based on https://stackoverflow.com/questions/42852945/delphi-correctly-displaying-a-message-dialog-in-firemonkey-and-returning-the-m
class procedure TApplicationHelper.Confirm(const Prompt: String; const SetConfirmationResult: TProc<Boolean>);
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.
usage example is in the MainForm of READCOM_App
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
);
end;
where MSG_CONFIRM_CLEAR_STORY is declared as
resourcestring
MSG_CONFIRM_CLEAR_STORY = 'Clearing story: are you sure?';
Gotcha: var x = x() in Javascript gives “Object Expected” error
At ClipFlair Studio (a Silverlight app), I had some time ago implemented a confirmation warning upon user trying to close the webpage (when it was running inside the web browser), which then had stopped functioning. It seems at some refactoring I had added code like the following:
var activityView = activityView();
and it was failing with error “Object Expected” (and even worse this was done silently – had to use browser’s debugging tools to catch it – because the respective code was running at onbeforeunload browser window event).
Such code would be fine in C#, but in Javascript it seems that a local variable with name x hides a function named x (with any number of parameters in its definition). This is obviously because functions are first-class objects in Javascript and can be treated like variables themselves too.
All started working again fine after changing that code to:
var a = activityView();
Below is the respective block of Javascript code included in a script tag at the webpage that hosts the Silverlight control. Note the “control.content.activityWindow”, where “activityWindow” is accessed via the Silverlight HTMLBridge (that object – that has to be marked with ScriptableType class attribute – is registered using that key via HtmlPage.RegisterScriptableObject at the Silverlight app side).
function silverlightControl() { return document.getElementById("silverlightControl"); } function onSilverlightLoad(sender, args) { var control = silverlightControl(); if (control != null) control.focus(); } function activityWindow() { var control = silverlightControl(); if ( (control != null) && (control.content != null) ) return control.content.activityWindow; else return null; //need this so that it doesn't return undefined } function activityView() { var a = activityWindow(); if (a != null) return a.GetView(); else return null; //need this so that it doesn't return undefined } function onClosing() { var a = activityView(); if ( (a != null) && (a.WarnOnClosing) ) return "Do you want to exit ClipFlair Studio?";
//else return undefined is implied (no onClosing message that is) } function onClosed() { var a = activityView(); if (a != null) a.WarnOnClosing = false; } function installEventHandlers() { window.onbeforeunload = onClosing; window.onunload = onClosed; } installEventHandlers();