MVVM Ping Pong

Here’s a problem I come across more often than I would have expected.  I want my buttons to use Commands in the View Model, but in response to the command, I have to interact with the user in a way that is much easier to do in the code behind.

Let’s take the example of a user pressing a button to delete a record.  The button calls a command in the ViewModel, but I want to show a confirmation dialog box in the view.  If the user affirms the deletion, I want to do the work back in the View Model.  Oy.

I could use MessageCenter but it makes for fragile code that is hard to debug.  Instead, I’ll use an event that is fired in the ViewModel and handled in the View, and I’ll call methods in the ViewModel directly from the View.

What follows is a stripped to its essentials example.

Let’s start with the main page, which will display the Discard button and, when we have discarded the record, it will display a confirmation.

<StackLayout>
<Button Text="Delete" Command="{Binding DeleteCommand}"
CommandParameter="{Binding Id}" />

<Label Text="{Binding Output}" />

</StackLayout>

Nothing very surprising, the button calls a command and passes the record Id as a parameter.

We create the command in the ViewModel

   public ICommand DeleteCommand { get; set; }

and register it in the ViewModel’s constructor

public MainPageViewModel()
{
   DeleteCommand = new Command<int>(HandleDelete);
}

As you can see, the command parameter is bound to a property, and for this exercise, we just set that value to 2.

      public int Id { get; set; } = 2;

Now, when the button is pressed, the command will fire, and will call HandleDelete, passing in the (int) parameter.  At that point, the ViewModel wants the View to display a confirmation dialog box.  To make that happen, we’ll declare an event in the ViewModel and fire that event in the HandleDelete method

      public event Action DeleteEvent;

      public void HandleDelete(int Id)      
      {
         DeleteEvent?.Invoke(Id);
      }

In our code-behind, we’ll register for that event in the OnAppearing method, which means that the DeleteEvent will not be null and thus will be invoked, and handled in the event handler in the code behind. Here is OnAppearing

      protected override void OnAppearing()
      {
         base.OnAppearing();
         vm = new MainPageViewModel();
         BindingContext = vm;
         vm.DeleteEvent += DeleteEventHandler;
      }

The event handler pops up the dialog box. If the user confirms, then we want to call a method in the ViewModel to do the actual deletion.

      private async void DeleteEventHandler(int Id)
      {
            var answer = await DisplayAlert(
               "Delete Record?", 
               $"Do you want to delete record {Id}?", 
               "Yes", 
               "No");
         if (answer==true)
         {
            vm.DeleteRecord(Id);
         }
      }

That’s it.  We have a nice distribution of responsibility with the logic in the ViewModel and the dialog box in the View’s code-behind.

You can find the essential files here

Special thanks to Rodrigo Juarez

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen Pluralsight & LinkedIn Learning courses. He was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Xamarin Certified Mobile Developer and a Xamarin MVP and a Microsoft MVP.
This entry was posted in MVVM, Xamarin and tagged , . Bookmark the permalink.

8 Responses to MVVM Ping Pong

Comments are closed.