Mimicking MessageDlg on mobile platforms

Posted by on in Blogs

This blog post will describe one of the ways to mimic the behaviour of a MessageDlg, or any other modal form for that matter, in Delphi when running on a mobile device.

Mobile devices, or more correctly the OS running on the mobile device won't allow blocking code, so the traditional Delphi approach of reading a result from a MessageDlg before continuing won't work.

There different ways of getting the desired behaviour, and this describes one way.

Start a new Firemonkey application, drop a button to call up the dialog, and an editbox, that we can display the user's choice in (I have chosen the Android Style, the same of course applies to iOS).

 Main Form

 

Now create a new form, name it frmDlg and follow these steps:

1.       Drop a TLayout on the form, align to Client, name it layoutDlg

2.       Drop a TRectangle on the Layout, align to Client, name it recBackground

3.       Set the recBackground.Fill.Color to black

4.       Set the recBackground.Opacity to 0.5

5.       Set the recBackground.Sides to false (all of the four sides)

 

Your form now looks very boring

Modal Form

Continue to follow the steps

1.       Drop another TRectangle on the form, name it recFront and set the alignment to VertCenter

2.       Set recFront.Margins.Left and Right to 30

3.       Remove the sides, as you did with recBack

4.       Set recFront.XRadius and YRadius to 6

 

Your dialog mimic is starting to take shape, and should look something like

 

Note that both rectangles have the Layout as parent.

StructureV1

Moving on, there are a few more steps.

1.       On recFront drop a TShadowEffect, shadowDlg

2.       Set the ShadowDlg.Distance to 5

3.       Set the ShadowDlg.Softness to 0.2

 

Make sure the shadow is on the recFront (recFront is parent)

StructureV2

Let’s make the dialog rectangle just a little better

1.       Set the recFront.Fill.Kind to Gradient

2.       Double-Click the recFront.Fill.Gradient property to get the property editor running

 

 dd  BrushDesigner

 

Cl   click the left dot of the two dots at the bottom of the disgner

        Brush

          Set its RGB values as shown in the picture

2.       Click the right one and set its to 255 (all of them), this is white

Now we just need a few components on the recFront and the GUI is done

3.       Drop a TLabel named lblMessage on the recFront, recFront is parent

4.       lblMessage.Align should be Top

5.       lblMessage.Margins.Top is 30, left and right are 10

6.       lblMessage.TextSettings.HorzAlign is Center

7.       lblMessage.TextSettings.WordWrap should be true

8.       lblMessage.Height is 60

9.       Drop a TLayout on recFront, name it layoutBottom (recFront is parent)

10.   layoutBottom.align is set to Bottom

11.   On layoutBottom drop two buttons, named btnOK and btnCancel

12.   btnOK.align is left, margins.left set to 5, and Text is ‘OK’

13.   btnCancel.align is right, margins.right set to 5, and Text is ‘Cancel’

      You should now have a form looking similar to my form below

      Dialog

     With the structure pane reflecting the parenting of the components

       Struct3

      Now we need to implement some code on this dialog. The object is to

1.       Display the dialog

2.       Having a text in the label

3.       And detect if the user clicked Ok or Cancel

To do so, I want the calling code to be as simple as possible, now we cannot have blocking code, so we cannot have a function waiting to return the user choice back to the main form. Let’s start with just displaying the form, adding the other bullets later.

Remember you put everything on layoutDlg, well there was a reason for that. It is possible to move a layout from one form to another, so that’s what we are going to do in the procedure that starts the dialog.

Procedure TfrmDlg.ShowDialog(aParent : TFMXObject; aText : string);

begin

  //First we set the text in the label

  lblMessage.Text := aText;

  //Then we move the entire Layout to the form (well, any FMXObject)

  layoutDlg.Parent := aParent;

  //The following lines shouldnt be necessary

  //I have however had one occasion where they were

  //I havent been able to reproduce, but they wont hurt

  recBackground.BringToFront;

  recFront.BringToFront;

end;

 So we create a procedure on the form with the dialog as I did above, remember to make it a public procedure.

To call this procedure from the main form, just use the dlgForm, and call it on the buttons OnClick eventhandler

procedure TForm1.Button1Click(Sender: TObject);

begin

   frmDlg.ShowDialog(self, 'Press ok or cancel');

end;

And this will bring our dialog up very nicely, with the text in the label. Now we need to react to the two buttons OnClick Events, but we of course need to do this from the main form.

To do so, there are different ways. We need to have a couple of events on the dialog. In the private section of the "modal" form define two events of type TNotifyEvent

 

private

    FOnOkBtnClick: TNotifyEvent;

    FOnCancelBtnClick: TNotifyEvent;

Expand your procedure to

 

Procedure TfrmDlg.ShowDialog(aParent : TFMXObject; aText : string; aOKClick, aCancelClick : TNotifyEvent);

//Procedure TfrmDlg.ShowDialog(aParent : TFMXObject; aText : string);

begin

  //First we set the text in the label

  lblMessage.Text := aText;

  //Then we move the entire Layout to the form (well, any FMXObject)

  layoutDlg.Parent := aParent;

  //The following lines shouldnt be necessary

  //I have however had one occasion where they were

  //I havent been able to reproduce, but they wont hurt

  recBackground.BringToFront;

  recFront.BringToFront;

  self.FOnOkBtnClick := aOKClick;

  self.FOnCancelBtnClick := aCancelClick;

end;

 

And on the OK, and cancel buttons OnClick Eventhandlers write the following code

 

procedure TfrmDlg.btnCancelClick(Sender: TObject);

begin

  //Set the parent back to self

  self.layoutDlg.Parent := self;

  //If the event is assigned, call it

  if assigned(self.FOnCancelBtnClick) then

    self.FOnCancelBtnClick(self);

end;

 

procedure TfrmDlg.btnOKClick(Sender: TObject);

begin

  //Set the parent back to self

  self.layoutDlg.Parent := self;

  //If the event is assigned, call it

  if assigned(self.FOnOkBtnClick) then

    self.FOnOkBtnClick(self);

end;

 

You could also have public properties for those events, in which case your public section of the dialog form would appear as

 

public

    { Public declarations }

    Procedure ShowDialog(aParent : TFMXObject; aText : string; aOKClick, aCancelClick : TNotifyEvent);

    property OnOkBtnClick : TNotifyEvent read FOnOkBtnClick write SetOnOkBtnClick;

    property OnCancelBtnClick : TNotifyEvent read FOnCancelBtnClick write SetOnCancelBtnClick;

 

Now, to call the dialog from your main form, you pass in two procedures with the signature matching a TNotifyEvent: Procedure Form1.procedureName(sender : TObject); I have defined in my example these two

 

procedure TForm1.DlgCancel(sender : TObject);

begin

  Edit1.Text := 'Cancel was clicked';

end;

 

procedure TForm1.DlgOK(sender : TObject);

begin

  Edit1.Text := 'Ok was clicked';

end;

 

Now, when you on the main forms buttons OnClick eventhandler have this

 

procedure TForm1.Button1Click(Sender: TObject);

begin

   frmDlg.ShowDialog(self, 'Press ok or cancel', dlgOK, dlgCancel);

end;

 

 

Your dialog appears nicely

MainDialog

 

 



Comments

  • SilverKnight
    SilverKnight Tuesday, 30 January 2018

    I have the same problem with the screen shots not showing - no amount of refresh makes them appear --- in FireFox Quantum - works perfect from Chrome browser. Try using a different browser to see the screen shots.

  • Jens Fudge
    Jens Fudge Wednesday, 31 January 2018

    I normally use Safari on a Mac, when I use a Windows machine, I normally use Chrome. On iPhone I also use Safari. On all three of these it works. I may have to hit refresh, but after that it works.

  • SilverKnight
    SilverKnight Tuesday, 30 January 2018

    Appears that this is due to the Image URLs not being "secure" - e.g. they are not referenced via HTTPS
    Just didn't notice the little warning icon in FF - clicking on the warning shows "part of this page is not secure (such as images)" -- looking at the source in chrome shows src="/uploads/644/Main.png" for "Main form" - so it doesn't specifically reference though https and appears to hit the root of the site for reference. Must be a bug in the site software.

  • Jens Fudge
    Jens Fudge Wednesday, 31 January 2018

    It could very well be a bug in the site software

  • Paul @ JWare
    Paul @ JWare Friday, 26 January 2018

    Good article. This is also a perfect place to use animations to, well animate the "floating" dialog box. For example, by using a float animation to you could drop the rectangle down from the top of the screen and then animate it back up after the user responds. This is not exact code but I think the point is made. I've done it before on Android and it's pretty cool.
    // Drop down
    recFront->Visible = true;
    DLGFAnimation->StartValue = (this->Height / 3) - (recFront->Height / 2);
    DLGFAnimation->StopValue = -recFront->Height;
    DLGFAnimation->Start();

    // Pull up
    recFront->Visible = true;
    DLGFAnimation->StartValue = -recFront->Height;
    DLGFAnimation->StopValue = (this->Height / 3) - (recFront->Height / 2);

    DLGFAnimation->Start();

  • Jens Fudge
    Jens Fudge Saturday, 27 January 2018

    That's a very good point Paul. I have indeed used a similar way to animate the dialog into place on both Android and iOS.
    You can also make the dialog bubble up, spin in or any other ways of animation. It also looks cool if you use a bouncing animation.

    Thank you very much for your comment :-)

  • aBarba
    aBarba Friday, 26 January 2018

    Well, I do all as described. But when I click on OK or Cancel button nothing appears on Label in
    Main form. I have senn this:
    property OnOkBtnClick : TNotifyEvent read FOnOkBtnClick write SetOnOkBtnClick;
    property OnCancelBtnClick : TNotifyEvent read FOnCancelBtnClick write SetOnCancelBtnClick;
    write fields are not used anywhere???
    Please, send me example source if possiblle.
    Thanks.

  • Jens Fudge
    Jens Fudge Saturday, 27 January 2018

    Hi aBarba.
    defining the fields as properties was meant to be an option in case you prefer that way. You are correct that in this example I do not use the properties.
    However, I do pass in the TNotifyEvents in the calling code in ShowDialog.
    So my guess is, that you are missing either on the main form:

    procedure TForm1.DlgCancel(sender : TObject);
    begin
    Edit1.Text := 'Cancel was clicked';
    end;

    procedure TForm1.DlgOK(sender : TObject);
    begin
    Edit1.Text := 'Ok was clicked';
    end;

    OR in the dialog

    In the next step of the method ShowDialog I added the lines:
    self.FOnOkBtnClick := aOKClick;
    self.FOnCancelBtnClick := aCancelClick;

    And on the buttons OnClick method

    if assigned(self.FOnOkBtnClick) then
    self.FOnOkBtnClick(self);

    In any case, I am absolutely certain that the provided example works as expected, if you still cannot get it to work feel free to send me your project, and I'll take a look

  • Rebekah D46194
    Rebekah D46194 Thursday, 25 January 2018

    Hi Jens!
    We are all reading your blog post today as it was promoted in email! :)

    "FireMonkey" is misspelled and missing an "e". Delphi should also be spelled with uppercase "D". Can you update your post?

    There are a couple missing apostrophes "wont" == "won't" and "the users choice" == "the user's choice"

    Thanks for writing this post and sharing your expertise!

  • Jens Fudge
    Jens Fudge Thursday, 25 January 2018

    Hi Rebekah
    Thanks for your comments.
    I'll fix the errors you pointed out as soon as possible.
    I think I fixed the errors :-)

    You are welcome.
    Jens

  • John M55486
    John M55486 Thursday, 25 January 2018

    Yes, the fact that you can't have modal dialogs is a complete nightmare especially if you have many of them.

    Also, I can't see any of the pictures in the examples above.

  • Jens Fudge
    Jens Fudge Thursday, 25 January 2018

    Sorry you can't see the pictures. I think its the community server or framework not displaying them properly every time. It works for me at the moment. Maybe refresh will help?

  • Bruce Messer
    Bruce Messer Thursday, 25 January 2018

    Hi Jens

    The verb for mimic is mimicking!

    Bruce.

  • Jens Fudge
    Jens Fudge Thursday, 25 January 2018

    Hi Bruce, thanks for pointing that out :-)

  • Albert I56300
    Albert I56300 Wednesday, 10 January 2018

    So - thank for the article!

  • Jens Fudge
    Jens Fudge Thursday, 25 January 2018

    You're welcome

  • Albert I56300
    Albert I56300 Wednesday, 10 January 2018

    Unfortunately something similar has to be created also on macOS - on my MacMini at least - because ShowModal is broken and Embarcadero ignores it since a year now :-(

  • Jim McKeeth
    Jim McKeeth Thursday, 25 January 2018

    I just tested ShowModal on macOS and it works beautifully. I've not heard about any issues there. Is there an issue on Quality.Embarcadero.com that describes the trouble you are having? Maybe it is specific to the certain version of macOS you are running or something.

  • Albert I56300
    Albert I56300 Wednesday, 31 January 2018

    Hello Jim. I am ready to bet a beer that you are using a mac laptop :-)
    I did open an issue, in May 2017, and it finally got taken into account a few days ago - after my initial message here.
    As I explained in the issue, no problem on my MBA, only shows on my Mac Mini and also on the iMac of a friend. Same OS (Sierra, then High Sierra).
    But anyway, now that it's active on quality.embarcadero.com I am hopeful a solution will come, eventually.

  • Jens Fudge
    Jens Fudge Thursday, 25 January 2018

    That may be the case... I have no idea.

  • Please login first in order for you to submit comments

Check out more tips and tricks in this development video: