Load a filtered class in Xamarin - issue for UWP app MVVM

Daniele Pinai 21 Reputation points
2020-11-21T08:03:25.343+00:00

0

I use MVVM and i had binded my class with ObservableCollection to myPage in XAML like this

xmlns:vm="clr-namespace:Goal.Funzioni"
...
<ContentPage.BindingContext>
<vm:Azioni_Righe></vm:Azioni_Righe>
</ContentPage.BindingContext>
This approch load all items in the class without any filter.

If in my OnAppearing() Page's Event i call my ViewModel Filter all work good in Android. In UWP i haven't an error but my page became all white. I don't see any control, any line, nothing !! Only BackButton of the navigazionPage. I have the same issue if i set the ItemSources to null.

Is seems a war XAML BindingContext versus OnApparing() ItemsSource changing !

How can i resolve it ? Is possible for UWP filter ( change ItemsSource) in a OnAppearing() Event ? OR... Is possible to link the Page BindingContext to a Filtered ViewModel Source ?

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,298 questions
Universal Windows Platform (UWP)
0 comments No comments
{count} votes

Accepted answer
  1. Lance 291 Reputation points MVP
    2020-11-21T13:16:48.14+00:00

    I suspect a combination of the two reason is to blame:

    • You have accidentally replaced the instance of the ObservableCollection without calling OnPropertyChanged
    • You are using hot reload with a XAML-instantiated BindingContext

    I highly discourage you from setting BindingContext in XAML like that. This is because Xamarin will reload the the XAML and you can end up with duplicates of the BindingContext. Please visit https://devblogs.microsoft.com/xamarin/tips-tricks-xaml-hot-reload/ and read the Making the Most of MVVM section.

    Fixing Things Up

    Please take the following steps so that you can start on solid ground and eliminate basic problems.

    Step 1. Delete this

    <ContentPage.BindingContext>
        <vm:Azioni_Righe></vm:Azioni_Righe>
    </ContentPage.BindingContext>
    

    Step 2. Set the BindingContext after InitializeComponent() in the page's constructor, like this

    using Xamarin.Forms;
    
    namespace Goal.Funzioni
    {
        public partial class YourPage : ContentPage
        {
            public YourPage()
            {
                InitializeComponent();
    
                // Set the BindingContext here
                BindingContext = new Azioni_Righe();
            }
        }
    }
    

    This does not break MVVM. It just makes sure that the view model is only instantiated once

    Loading data

    Okay, now let's move onto the point where you want to load data using the OnAppearing method. Let's start with the assumption that you have a valid ObservableCollection in the view model and it is ready to accept the data.

    public class Azioni_Righe
    {
        public Azioni_Righe()
        {
            Names = new ObservableCollection<string>();
        }
    
        //  This is a READONLY property so that you do not accidentally replace the collection 
        public ObservableCollection<string> Names { get; }
    
        public async Task LoadDataAsync()
        {
            // Clear any old data
            Names.Clear();
    
            // Get new data
            var data = myApi.FetchData();
    
            // Add the new data to the ObservableColleciton
            foreach(name in data)
            {
                Names.Add(name);
            }
        }
    }
    

    Notice that the Names ObservableCollection is a readonly property (i.e. it only has a getter). This is important so that you do not accidentally reset it without using INotifyPropertyChanged. If you do want to replace the instance, you need to add a backing field for the ObservableCollection and invoke OnPropertyChanged in the setter.

    Now you can call your LoadDataAsync method in the page's OnAppearing override

    namespace Goal.Funzioni
    {
        public partial class YourPage : ContentPage
        {
            public YourPage()
            {
                InitializeComponent();
    
                // Set the BindingContext here
                BindingContext = new Azioni_Righe();
            }
    
            protected override OnAppearing(...)
            {
                await (BindingContext as Azioni_Righe).LoadDataAsync();
            }
        }
    }
    
    1 person found this answer helpful.
    0 comments No comments

1 additional answer

Sort by: Most helpful
  1. Daniele Pinai 21 Reputation points
    2020-11-21T13:33:09.06+00:00

    Wow Thank's a lots
    The best answer that i had nver seen.
    It work very good !!!

    0 comments No comments