problem:Aktualizacja ItemsControl gdy pozycja w ObservableCollection jest aktualizowana
- deklarujesz
ItemsControl
(lub kontrola pochodzi zItemsControl
) w widoku . - Powiąż właściwość
ItemsControl.ItemsSource
zObservableCollection
w swoim ViewModel. - Twój widok aktualizuje się zgodnie z oczekiwaniami, gdy element zostanie dodany do/usunięty z
ObservableCollection
. - ALE, widok nie aktualizuje się po zmianie właściwości elementu w
ObservableCollection
.
Tło:
Wydaje się, że jest to częsty problem wielu deweloperów WPF spotkałem. Został poproszony kilka razy:
Notify ObservableCollection when Item changes
ObservableCollection not noticing when Item in it changes (even with INotifyPropertyChanged)
ObservableCollection and Item PropertyChanged
Moje Wykonanie:
starałem się wdrożyć przyjętego rozwiązania w Notify ObservableCollection when Item changes. Podstawową ideą jest podpięcie się handlerkiem PropertyChanged
w MainWindowViewModel dla każdego elementu w ObservableCollection
. Gdy właściwość elementu zostanie zmieniona, procedura obsługi zdarzenia zostanie wywołana i jakoś zaktualizuje widok.
Nie mogłem uruchomić wdrożenia. Oto moja implementacja.
ViewModels:
class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName = "")
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Przedmiot 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);
}
}
Okno główne 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");
}
}
Widok:
<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>
Moi Wyniki:
zweryfikować swoją realizację, tworzę widok jak tak. Numer TextBlock.Text
jest powiązany z pierwszym elementem w kolekcji. ItemsControl
jest związany z samą kolekcją.
- Naciśnięcie przycisku „Dodaj pracownicze” dodaje obiekt
EmployeeViewModel
w zbiorach i zarównoTextBlock
iItemsControl
są aktualizowane zgodnie z oczekiwaniami. - Po ponownym naciśnięciu przycisku "Dodaj pracownika",
ItemsControl
zostaje zaktualizowany o kolejny wpis. Wspaniały! - Naciśnięcie przycisku "Przyrost wieku pracownika". Właściwość
Age
każdej pozycji zwiększa się o 1. WydarzeniePropertyChanged
zostało podniesione. Obsługa zdarzeniaItemPropertyChanged
jest wywoływana. NumerTextblock
jest aktualizowany zgodnie z oczekiwaniami. Jednak wersjaItemsControl
nie jest aktualizowana.
Jestem pod wrażeniem, że ItemsControl
należy zaktualizować zbyt kiedy Employee.Age
zmienia się w zależności od odpowiedzi w Notify ObservableCollection when Item changes.
Co jeszcze próbujesz zrobić? Obserwowalna kolekcja obserwuje samą kolekcję, a nie właściwości dzieci. Jaka jest korzyść z przeprowadzania zdarzenia zmiany kolekcji, jeśli zmieni się właściwość podrzędna? – michael
@ Michael, chcę 'ItemsControl' odświeżyć, gdy element w kolekcji jest aktualizowany. –
Co by się udało, wszystkie pozycje w kolekcji zostały uwzględnione. Posiadanie interfejsu użytkownika powoduje, że właściwość niczego nie zmieni ... odniesienie jest wciąż takie samo. – michael