How do I create an alert popup from my ViewModel in Xamarin?

Viewed 8787

I'm developing my Xamarin app with the MVVM pattern. I want to display an alert to the user when the user presses a button.

I declare my ViewModel with

class MainPageViewModel : BindableBase   {

Unfortunately, I'm not able to access a Page object from within the ViewModel directly. How do I best go about displaying my alert?

6 Answers

Late to the party but as Nick Turner has mentioned in a number of comments, the solutions given so far require the view model to reference the view which is an antipattern/infringement of MVVM. Additionally, you'll get errors in your view model unit tests such as: You MUST call Xamarin.Forms.Init(); prior to using it.

Instead you can create an interface that contains the code for your alert boxes and then use in your view model as follows:

Interface:

public interface IDialogService
{ 
    Task ShowAlertAsync(string message, string title, string buttonLabel); 
}

Implementation (Using ACR.UserDialogs NuGet package):

public class DialogService : IDialogService
{
    public async Task ShowAlertAsync(string message, string title, string buttonLabel)
    {
        if (App.IsActive)
            await UserDialogs.Instance.AlertAsync(message, title, buttonLabel);            
        else
        {
            MessagingCenter.Instance.Subscribe<object>(this, MessageKeys.AppIsActive, async (obj) =>
            {
                await UserDialogs.Instance.AlertAsync(message, title, buttonLabel);
                MessagingCenter.Instance.Unsubscribe<object>(this, MessageKeys.AppIsActive);
            });
        }
    }
}

ViewModel:

public class TestViewModel
{    
    private readonly IDialogService _dialogService;

    public TestViewModel(IDialogService dialogService)
    {
         //IoC handles _dialogService implementation
         _dialogService = dialogService ?? throw new ArgumentNullException(nameof(dialogService));
    }

    public ICommand TestCommand => new Command(async () => await TestAsync());

    private async Task TestAsync()
    {
        await _dialogService.ShowAlertAsync("The message alert will show", "The title of the alert", "The label of the button");
    }
}

TestCommand can then be bound to the button in the your xaml:

<Button x:Name="testButton" Command="{Binding TestCommand}">

You can call the below code within the view model, if you are using normal MVVM pattern.

App.current.MainPage.DisplayAlert("","","");

To display Alert write below code in your ViewModel class

public class MainViewModel
{
    public ICommand ShowAlertCommand { get; set; }
    public MainViewModel()
    {
        ShowAlertCommand = new Command(get => MakeAlter());
    }
    void MakeAlter()
    {
        Application.Current.MainPage.DisplayAlert("Alert", "Hello", "Cancel", "ok");
    }
}

Set your Command to Button in xaml

<StackLayout>
<Button Text="Click for alert" Command="{Binding ShowAlertCommand}"
   HorizontalOptions="Center"
   VerticalOptions="CenterAndExpand" />
</StackLayout>

Set BindingContext in code behind of your xaml file. If you xaml file MainPage.xaml

public MainPage()
{
    InitializeComponent();
    BindingContext = new MainViewModel();

}

You can use Prism's PageDialogService which keeps your ViewModels very clean and testable.

For Shell application I was able to achieve like this

await Shell.Current.DisplayAlert("Title", "Message", "Cancel");

Would it be a good idea to create popups like this? (called from my ViewModel)

private void OpenPopUp()
{
    Application.Current.MainPage.Navigation.ShowPopup(new CustomPopUp());
}

You can find a guide on how to create custom pupups here: https://www.youtube.com/watch?v=DkQbTarAE18

Works pretty good for me, but I am very new to Xamarin.

Related