Search code examples
c#xamllistviewobservablecollectionpropertychanged

ListView does not update when source is changed - Even though I implemented PropertyChanged


I've been struggling with this problem for hours now:

I'm building an UWP application in the MVVM pattern.

Problem: I can't get my ListView's ItemSource to update/rebind to a new instance of ObservableCollection, when I change the ItemSource's property's value, even though I have implemented IPropertyChanged on the property.

Details: The page I am working on has a ListView which ItemSource is bound to an observable collection:

Page:

<ListView ItemsSource="{Binding Orders}" x:Name="OrderListView"><!--Inner bindings works fine--></ListView>

ViewModel:

    public ObservableCollection<Order> Orders {
        get { return _Orders; }
        set { Set(ref _Orders, value); }
    }
    private ObservableCollection<Order> _Orders;
    (...)
    //Property changed implementation from video (06:48): https://mva.microsoft.com/en-US/training-courses/windows-10-data-binding-14579?l=O5mda3EsB_1405632527

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    //If property is updated with, raise property changed, else don't
    public bool Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (Equals(storage, value))
            return false;
        storage = value;
        RaisePropertyChanged(propertyName);
        return true;
    }

When pressing a button in the UI:

<Button Content="Waiter" Click="Waiter_Click" Background="{Binding Waiter, Converter={StaticResource BoolToColorConverter}, FallbackValue='Yellow', TargetNullValue='Red'}"/>

It will tricker the "Waiter_Click" event handler in the code behind:

private void Waiter_Click(object sender, RoutedEventArgs e)
    {
        var viewModel = ((Button)sender).DataContext as OrderPageViewModel;
        if (viewModel.Waiter) viewModel.Waiter = false;
        else viewModel.Waiter = true;
    }

The Waiter property is implemented, so it raises PropertyChangedEvent in the ViewModel:

public bool Waiter
    {
        get { return _waiter; }
        set { Set(ref _waiter, value); FilterView(); }
    }
private bool _waiter;

The button is updated in the UI - This works fine.

After setting the private field of _waiter, I also want to filter out some data that I don't want to show.

I do this in the FilterView(); method. This is done by creating a new ObservableCollection, and setting the Orders property:

    public void FilterView()
    {    
        var filteredOrderList = new ObservableCollection<Order>();
        //Sorting is done
        Orders = filteredOrderList;
    }

Now. I thought that the code: Orders = filteredOrderList; would trigger the "setter" of Orders property, and raise the PropertyChanged event as it does with the button and all other UI elements that works fine - and tell the ListView to rebind to it. However it seems like the ListView is running on an old instance of the ObservableCollection, and does not care about my PropertyChanged event being fired...

I've debugged the program, and I see that filteredOrderList contains the data I want, and that Orders is changed. But it's like ViewList does not care about the PropertyChanged event is fired...

I don't want to add and remove items from the ObservableCollection because the filtering becomes very complicated... I just want to replace the Orders ObservableCollection with a new one, and let the ViewList reflect my changes...

Am I missing something? Can't I call PropertyChanged event multiple times? (I'm calling it once for the button update, and once for the Orders ObservableCollection to be changed).

Why is ListView not rebinding to the the new list? -Does ListView not care about PropertyChanged events being fired?

I will deeply appreciate any answer or workaround that will fix this or send me in the right direction.

Thank you, and sorry for long post.


Solution

  • After 8 and half an hour I found my mistake:

    I had accidentally defined the DataContext two times:

    <Page.DataContext>
        <vm:OrderPageViewModel/>
    </Page.DataContext>
    

    and further down the XAML file just before the ListView:

    <Grid.DataContext>
        <vm:OrderPageViewModel/>
    </Grid.DataContext>
    

    This caused two instances of my OrderPageViewModel.

    My ViewList was then bound to the old instance of the ViewModel, and any changes to the new ViewModel instance of ObservableCollection did not reflect any changes.

    If someone else get's the same problem: That ViewList does not update when changing ObservableCollection - It's properbly because you created multiple instances of the ObservableCollection.

    Also I found out that ObservableCollection only updates when adding og removing objects from it and clearing it using .Add(), .Remove() and .Clear() methods. It is not posible to call "CollectionChanged" or "PropertyChanged" without implementing it in the property itself, as I did on the Waiter property.

    I remade the filtering function to clear the collection and add new elements to it. It fixed my issue. :-)

    3 lines in XAML can really mess up your day, if you are not carefull.

    Have a great day, and thank you people who posted previous answers :-)