Home > Back-end >  Using a view model and calling OnAppearing in ContentView
Using a view model and calling OnAppearing in ContentView

Time:03-20

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.

  • Related