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é deItemsControl
) dans le
vue. - Vous lier la
ItemsControl.ItemsSource
propriété à unObservableCollection
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 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 leTextBlock
etItemsControl
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. LePropertyChanged
événement est déclenché. LeItemPropertyChanged
gestionnaire d'événements est appelé. LeTextblock
est mis à jour comme prévu. Cependant, laItemsControl
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.
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
Vous devez vous connecter pour publier un commentaire.
J'ai trouvé la réponse à l'aide Snoop pour déboguer le code XAML.
Le problème, c'est que vous êtes en essayant de se lier à la méthode ToString() et qui ne soulève pas l'événement PropertyChanged. Si vous regardez le code XAML liaisons, vous remarquerez que l'ObservableCollection est en train de changer.
Maintenant, regardez à chaque point de contrôle et des textes de liaison dans le "Texte" de la propriété. Il n'y a aucun, c'est juste du texte.
Pour résoudre ce simplement ajouter un ItemsControl ItemTemplate avec un DataTemplate, qui contient les éléments que vous souhaitez afficher.
Nous avons maintenant une lumière verte sur la liaison. RaisePropertyChanged est appelé.
Ta-da!
Employees
de la propriété (l'ObservableCollection) et appelé leRaisePropertyChanged
dans l'incubateur. Mais elle n'est pas de déclencher laItemsControl
pour mettre à jour lorsque vous appuyez sur l'augmentation d'échelon de l'employé de l'âge de bouton.Grande réponse. Je comprends l'aide de ItemTemplate de se lier à l'élément de propriété peut contourner ce problème. En fait, si vous utilisez l'ItemTemplate, nous n'avons même plus besoin de la poignée de l'événement CollectionChanged pour raccorder ItemPropertyChanged gestionnaire pour chaque élément. Je pense qu'il y a peut-être un moyen de résoudre ce problème sans l'aide de l'ItemTemplate. Si aucune autre réponse est présentée. Je vais marquer le vôtre comme acceptée.
Le fait de déclarer un ItemTemplate est la façon standard de visualisation des éléments dans un ItemsControl ou un dérivé de contrôle ListBox. Vous voudrez peut-être lire le modèles de Données vue d'ensemble l'article sur MSDN.
Bon oeil, je n'ai pas le même avis, l'OP a été en s'appuyant sur la
.ToString()
pour afficher l'article! Vous avez raison, la bonne façon de le faire serait de créer unTemplate
pour l'affichage de l'élément, et lier les éléments de l'INTERFACE utilisateur pour les propriétés de sorte qu'ils réagissent à des notifications de changement. @FrankLiu Si vous voulez éviter la définition d'unItemTemplate
pour votre ItemsControl, vous pouvez également utiliser un Implicite DataTemplate à la place. C'est une façon de dire WPF qu'à chaque fois qu'il rencontre un élément de typeEmployeeViewModel
dans l'Arborescence Visuelle, il doit rendre l'aide du modèle spécifié au lieu de la valeur par défaut .ToString()Je pense que vous êtes à confondre les deux et quand ils doivent être utilisés. Le DataTemplate est-il simplement montrer ce que vous demandez XAML pour montrer. Il n'est pas là pour faire en sorte que lorsque vos propriétés sont mises à jour seront mises à jour XAML. Il ne s'occupe pas que. Comme pour l'utilisation de INotifyPropertyChanged, vous n'avez pas nécessairement besoin. Vous n'aurez besoin pour faire certain qu'il va changer dans l'INTERFACE utilisateur lorsque quelqu'un modifie la propriété. Look here.
OriginalL'auteur AzzamAziz