Mise à jour ItemsControl lorsqu'un élément dans une ObservableCollection est mis à jour

Le Problème:

  • Vous déclarez un ItemsControl ( ou un contrôle dérivé de ItemsControl) dans le
    vue.
  • Vous lier la ItemsControl.ItemsSource propriété à un ObservableCollection dans votre ViewModel.
  • Votre point de vue mises à jour comme prévu lorsqu'un élément est ajouté /retiré de la ObservableCollection.
  • MAIS, le point de vue n'est pas mise à jour lorsque vous modifiez une propriété d'un élément dans la ObservableCollection.

De fond:

Il semble que ce est un problème commun à de nombreux développeurs WPF ont rencontrés. Il a été demandé à quelques reprises:

Informer ObservableCollection lorsque l'objet de modifications

ObservableCollection pas de remarquer, dans l'Élément de changement (même avec INotifyPropertyChanged)

ObservableCollection et le Point PropertyChanged

Ma Mise En Œuvre:

J'ai essayé de mettre en œuvre la solution retenue dans Informer ObservableCollection lorsque l'objet de modifications. L'idée de base est de brancher une PropertyChanged gestionnaire dans votre MainWindowViewModel pour chaque élément dans la ObservableCollection. Lorsqu'un élément est modifié, le gestionnaire d'événements ne peut être invoquée, et en quelque sorte le point de Vue est mise à jour.

Je ne pouvais pas obtenir la mise en oeuvre de travailler. Voici ma mise en œuvre.

Viewmodel:

class ViewModelBase : INotifyPropertyChanged 
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged(string propertyName = "")
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Élément ViewModel:

class EmployeeViewModel : ViewModelBase
{
    private int _age;
    private string _name;

    public int Age 
    {
        get { return _age; }
        set
        {
            _age = value;
            RaisePropertyChanged("Age");
        }
    }

    public string Name  
    {
        get { return _name; }
        set
        {
            _name = value;
            RaisePropertyChanged("Name");
        }
    }

    public override string ToString()
    {
        return string.Format("{0} is {1} years old", Name, Age);
    }
}

Fenêtre Principale ViewModel:

class MainWindowViewModel : ViewModelBase
{
private ObservableCollection<EmployeeViewModel> _collection;
public MainWindowViewModel()
{
_collection = new ObservableCollection<EmployeeViewModel>();
_collection.CollectionChanged += MyItemsSource_CollectionChanged;
AddEmployeeCommand = new DelegateCommand(() => AddEmployee());
IncrementEmployeeAgeCommand = new DelegateCommand(() => IncrementEmployeeAge());
}
public ObservableCollection<EmployeeViewModel> Employees 
{
get { return _collection; }
}
public ICommand AddEmployeeCommand { get; set; }
public ICommand IncrementEmployeeAgeCommand { get; set; }
public void AddEmployee()
{
_collection.Add(new EmployeeViewModel()
{
Age = 1,
Name = "Random Joe",
});
}
public void IncrementEmployeeAge()
{
foreach (var item in _collection)
{
item.Age++;
}
}
private void MyItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
foreach (EmployeeViewModel item in e.NewItems)
item.PropertyChanged += ItemPropertyChanged;
if (e.OldItems != null)
foreach (EmployeeViewModel item in e.OldItems)
item.PropertyChanged -= ItemPropertyChanged;
}
private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
RaisePropertyChanged("Employees");
}
}

Vue:

<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
xmlns:d="clr-namespace:Iress.IosPlus.DynamicOE.Controls"
Title="MainWindow" Height="350" Width="350">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*"></ColumnDefinition>
<ColumnDefinition Width="0.7*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<Button Command="{Binding AddEmployeeCommand}">Add Employee</Button>
<Button Command="{Binding IncrementEmployeeAgeCommand}">Increment Employee Age</Button>
</StackPanel>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="0.1*"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Path=Employees[0]}"></TextBlock>
<ItemsControl Grid.Row="1" ItemsSource="{Binding Path=Employees}" BorderBrush="Red" BorderThickness="1"></ItemsControl>
</Grid>
</Grid>

Mes Résultats:

De vérifier mon application, j'ai créer une vue comme si. Le TextBlock.Text est lié au premier élément de la collection. Le ItemsControl est lié à la collection elle-même.

  • La touche "Ajouter un Employé" bouton ajoute un EmployeeViewModel objet dans la collecte et à la fois le TextBlock et ItemsControl sont mis à jour comme prévu.
  • La touche "Ajouter un Employé" encore une fois, la ItemsControl est mis à jour avec une autre entrée. Super!
  • Appuyant sur le "Incrémenter Employé de l'Âge". Le Age de la propriété de chaque élément est incrémenté de 1. Le PropertyChanged événement est déclenché. Le ItemPropertyChanged gestionnaire d'événements est appelé. Le Textblock est mis à jour comme prévu. Cependant, la ItemsControl n'est pas mis à jour.

Je suis sous l'impression que les ItemsControl doit être mis à jour lors de la Employee.Age est modifiée en fonction de la réponse dans Informer ObservableCollection lorsque l'objet de modifications.

Mise à jour ItemsControl lorsqu'un élément dans une ObservableCollection est mis à jour

De quoi êtes-vous même en essayant de faire? Une collection observable est l'observation de la collection elle-même, et non pas des propriétés de l'enfant. Quel est l'avantage de faire une collection d'événements de changement si un enfant modifications de la propriété?
Je veux le ItemsControl pour actualiser lorsqu'un élément de la collection est mise à jour.
Que pourrait-elle accomplir bien que, de tous les éléments de la collection sont pris en compte. Avoir l'INTERFACE utilisateur d'obtenir la propriété de ne pas changer quoi que ce soit... la référence est toujours la même.

OriginalL'auteur Frank Liu | 2015-02-10