web-dev-qa-db-fra.com

Implémentation de CollectionChanged

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.

20
WhoIsNinja

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.

43
Arxisos

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.

10
Robert Rossney

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
}
4
Behzad Ebrahimi

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)); 
        }
    }
2
Aaron McIver

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.

1
Jay

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.

0
LightTechnician

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;

-

0
Sebastian Rycombel

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

0
Robert Levy