How to Build a Native Xamarin.Forms Custom Modal Popup

in #utopian-io7 years ago (edited)

This tutorial will teach you how to call native custom Input dialogs in Xamarin.Forms

  • Create Custom Native Dialogs per platform with MVVM and ReactiveUI
  • Calling the Dialogs in the shared Xamarin.Forms application
  • Receive data from the dialogs.

Custom Popup

Requirements

Here are the nuget packages we will need.

  • Reactiveui Xamarin.Forms
  • Reactiveui Android Support
  • CrossCurrentActivity Plugin
  • Basic knowledge of MVVM with Reactiveui which you can get here

Difficulty

  • Intermediate

Let’s Dive In

Here is the repository in which the source code for this demo is found.

The app which we will be building is a simple application which will permit users to create to do items and mark them as completed, the creation of these todo items will be done with dialogs, and when they are created, the underlying view will be populated with the newly created item. Let’s dive into it.

We will call the popup from each platform. We will use Xamarin.Forms dependency service. So create this interface in the shared code.

public interface ICallDialog
    {
        Task CallDialog(object viewModel);
    }

Create a ViewModel for the main page. The part of the view model we are interested in is the command for calling the popup. Which is CreateCommand.

CreateTodoCommand = ReactiveCommand.Create(async () =>
            {
                //CAll the Dialog
                await DependencyService.Get<ICallDialog>().CallDialog(new CreateTodoViewModel());
            });

And also, we need to write the code to populate the Todo List. The list in the main view model, which is populated when an item is created on the modal popup. We subscribe and unsubscribe to the messages sent between the main view model and the modal popup’ s view model.

public void Initialize()
       {
           MessagingCenter.Subscribe<object, Todo>(this, $"ItemCreated", (s, todo) =>
           {
               Todos.Add(todo);
           });
       }

       public void Stop()
       {
           MessagingCenter.Unsubscribe<object, Todo>(this, $"ItemCreated");
       }

Let’s create the modal’s view model. This View model will contain the properties to which the popup’s controls will bind to. The command which is called when the todo is completed, is also found here
.
Here is the code for the todo view model.


public class CreateTodoViewModel : ReactiveObject
   {
       public ReactiveCommand CreateTodo { get; set; }
       private string _title;

       public string Title
       {
           get { return _title; }
           set {
               this.RaiseAndSetIfChanged(ref _title, value); }
       }

       public CreateTodoViewModel()
       {
           CreateTodo = ReactiveCommand.Create(() =>
           {
               MessagingCenter.Send<object, Todo>(this, $"ItemCreated", new Todo { Title = Title, IsDone = false});
           });
       }
   }

Let’s dive into the code to create the popups. We will do this for two platforms in this tutorial, which are UWP and Android.

Android Modal Popup
On android, to implement this with MVVM and ReactiveUI, install Reactiveui Android Support . Create a ReactiveDialogFragment this contains methods which we will leverage to bind the views to the view model’s properties we created earlier. This data binding is just a tricky one we will create using methods provided by the reactive dialog fragment. Here is the code to implement the layout for this dialog.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <EditText
            android:id="@+id/TitleEditText"
            android:layout_height="50dp"
            android:layout_gravity="center"
            android:layout_width="match_parent" />

  <LinearLayout
        android:layout_margin="10dp"
        android:orientation="horizontal"
        android:layout_gravity="center"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content">
    <Button
        android:layout_gravity="center"
        android:id="@+id/CatDoneButton"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content" />
    <Button
        android:layout_gravity="center"
        android:id="@+id/CatCancelButton"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content" />
  </LinearLayout>
</LinearLayout>

The code behind this dialog fragment contains mainly validation and binding views to the view model.

In the on create method of the dialog, we use the WhenAny method provided by the ReactiveDialogFragment to watch changes on the view and change the view model in consequence, that is some sort of data binding.

//Use WhenAny to create a kind of one way databining between view and viewmodel property
            this.WhenAny(x => x._titleEditText.Text, x => x.Value).Subscribe((val) =>
            {
                ViewModel.Title = val;
            });

We also make validation on the dialog, if the todo’s text is entered or not.

private async void DoneBtn_Click(object sender, EventArgs e)
       {
           if (!string.IsNullOrEmpty(_titleEditText.Text))
           {
               if (((ICommand)ViewModel.CreateTodo).CanExecute(null))
               {
                   ((ICommand)ViewModel.CreateTodo).Execute(null);
                   this.Dismiss();
               }
           }
           else
           {
               _titleEditText.SetError("Enter the title please", Resources.GetDrawable(Resource.Drawable.abc_ab_share_pack_mtrl_alpha));
           }
       }

Mean while, we implement the interface we defined earlier in the shared code. as follows.

[assembly: Xamarin.Forms.Dependency(
   typeof(CallDialog))]
namespace NativeCustomDialogs.Droid
{
    public class CallDialog : ICallDialog
    {
        async Task ICallDialog.CallDialog(object viewModel)
        {
            var activity = CrossCurrentActivity.Current.Activity as FormsAppCompatActivity;

            new CreateTodoDialog(viewModel as CreateTodoViewModel)
                        .Show(activity.SupportFragmentManager, "CreateTodoDialog");
        }
    }

UWP Modal Popup
On UWP, it is easier to do this since data binding here is naturally available.

Here is the layout created for this dialog.

<ContentDialog
    x:Class="NativeCustomDialogs.UWP.Dialogs.CreateTodoDialog"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:NativeCustomDialogs.UWP.Dialogs"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="New Todo"
    PrimaryButtonText="Ok"
    SecondaryButtonText="Cancel"
    PrimaryButtonClick="ContentDialog_PrimaryButtonClick"
    SecondaryButtonClick="ContentDialog_SecondaryButtonClick">

    <Grid>
        <TextBox Text="{Binding Title, Mode=TwoWay}" Name="TitleDialog"/>
    </Grid>
    
</ContentDialog>

Implementing the ICallDialog interface in UWP gives the following.

[assembly: Xamarin.Forms.Dependency(
   typeof(CallDialog))]
namespace NativeCustomDialogs.UWP
{
    public class CallDialog : ICallDialog
    {
        async Task ICallDialog.CallDialog(object viewModel)
        {
            await new CreateTodoDialog(viewModel as CreateTodoViewModel).ShowAsync();
        }
    }

We are done.
With this, running the sample on the platforms implemented should go smoothly. This is one solution to the problem of creating custom popups in Xamarin.Forms. There are other solutions to create modal popups in Xamarin.Forms. Some include using custom renderers and others include using tricks on Xaml layout to make the view feel like a modal popup. Just by searching on google, you could find these other solutions.But in my opinion, this method is the best.Since it makes call to native code. Also, the popups created link back to a view model in the shared code. Where data received is processed as it should be in a proper MVM application.

Don’t forget, here is the code for this tutorial, and if you think there is another better method for doing this, please share it in the comments. So that everyone reading this post can profit of it.

Sort:  

Hello Damien,
I am certain you realize that a prior submission, posted by you, and with similar content and idea to this had been rejected before.
Re-posting content with some modifications is not acceptable per Utopian Rules
Please make sure to only submit new and high quality content.
Thank you


Need help? Write a ticket on https://support.utopian.io.
Chat with us on Discord.

[utopian-moderator]

I modified this post in accordance to what you said on the previous one. I replied on comments you made on the previous post stating the modifications which I made, but you didn't reply.