3
votes

I have a binding problem with the WPF ListView. The view model implements INotifyPropertyChanged to get triggered on data updates. But it contains an observable collection of a type ("Person") that does NOT implement INotifyPropertyChanged.

The ListView shows up my bound persons after startup, that is fine. But after having changed the model's data (person's age), I somehow need to manually update the visual representation/binding - here's my problem.

I would appreciate if someone could kick me into the right direction, thank you!

The model is really simple:

// does not implement INotifyPropertyChanged interface
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

The PersonList is an ObservableCollection that is bound via ItemsSource to the ListView:

<ListView x:Name="ListViewPersons" ItemsSource="{Binding PersonList}">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"></GridViewColumn>
                <GridViewColumn Header="Age" DisplayMemberBinding="{Binding Age}"></GridViewColumn>
            </GridView>
        </ListView.View>
    </ListView>

The view's codebehind delegates the "age growing" to the viewmodel. After this change in the model's data, I need to update the GUI somehow, and here's my problem:

private void Button_Click(object sender, RoutedEventArgs e)
    {
           ...

            // increasing the age of each person in the model 
            viewModel.LetThemGetOlder();

            **// how to update the View?**

            // does not work
            ListViewPersons.GetBindingExpression(ListView.ItemsSourceProperty)
                                 .UpdateTarget();

            // does not work either
            ListViewPersons.InvalidateProperty(ListView.ItemsSourceProperty);
        }
    }

To be complete, the ViewModel:

class ViewModel : INotifyPropertyChanged
{
    public ViewModel()
    {
        PersonList = new ObservableCollection<Person>
            {
                new Person {Name = "Ellison", Age = 56},
                new Person {Name = "Simpson", Age = 44},
                new Person {Name = "Gates", Age = 12},
            };
    }

    internal void LetThemGetOlder()
    {
        foreach (var p in PersonList)
        {
            p.Age += 35;
        }
    }

    private ObservableCollection<Person> _personList;
    public ObservableCollection<Person> PersonList
    {
        get { return _personList; }
        set
        {
            _personList = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}
2

2 Answers

3
votes

You can try Items.Refresh(); but i would reconsider my class design, especially your viewmodel and models. Why don't your models implement INotifyPropertyChanged? Why don't you wrap your models in view models on a 1:1 basis? Currently you are working around WPF and you should only do that if you have a good reason.

3
votes

You also need to implement INotifyPropertyChanged on your Person class to update the UI when you change properties of the Person instances that are stored in the ObservableCollection.

public class Person : INotifyPropertyChanged
{
  private string _name;
  private string _age;

  public string Name { 
    get { 
      return _name;
    }
    set{
      _name = value, 
      OnPropertyChanged();
    }
  }
  public int Age { 
    get { 
     return _age;
    } 
    set{
      _age= value,
    OnPropertyChanged(); 
    }
  }

  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  {
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
  }

}