Il problema:Aggiornamento ItemsControl quando un elemento in un ObservableCollection viene aggiornato
- si dichiara un
ItemsControl
(o un controllo derivato daItemsControl
) nella vista . - Si associa la proprietà
ItemsControl.ItemsSource
a unObservableCollection
nel ViewModel. - La visualizzazione viene aggiornata come previsto quando un articolo viene aggiunto/rimosso da
ObservableCollection
. - MA, la vista non si aggiorna quando si modifica una proprietà di un articolo in
ObservableCollection
.
Background:
Sembra che questo è un problema comune molti sviluppatori WPF hanno incontrato. E 'stato chiesto un paio di volte:
Notify ObservableCollection when Item changes
ObservableCollection not noticing when Item in it changes (even with INotifyPropertyChanged)
ObservableCollection and Item PropertyChanged
mia implementazione:
ho cercato di implementare la soluzione accettata in Notify ObservableCollection when Item changes. L'idea di base è quella di collegare un gestore PropertyChanged
nel MainWindowViewModel per ciascun articolo nello ObservableCollection
. Quando viene modificata la proprietà di un oggetto, viene richiamato il gestore eventi e in qualche modo la vista viene aggiornata.
Non sono riuscito a far funzionare l'implementazione. Ecco la mia implementazione.
ViewModels:
class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName = "")
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Articolo 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);
}
}
Finestra 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");
}
}
Vista:
<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>
mie Risultati:
per verificare la mia realizzazione, creo una vista in questo modo. Lo TextBlock.Text
è associato al primo elemento della raccolta. Lo ItemsControl
è associato alla raccolta stessa.
- Premendo il pulsante "Aggiungi Dipendente" aggiunge un oggetto
EmployeeViewModel
nella raccolta e sia ilTextBlock
eItemsControl
vengono aggiornati come previsto. - Premendo di nuovo "Aggiungi dipendente", lo
ItemsControl
viene aggiornato con un'altra voce. Grande! - Premere il pulsante "Increment Employee Age". La proprietà
Age
di ciascun elemento viene incrementata di 1. Viene generato l'eventoPropertyChanged
. Il gestore di eventiItemPropertyChanged
viene richiamato.Textblock
viene aggiornato come previsto. Tuttavia, loItemsControl
non è aggiornato.
Ho l'impressione che il ItemsControl
deve essere aggiornato anche quando il Employee.Age
viene modificato in base alla risposta in Notify ObservableCollection when Item changes.
Cosa stai cercando di fare? Una collezione osservabile sta osservando la collezione stessa, non le proprietà dei bambini. Qual è il vantaggio di un evento modificato di una raccolta se viene modificata una proprietà figlio? – michael
@michael, voglio che 'ItemsControl' si aggiorni quando un oggetto della collezione viene aggiornato. –
Ciò che comporterà comunque, tutti gli elementi della collezione sono stati conteggiati. Avere l'interfaccia utente per ottenere la proprietà non cambierà nulla ... il riferimento è sempre lo stesso. – michael