WPF MVVM observable collection not updating GUI

19,702

You're not modifying the ObservableCollection itself (such as adding/removing items) but the items INSIDE that collection. The ObservableCollection is responsible for notifying its own changes, not changes pertaining to its items. You Should NotifyPropertyChange("SubCustValue") in the setter of your SubCustValue.

The change is not being reflected in the UI because when you NotifyPropertyChange() the entire collection, instead of the indiviual property of the individual item, WPF detects it is actually the same instance (the same object reference to the same collection) as before, so nothing seems to have changed.

Share:
19,702
NZJames
Author by

NZJames

Updated on June 14, 2022

Comments

  • NZJames
    NZJames almost 2 years

    Im designing an MVVM WPF app, and have a ViewModel which has a property called SelectedCustomer, of type Customer. This object has a property called SummaryDetails of type ObservableCollection which renders into a ListView, line by line.

    To do this, I have created a separate property on the ViewModel called CustomerSummaryDetails which contains simply a get, that returns the collection contained within the customer I mentioned above.

    In the XAML I have bound the ItemsSource to the CustomerSummaryDetails Property.

    This was so I didnt have to bind to SelectedCustomer.SummaryDetails which isn't as clean.

    The SelectedCustomer property has a get and a set method, and the set calls OnPropertyChanged for the OTHER property, CustomerSummaryDetails, letting the XAML know that the underlying collection has changed and to update.

    The problem is though that when I update an item within the collection, it is not reflecting on the GUI, despite all the right events being called. I have stepped in and the set method of the SelectedCustomer is being called, and I then follow the OnPropertyChanged("CustomerSummaryDetails") call which goes into the "get" method of the CustomerSummaryDetails property as expected. I have delved into the value of the returned collection at this point, and the value within the list is the updated value, however nothing is getting reflected on the GUI, so I am puzzled as it seems the GUI is calling the get method to update it on the OnPropertyChanged() call, but it is not reflecting visually.

    UPDATE - CODE INCLUDED

    Sorry for not including code, I thought it would be easier to just describe but here are the main ViewModel properties

    public CustomerSummaryViewModel SelectedCustomer
    {
        get { return _selectedCustomer; }
        set
        {
            _selectedCustomer = value;
            OnPropertyChanged("CustomerSummaryDetails");
        }
    }
    
    public ObservableCollection<RbcUICustomerSummary> CustomerSummaryDetails
    {
        get { return _selectedCustomer.SummaryDetails; }
    }
    
    public ItemSummaryViewModel SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            _selectedItem = value;
            OnPropertyChanged("SelectedItem");
        }
    }
    

    XAML below

        <ListView x:Name="lvCustomerSummary" Margin="10,10,10,10" Background="#F4F8FB" ItemsSource="{Binding CustomerSummaryDetails}" MouseDoubleClick="lvCustomerSummary_MouseDoubleClick" ItemContainerStyle="{StaticResource myHeaderStyleColor}" VirtualizingStackPanel.IsVirtualizing="False" VirtualizingStackPanel.VirtualizationMode="Recycling">
                <ListView.View>
                <GridView ColumnHeaderContainerStyle="{StaticResource myHeaderStyle}">
                        <GridView.Columns>
                        <GridViewColumn Header=""  >
                            <GridViewColumn.CellTemplate>
                                <DataTemplate >
                                    <Grid>
                                        <Image Source="{z:ImageStaticResource {Binding IconSelect}}" Width="20" Height="20" />
                                    </Grid>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Width="200" Header="SubCustType" DisplayMemberBinding="{Binding SubCustType}" >
                        </GridViewColumn>
                        <GridViewColumn Width="200" Header="SubCustValue"  DisplayMemberBinding="{Binding SubCustValue}">
                        </GridViewColumn>
                        <GridViewColumn Header=""  >
                            <GridViewColumn.CellTemplate>
                                <DataTemplate >
                                    <Grid>
                                        <Image Source="{z:ImageStaticResource {Binding IconFlag}}" Width="20" Height="20" />
                                    </Grid>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
    

    And finally the Updater method which does the actual update

    private void DisplayCustomerComment(string commentEnt)
    {            
        if (_queueViewModel.SelectedCustomer == null) return;
            var selCust = _queueViewModel.SelectedCustomer;
    
        foreach (var t in selCust.SummaryDetails
            .Where(t => t.SubCustType == AppString.CustomerSummary.Comment))
        {
            t.SubCustValue = commentEnt;
            break;
        }
    
        _queueViewModel.SelectedCustomer = selCust;
    }
    
  • Federico Berasategui
    Federico Berasategui over 11 years
    As a side note, why do you foreach a collection and break on the first item? replace that for a FirstOrDefault() or something and remove the foreach.
  • Serhii Kyslyi
    Serhii Kyslyi over 11 years
    Could you explain, how I can update\rebind data if I have simple collection inside (no observable). Which way is the best? Thank You.
  • Federico Berasategui
    Federico Berasategui over 11 years
    @СергійКислий That's a separate question on its own, you'd rather post a new question.
  • aggietech
    aggietech over 9 years
    Question - what if we raised the property change for the entire collection manually - will we still need to do the changes for individual property?
  • Federico Berasategui
    Federico Berasategui over 9 years
    @aggietech If the collection remains the same, then Yes. That's precisely what the answer says.