I'm using view models for my ContentPage
's in my Xamarin Forms 5 app and typically call an Init()
method in my view model from the OnAppearing()
method in code behind.
I tried the same approach in my ContentView
but it's never hitting the OnAppearing()
method.
This is my ContentView
code:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MyApp.ViewModels"
x:Class="MyApp.MyContentView">
<ContentView.BindingContext>
<vm:MyViewModel/>
</ContentView.BindingContext>
<ContentView.Content>
<StackLayout
BackgroundColor="{StaticResource PrimaryDark }"
HeightRequest="200">
<Label
Text="{Binding User.FullName}"
TextColor="White"
FontSize="Medium"
FontAttributes="Bold"
HorizontalOptions="CenterAndExpand"/>
</StackLayout>
</ContentView.Content>
</ContentView>
The view model for this content view looks like this:
public class MyViewModel : BaseViewModel
{
User user;
public MyViewModel()
{
}
public User User
{
get => user;
set
{
if (user == value)
return;
user = value;
OnPropertyChanged();
}
}
public async void Init()
{
// Get user info
var data = await _dbService.GetUser();
if(data != null)
{
User = data;
OnPropertyChanged(nameof(User));
}
}
}
And in my code behind, this is what I'm doing:
public partial class MyContentView : ContentView
{
MyViewModel _vm;
public MyContentView()
{
InitializeComponent();
_vm = new MyViewModel();
BindingContext = _vm;
}
protected virtual void OnAppearing()
{
_vm.Init();
}
}
This pattern is working nicely in my content pages but not working in a content view. What am I doing wrong here?
CodePudding user response:
The content view doesn't have the lifecycle methods like the content page. So when the content view shows or displays on the screen, the OnAppearing() and OnDisAppearing method developer custom will not invoke.
So you can call the the page's OnAppearing() method to do that if there is only a content view in your page. And if there is not only one contentview, you can call the _vm.Init();
method when you use the instance of the content view.
CodePudding user response:
Here's what I've done and it seems to be working fine.
First, I created a ContentView
to display the flyout header which includes user's avatar and name. Notice that I set the view model for this content view in the XAML file -- see below:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:vm="clr-namespace:MyApp.ViewModels"
x:Class="MyApp.Views.FlyoutHeader">
<ContentView.BindingContext>
<vm:AppViewModel/>
</ContentView.BindingContext>
<ContentView.Content>
<StackLayout
BackgroundColor="{StaticResource PrimaryDark }"
HeightRequest="200">
<xct:AvatarView
Source="{Binding UserInfo.AvatarUrl}"
Size="100"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"/>
<Label
Text="{Binding UserInfo.FullName}"
TextColor="White"
FontSize="Medium"
FontAttributes="Bold"
HorizontalOptions="CenterAndExpand"
Margin="0,0,0,30"/>
</StackLayout>
</ContentView.Content>
</ContentView>
I then created a view model named AppViewModel
that I intend to use in multiple places, including the FlyoutHeader.xaml
that I shared above. Here's what AppViewModel
looks like:
public class AppViewModel : BaseViewModel
{
User user { get; set; }
public AppViewModel()
{
}
public User UserInfo
{
get => user;
set
{
if (user == value)
return;
user = value;
OnPropertyChanged();
}
}
public async void Init()
{
if(user == null || user.Id == Guid.Empty)
{
var data = await _dbService.GetUser();
if(data != null)
{
UserInfo = data;
OnPropertyChanged();
}
}
}
}
Finally, in the code behind for FlyoutHeader.xaml.cs
, I call the Init()
method of the view model in the constructor:
public partial class FlyoutHeader : ContentView
{
AppViewModel _vm;
public FlyoutHeader()
{
InitializeComponent();
_vm = new AppViewModel();
_vm.Init();
BindingContext = _vm;
}
}
I'm actually a bit concerned that there maybe tight coupling with the UI and the async
call being initiated in the constructor may tie up the UI thread and delay it. Please let me know if there's a better way to handle this.