c# wpf mvvm observablecollection itemscontrol

c# - Actualizar ItemsControl cuando se actualiza un elemento en un ObservableCollection



wpf mvvm (1)

Encontré la respuesta usando Snoop para depurar XAML.

El problema es que está intentando enlazar con el método ToString () y eso no provoca el evento PropertyChanged. Si observa los enlaces XAML, notará que el ObservableCollection realmente está cambiando.

Ahora observe el control de cada artículo y su enlace de texto en la propiedad "Texto". No hay ninguno, es solo texto.

Para solucionarlo, simplemente agregue ItemsControl ItemTemplate con un DataTemplate que contenga los elementos que le gustaría que se muestren.

<ItemsControl Grid.Row="1" ItemsSource="{Binding Path=Employees, UpdateSourceTrigger=PropertyChanged}" BorderBrush="Red" BorderThickness="1" > <ItemsControl.ItemTemplate> <DataTemplate> <TextBlock> <TextBlock.Text> <MultiBinding StringFormat=" {0} is {1} years old"> <Binding Path="Name"/> <Binding Path="Age"/> </MultiBinding> </TextBlock.Text> </TextBlock> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl>

Ahora tenemos luz verde sobre el enlace. Se llama a RaisePropertyChanged.

Ta-da!

El problema:

  • ItemsControl un control de ItemsControl (o un control derivado de ItemsControl ) en la vista.
  • ItemsControl.ItemsSource propiedad ItemsControl.ItemsSource a un ObservableCollection en su ViewModel.
  • La vista se actualiza como se espera cuando se agrega o quita un elemento de ObservableCollection .
  • PERO, la vista no se actualiza cuando cambia una propiedad de un elemento en el ObservableCollection .

Fondo:

Parece que este es un problema común que muchos desarrolladores de WPF han encontrado. Se ha preguntado algunas veces:

Notificar a ObservableCollection cuando el artículo cambie

ObservableCollection no se da cuenta cuando el elemento en él cambia (incluso con INotifyPropertyChanged)

ObservableCollection y Item PropertyChanged

Mi implementación:

Intenté implementar la solución aceptada en Notify ObservableCollection cuando el artículo cambia . La idea básica es conectar un controlador PropertyChanged en su MainWindowViewModel para cada elemento en el ObservableCollection . Cuando se cambia la propiedad de un elemento, se invocará el controlador de eventos y, de alguna manera, se actualizará la Vista.

No pude lograr que la implementación funcionara. Aquí está mi implementación.

ViewModels:

class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string propertyName = "") { var handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } }

Artículo 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); } }

Ventana principal 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"); } }

Ver:

<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>

Mis resultados:

Para verificar mi implementación, creo una vista como tal. TextBlock.Text está vinculado al primer elemento de la colección. ItemsControl está vinculado a la colección en sí.

  • Al presionar el botón "Agregar empleado", se agrega un objeto EmployeeViewModel en la colección y tanto TextBlock como ItemsControl se actualizan como se esperaba.
  • Al presionar nuevamente "Agregar empleado", ItemsControl se actualiza con otra entrada. ¡Estupendo!
  • Presionando el botón "Incrementar edad del empleado". La propiedad Age de cada elemento se incrementa en 1. Se PropertyChanged evento PropertyChanged . Se ItemPropertyChanged controlador de eventos ItemPropertyChanged . El Textblock se actualiza como se esperaba. Sin embargo, ItemsControl no se actualiza.

Tengo la impresión de que ItemsControl debe actualizarse cuando Employee.Age se ItemsControl acuerdo con la respuesta en Notify ObservableCollection cuando el artículo cambie .