Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to open custom dialog box / popup using Xamarin.Forms?

I am newbie to Xamarin.Forms and stuck with a situation where I want to open up a popup box with my control details [e.g. View Employee Details] on click of parent page.

How can I open custom dialog box / popup using Xamarin.Forms?

Any example code will be appreciated?

Thanks in advance!

like image 820
Nirav Mehta Avatar asked Sep 22 '14 10:09

Nirav Mehta


2 Answers

If you still want to have your popup's code in its own Page you can set up some custom renderers along the following logic.

1. A ModalPage & corresponding renderer

public class ModalPage : ContentPage { }

public class ModalPageRenderer : PageRenderer {
    protected override void OnElementChanged(VisualElementChangedEventArgs e)
    {
        base.OnElementChanged(e);
        this.View.BackgroundColor = UIColor.Clear;
        this.ModalPresentationStyle = UIModalPresentationStyle.OverCurrentContext;
    }

    public override void ViewDidLayoutSubviews()
    {
        base.ViewDidLayoutSubviews();
        SetElementSize (new Size (View.Bounds.Width, View.Bounds.Height));
    } 
}

2. HostPage

public class ModalHostPage : ContentPage, IModalHost
{
    #region IModalHost implementation

    public Task DisplayPageModal(Page page)
    {
        var displayEvent = DisplayPageModalRequested;

        Task completion = null;
        if (displayEvent != null)
        {
            var eventArgs = new DisplayPageModalRequestedEventArgs(page);
            displayEvent(this, eventArgs);
            completion = eventArgs.DisplayingPageTask;
        }

        // If there is no task, just create a new completed one
        return completion ?? Task.FromResult<object>(null);
    }

    #endregion

    public event EventHandler<DisplayPageModalRequestedEventArgs> DisplayPageModalRequested;

    public sealed class DisplayPageModalRequestedEventArgs : EventArgs
    {
        public Task DisplayingPageTask { get; set;}

        public Page PageToDisplay { get; }

        public DisplayPageModalRequestedEventArgs(Page modalPage)
        {
            PageToDisplay = modalPage;
        }
    }
}

3. HostPage renderer

public class ModalHostPageRenderer: PageRenderer
{
    protected override void OnElementChanged(VisualElementChangedEventArgs e)
    {
        base.OnElementChanged(e);

        if(e.OldElement as ModalHostPage != null)
        {
            var hostPage = (ModalHostPage)e.OldElement;
            hostPage.DisplayPageModalRequested -= OnDisplayPageModalRequested;
        }

        if (e.NewElement as ModalHostPage != null)
        {
            var hostPage = (ModalHostPage)e.NewElement;
            hostPage.DisplayPageModalRequested += OnDisplayPageModalRequested;
        }
    }

    void OnDisplayPageModalRequested(object sender, ModalHostPage.DisplayPageModalRequestedEventArgs e)
    {
        e.PageToDisplay.Parent = this.Element;
        var renderer = RendererFactory.GetRenderer (e.PageToDisplay);
        e.DisplayingPageTask = this.PresentViewControllerAsync(renderer.ViewController, true);
    }
}

Then it is as simple as calling await ModalHost.DisplayPageModal(new PopUpPage()); from your host page or in this particular case from the ViewModel behind.

What Pete said about PushModalAsync / PopModalAsync still remains valid for this solution too (which in my opinion is not a disadvantage), but your popup would appear with transparent background.

The main advantage of this approach, in my opinion, is that you can have your popup XAML/code definition separate from the host page and reuse it on any other page where you wish to show that popup.

like image 138
Gabor Avatar answered Nov 10 '22 05:11

Gabor


The general purpose of what you are trying to achieve can be accomplished by using the PushModalAsync and PopModalAsync methods of Xamarin.Forms Navigation object.

The chances are that this is good enough for what you are needing - However - this isn't truely modal. I will explain after a small code snippet:-

        StackLayout objStackLayout = new StackLayout()
        {
        };
        //
        Button cmdButton_LaunchModalPage = new Button();
        cmdButton_LaunchModalPage.Text = "Launch Modal Window";
        objStackLayout.Children.Add(cmdButton_LaunchModalPage);
        //
        cmdButton_LaunchModalPage.Clicked += (async (o2, e2) =>
        {
            ContentPage objModalPage = new ContentPage();
            objModalPage.Content = await CreatePageContent_Page2();
            //
            await Navigation.PushModalAsync(objModalPage);
            //
            // Code will get executed immediately here before the page is dismissed above.
        });
        //
        return objStackLayout;



    private async Task<StackLayout> CreatePageContent_Page2()
    {
        StackLayout objStackLayout = new StackLayout()
        {
        };
        //
        Button cmdButton_CloseModalPage = new Button();
        cmdButton_CloseModalPage.Text = "Close";
        objStackLayout.Children.Add(cmdButton_CloseModalPage);
        //
        cmdButton_CloseModalPage.Clicked += ((o2, e2) =>
        {
            this.Navigation.PopModalAsync();
        });
        //
        return objStackLayout;
    }

The problem with the above is that the

            await Navigation.PushModalAsync(objModalPage);

will immediately return after the animation.

Although you can't interact with the previous page, as we are displaying a new NavigationPage with a Close button shown - the parent Navigation Page is still executing behind the scenes in parallel.

So if you had any timers or anything executing these still would get called unless you stopped those.

You could also use the TaskCompletionSource approach as outlined in the following post also How can I await modal form dismissal using Xamarin.Forms?.

Note - that although you can now await the 2nd page displaying and then when that page is dismissed allowing code execution to continue on the next line - this is still not truely a modal form. Again timers or anything executing still will get called on the parent page.

Update 1:-

To have the content appear over the top of existing content then simply include it on the current page, however make this section of content invisible until you need it.

If you use an outer container such like a Grid that supports multiple child controls in the same cell, then you will be able to achieve what you want.

You will also want to use something like a filled Box with transparency that will cover the entire page also, to control the visible, see through section, that surrounds your inner content section.

like image 3
Pete Avatar answered Nov 10 '22 05:11

Pete