Hello, friends. You might have noticed that when building Xamarin.Forms applications, you can only call extremely simple modal, popups, or dialog (these words will be used interchangeably). These popups are used only for very simple scenarios such as displaying information or allowing users to select a set of predefined options only. Where the need of building Xamarin.Forms Custom Modal Popups.
Other scenarios require more complex modals that allow the addition of entries, input views and other complex views. Xamarin.Forms does not provide any obvious way of doing these custom popup. At least not without additional external plugins, or tricks else not to my knowledge yet. Adding views to Xamarin.Forms pages is obvious and easy. But the Xamarin.Forms Alert does not provide a way to add views on it for customization.
This is not so ideal, since one may need to call custom popups to perform simple tasks. Like getting numbers, or text or any type of input from the user. And do so without much stress. What we will be doing in this tutorial is just that. Calling native custom modals and passing data back to the shared code while respecting the MVVM design pattern.
What we will be doing
To make everything short, we will go through all the steps to implement a custom native modal popup in xamarin.forms. And do so while respecting the MVVM pattern. Bellow is a bullet list of what will be done.
- Create Custom Native Dialogs per platform with MVVM and ReactiveUI
- Calling the Dialogs in the shared Xamarin.Forms application
- Receive data from the dialogs.
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
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.
1 2 3 4 | 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.
1 2 3 4 5 | 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.
1 2 3 4 5 6 7 8 9 10 11 12 | 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <?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.
1 2 3 4 5 | //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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <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.
1 2 3 4 5 6 7 8 9 10 11 | [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.
If you liked this post, or it was useful to you, please ? like it, share it on twitter, facebook or other social media… in case you want to get updated on any new useful post, follow me on twitter and like my page on facebook.
Follow me on social media and stay updated