UI animation from your ViewModel

This post is about accessing your views code-behind from your viewModel and nothing much about animation sorry!

UI animation from your ViewModel

This post is all about accessing your views code-behind from your viewModel and nothing much about animation sorry!

I'm writing this because I needed to kick off an animation in my View page code-behind and I found that the usually preferred way (using Prisms PropertyChanged) didn't work that great for me.

What was I trying to do?

I'm still working with buttons like my previous blog post Circle button in Xamarin.Forms but now I'm doing custom animation like this image shows.

ButtonAnimation

Two ways to do this

There are at least two ways to talk between the view and view-model so here goes.

Method 1: Using PropertyChanged

Lets first take a look at how you can do this with PropertyChanged

The ViewModel

Add a property to your model that you want to subscribe to changes to in the View page.

 private bool? _isCorrectAnswer;
 public bool? IsCorrectAnswer
 {
    get { return _isCorrectAnswer; }
    set { SetProperty(ref _isCorrectAnswer, value); }
 }

Then change the value to the property somewhere in your code to

 IsCorrectAnswer = true;

The View

Then setup your view like this

 public partial class AnimationPage : ContentPage
 {
    public AnimationPageViewModel? ViewModel 
    => BindingContext as AnimationPageViewModel;

    public AnimationPage()
    {
        InitializeComponent();

        if (ViewModel != null)
        {
            ViewModel.PropertyChanged += VM_PropertyChanged;
        }
    }

    /// <summary>
    /// Called when there is a change to any property on the viewModel
    /// </summary>
    private void VM_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        // Did the property we are interested in change?
        if (e.PropertyName == "IsCorrectAnswer")
        {
            // Do some UI animation code 
            DoAnimationMethod(((MultiplyPageViewModel)sender)?.IsCorrectAnswer);
        }
    }
 }

And now the DoAnimationMethod() is always called when the IsCorrectAnswer in the view model changes.

But why didn't that work for me?

Because I need I basically need to reset this property for my logic to work. So either setting it back to default false or even changing it to a nullable string (string?) will trigger the PropertyChanged.

Maybe I could have changed my logic to make this work but I decided there was a better way in this instance to do this.

Method 2: Using a EventHandler

Here I'm going to use EventHandlers like I talked about in my blog post Events and this is the approach I ended on taking.

The ViewModel

Add this code to your ViewModel

 public event EventHandler<ChangeColorsArgs> ChangeButtonColorsEvent;

 private void ChangeButtonColors(bool isCorrectAnswer)
 {
    ChangeButtonColorsEvent?.Invoke(this, new ColorsArgs(isCorrectAnswer));
 }

and call the method ChangeButtonColors() from your code

 ChangeButtonColors(true);

EventArg

You need to create your own eventArgs to pass your parameters between so we do it like this

 public class ChangeColorsArgs : System.EventArgs
 {
    public bool IsCorrectAnswer { get; set; }

    public ChangeColorsArgs(bool correctAnswer)
    {
        IsCorrectAnswer = correctAnswer;
    }
 }

For more parameters just add them in there and to the constructor.

The View

Add this code to your view.

 public partial class ButtonAnimationPage : ContentPage
 {
    public AnimationPageViewModel? ViewModel 
    => BindingContext as AnimationPageViewModel;

    public AnimationPage()
    {
        InitializeComponent();

        if (ViewModel != null)
        {
            ViewModel.ChangeButtonColorsEvent += OnAnswerProvidedEvent;
        }
    }

    /// <summary>
    /// Called when event in viewModel is Invoked
    /// </summary>
    private async void OnAnswerProvidedEvent(object sender, ChangeColorsArgs e)
    {
        await DoAnimationMethod(e.IsCorrectAnswer);
    }
 }

And now the DoAnimationMethod() in your view gets called every time you call ChangeButtonColors(true) in your ViewModel.

Easy?

Please hit me up on Twitter if I'm missing something and I'll try to augment it.