Reputation: 24602
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
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
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>
...
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
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
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