Alan2
Alan2

Reputation: 24602

Pass OnAppearing to a ViewModel in Xamarin Forms MVVM?

I have business logic that loops around and does waits and other things. Currently this is in the code behind.

From what I have been able to read this is the wrong place and I should be placing this in the viewModel (correct me if wrong). If that's the case then should I have an OnAppearing method in my VM and if so how should I pass the OnAppearing to the View Model?

Currently my page OnAppearing looks like this:

    protected async override void OnAppearing()
    {
        base.OnAppearing();
        Title = Settings.mode.Text() + " Deck";
        vm.LearnViewVisible = Settings.mode.IsLearn();
        vm.PracticeViewVisible = Settings.mode.IsPractice();
        vm.QuizViewVisible = Settings.mode.IsQuiz();
        vm.QuizStartViewVisible = false;

If I am to be moving most of the business logic to the ViewModel then would that mean that all of this would move to an OnAppearing() method I create in the ViewModel?

Upvotes: 10

Views: 11058

Answers (4)

Andy Dent
Andy Dent

Reputation: 17981

I prefer a pattern I first encountered in some Realm sample code.

A ViewModel base provides empty overrideable OnAppearing/Disappearing


    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string name = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

        protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
        {
            if (Equals(field, value))
            {
                return false;
            }

            field = value;

            OnPropertyChanged(propertyName);
            return true;
        }

        internal virtual void OnAppearing() { }

        internal virtual void OnDisappearing() { }
    }

User classes descend from a base that conditionally invokes the VM.

    public class BasePage : ContentPage
    {
        protected override void OnAppearing()
        {
            base.OnAppearing();
            (BindingContext as BaseViewModel)?.OnAppearing();
        }

        protected override void OnDisappearing()
        {
            base.OnDisappearing();
            (BindingContext as BaseViewModel)?.OnDisappearing();
        }
    }

// used as
public class JournalEntryDetailsViewModel : BaseViewModel

Warning: if you change the base class like this you need to use it in the XAML - use a scoped version of BasePage instead of the <ContentPage top element.

Otherwise you will get an error [CS0263] Partial declarations of 'JournalEntriesPage' must not specify different base classes

<?xml version="1.0" encoding="UTF-8"?>
<v:BasePage xmlns="http://xamarin.com/schemas/2014/forms" 
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:v="clr-namespace:QuickJournal.Views"
        x:Class="QuickJournal.Views.JournalEntriesPage"
        Title="Journal"
        x:Name="page">
    <ContentPage.ToolbarItems>
        <ToolbarItem Text="Add" Command="{Binding AddEntryCommand}" />
    </ContentPage.ToolbarItems>
    <ContentPage.Content>

Upvotes: 1

Diego Ven&#226;ncio
Diego Ven&#226;ncio

Reputation: 6037

Other way is using Behaviors.Forms from David Britch

    ...
    <ContentPage.Behaviors>
        <behaviors:EventHandlerBehavior EventName="Appearing">
            <behaviors:InvokeCommandAction Command="{Binding PageAppearingCommand}" />
        </behaviors:EventHandlerBehavior>
        <behaviors:EventHandlerBehavior EventName="Disappearing">
            <behaviors:InvokeCommandAction Command="{Binding PageDisappearingCommand}" />
        </behaviors:EventHandlerBehavior>
    </ContentPage.Behaviors>
    ...

Original

Or Xamarin Community Toolkit EventToCommandBehavior

    <ContentPage.Behaviors>
            <xct:EventToCommandBehavior
                EventName="Appearing"
                Command="{Binding PageAppearingCommand}" />
            <xct:EventToCommandBehavior
                EventName="Disappearing"
                Command="{Binding PageDisappearingCommand}" />
    </ContentPage.Behaviors>

Related Question: EventHandlerBehavior vs EventToCommandBehavior

Upvotes: 16

SchindlerFlorian
SchindlerFlorian

Reputation: 21

This is how i link my Viewmodel. I would recommend setting up a ViewModelBase with : VModelActive and VModelInactive

Code Behind:

public partial class YourClass : ContentPage
    {
        ViewModelClass viewModelClass;
        public YourClass()
        {
            InitializeComponent();
            viewModelClass = new ViewModelClass();
            this.BindingContext = viewModelClass;                
        }

        protected override void OnAppearing()
        {
            base.OnAppearing();
            viewModelClass.VModelActive(this, EventArgs.Empty);
        }

        protected override void OnDisappearing()
        {
            base.OnDisappearing();
            viewModelClass.VModelInactive(this, EventArgs.Empty);
        }
    }

View Model

public override void VModelActive(Page sender, EventArgs eventArgs)
        {
            base.VModelActive(sender, eventArgs);
            //your code 
        }

public override void VModelInactive(Page sender, EventArgs eventArgs)
        {
            base.VModelInactive(sender, eventArgs);
            //your code
        }

Upvotes: 2

valentasm
valentasm

Reputation: 2442

Here is example from my solution

public partial class TaskDetailsPage : MvvmContentPage
{
    private readonly TaskDetailsViewModel _model;

    public TaskDetailsPage()
    {
        InitializeComponent();
        Shell.SetNavBarIsVisible(this, true);
        Shell.SetTabBarIsVisible(this, false);
        _model = BindingContext as TaskDetailsViewModel;
    }

    protected override string NavigationRoute => UniqeCodes.Routes.TaskDetailsPage;

    

    protected override void OnAppearing()
    {
        _model.Init();
    }
}

Upvotes: -1

Related Questions