J'ai ajouté CollectionChanged eventhandler(onCollectionChanged)
à l'une des propriétés ObservableCollection
.
J'ai découvert que la méthode onCollectionChanged
n'est invoquée qu'en cas d'ajout ou de suppression d'éléments à la collection, mais pas dans le cas où un élément de collection est modifié.
J'aimerais savoir comment envoyer la liste/la collection d'éléments nouvellement ajoutés, supprimés et modifiés dans une seule collection.
Merci.
Vous devez ajouter un écouteur PropertyChanged
à chaque élément (qui doit implémenter INotifyPropertyChanged
) pour recevoir une notification concernant la modification d'objets dans une liste observable.
public ObservableCollection<Item> Names { get; set; }
public List<Item> ModifiedItems { get; set; }
public ViewModel()
{
this.ModifiedItems = new List<Item>();
this.Names = new ObservableCollection<Item>();
this.Names.CollectionChanged += this.OnCollectionChanged;
}
void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach(Item newItem in e.NewItems)
{
ModifiedItems.Add(newItem);
//Add listener for each item on PropertyChanged event
newItem.PropertyChanged += this.OnItemPropertyChanged;
}
}
if (e.OldItems != null)
{
foreach(Item oldItem in e.OldItems)
{
ModifiedItems.Add(oldItem);
oldItem.PropertyChanged -= this.OnItemPropertyChanged;
}
}
}
void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
Item item = sender as Item;
if(item != null)
ModifiedItems.Add(item);
}
Peut-être devez-vous vérifier si un élément est déjà dans la liste ModifedItems (avec la méthode List de la liste contient (object obj)) et ajoutez uniquement un nouvel élément si le résultat de cette méthode est false
La classe Item
doit implémenter INotifyPropertyChanged
. Voir ce exemple pour savoir comment. Comme Robert Rossney l’a dit, vous pouvez également le faire avec IEditableObject
- si vous avez cette exigence.
Une ItemsControl
écoute CollectionChanged
pour gérer l'affichage de la collection d'éléments qu'il présente à l'écran. ContentControl
écoute PropertyChanged
pour gérer l'affichage de l'élément spécifique qu'il présente à l'écran. Il est assez facile de garder les deux concepts séparés dans votre esprit une fois que vous comprenez cela.
Suivre si un élément est édité ou non n'est pas quelque chose que ces interfaces font. Les modifications de propriétés ne sont pas des modifications. En d'autres termes, elles ne représentent pas nécessairement une sorte de modification de l'état de l'objet initiée par l'utilisateur. Par exemple, un objet peut avoir une propriété ElapsedTime
continuellement mise à jour par un minuteur; l'interface utilisateur doit être avertie de ces événements de modification de propriété, mais ils ne représentent certainement pas les modifications apportées aux données sous-jacentes de l'objet.
Pour savoir si un objet est édité ou non, il faut d'abord que cet objet soit implémenté IEditableObject
. Vous pouvez ensuite, en interne dans la classe de l'objet, décider quelles modifications constituent une modification (c'est-à-dire vous obliger à appeler BeginEdit
) et quelles modifications ne le sont pas. Vous pouvez ensuite implémenter une propriété booléenne IsDirty
qui est définie lorsque BeginEdit
est appelée et effacée lorsque EndEdit
ou CancelEdit
est appelé. (Je ne comprends vraiment pas pourquoi cette propriété ne fait pas partie de IEditableObject
; je n'ai pas encore implémenté d'objet éditable qui ne l'exige pas.)
Bien sûr, il n'est pas nécessaire d'implémenter ce deuxième niveau d'abstraction si vous n'en avez pas besoin - vous pouvez certainement écouter l'événement PropertyChanged
et simplement supposer que l'objet a été modifié s'il est soulevé. Cela dépend vraiment de vos besoins.
Mon édition de ' cette réponse ' est rejetée! Je mets donc mon édition ici:
void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach(Item newItem in e.NewItems)
{
ModifiedItems.Add(newItem);
//Add listener for each item on PropertyChanged event
if (e.Action == NotifyCollectionChangedAction.Add)
newItem.PropertyChanged += this.ListTagInfo_PropertyChanged;
else if (e.Action == NotifyCollectionChangedAction.Remove)
newItem.PropertyChanged -= this.ListTagInfo_PropertyChanged;
}
}
// MSDN: OldItems:Gets the list of items affected by a Replace, Remove, or Move action.
//if (e.OldItems != null) <--- removed
}
INotifyCollectionChanged
n’est pas identique à INotiftyPropertyChanged
. La modification des propriétés des objets sous-jacents ne suggère en aucun cas que la collection a été modifiée.
Une façon de résoudre ce problème consiste à créer une collection personnalisée qui interrogera l'objet une fois ajouté et s'enregistrera pour l'événement INotifyPropertyChanged.PropertyChanged
; il serait alors nécessaire de se désinscrire de manière appropriée lorsqu'un élément est supprimé.
Un inconvénient de cette approche est que vos objets sont imbriqués dans N niveaux. Pour résoudre ce problème, vous devrez essentiellement interroger chaque propriété à l'aide de la réflexion afin de déterminer s'il s'agit peut-être d'une autre collection implémentant INotifyCollectionChanged
ou d'un autre conteneur à traverser.
Voici un exemple rudimentaire non testé ...
public class ObservableCollectionExt<T> : ObservableCollection<T>
{
public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged;
protected override void SetItem(int index, T item)
{
base.SetItem(index, item);
if(item is INotifyPropertyChanged)
(item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
}
protected override void ClearItems()
{
for (int i = 0; i < this.Items.Count; i++)
DeRegisterINotifyPropertyChanged(this.IndexOf(this.Items[i]));
base.ClearItems();
}
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
RegisterINotifyPropertyChanged(item);
}
protected override void RemoveItem(int index)
{
base.RemoveItem(index);
DeRegisterINotifyPropertyChanged(index);
}
private void RegisterINotifyPropertyChanged(T item)
{
if (item is INotifyPropertyChanged)
(item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
}
private void DeRegisterINotifyPropertyChanged(int index)
{
if (this.Items[index] is INotifyPropertyChanged)
(this.Items[index] as INotifyPropertyChanged).PropertyChanged -= OnPropertyChanged;
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
T item = (T)sender;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, item));
}
}
Je pense que le remplissage de ObservableCollection
avec des éléments qui implémentent INotifyPropertyChanged
provoquera le déclenchement de l'événement CollectionChanged
lorsqu'un élément déclenche sa notification PropertyChanged
.
À la réflexion, je pense que vous devez utiliser BindingList<T>
pour que les modifications d’éléments individuels se propagent de cette manière, prématurément.
Sinon, vous devrez vous abonner manuellement aux notifications de modification de chaque élément et déclencher l'événement CollectionChanged
. Notez que si vous créez votre propre ObservableCollection<T>
dérivé, vous devez vous abonner à l'instanciation et à Add()
et Insert()
, et vous désabonner à Remove()
, RemoveAt()
et Clear()
. Sinon, vous pouvez vous abonner à l'événement CollectionChanged
et utiliser les éléments ajoutés et supprimés des arguments d'événement pour vous abonner/vous désabonner.
La solution la plus simple que j'ai trouvée à cette limitation est de supprimer l'élément à l'aide de RemoveAt(index)
, puis d'ajouter l'élément modifié sur le même index à l'aide de InsertAt(index)
. Ainsi, ObservableCollection notifiera la vue.
Utilisez le code suivant:
-mon modele:
public class IceCream: INotifyPropertyChanged
{
private int liczba;
public int Liczba
{
get { return liczba; }
set { liczba = value;
Zmiana("Liczba");
}
}
public IceCream(){}
//in the same class implement the below-it will be responsible for track a changes
public event PropertyChangedEventHandler PropertyChanged;
private void Zmiana(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Et dans ma classe PersonList, méthode d'implémentation responsable de l'activation augmentant la valeur d'un clic après un clic dans AppBarControl
async private void Add_Click(object sender, RoutedEventArgs e)
{
List<IceCream> items = new List<IceCream>();
foreach (IceCream item in IceCreamList.SelectedItems)
{
int i=Flavors.IndexOf(item);
Flavors[i].Liczba =item.Liczba+ 1;
//Flavors.Remove(item);
//item.Liczba += 1;
// items.Add(item);
// Flavors.Add(item);
}
MessageDialog d = new MessageDialog("Zwiększono liczbę o jeden");
d.Content = "Zwiększono liczbę o jeden";
await d.ShowAsync();
IceCreamList.SelectedIndex = -1;
}
}
J'espère que cela sera utile pour quelqu'un de Notez que:
private ObservableCollection<IceCream> Flavors;
-
dans winforms, BindingList
est une pratique courante. dans WPF et Silverlight, vous êtes généralement coincé avec ObservableCollection
et devez écouter PropertyChanged
pour chaque élément