wake-up-neo.com

CollectionChanged implementieren

Ich habe CollectionChanged eventhandler(onCollectionChanged) einer der ObservableCollection-Eigenschaft hinzugefügt.

Ich habe herausgefunden, dass die onCollectionChanged-Methode nur für den Fall aufgerufen wird, dass Elemente hinzugefügt oder Elemente zur Sammlung entfernt werden, nicht jedoch, wenn das Element der Sammlung bearbeitet wird. 

Ich würde gerne wissen, wie ich die Liste/Sammlung neu hinzugefügter, entfernter und bearbeiteter Elemente in einer einzigen Sammlung verschicken kann. 

Vielen Dank.

20
WhoIsNinja

Sie müssen jedem Element einen PropertyChanged-Listener hinzufügen (der INotifyPropertyChanged implementieren muss), um eine Benachrichtigung über das Bearbeiten von Objekten in einer beobachtbaren Liste zu erhalten. 

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);
}

Vielleicht müssen Sie überprüfen, ob sich ein Element bereits in der ModifedItems-List befindet (mit der Methode Contains von List (object obj)) und nur dann ein neues Element hinzufügen, wenn das Ergebnis dieser Methode false ist.

Die Klasse Item muss INotifyPropertyChanged implementieren. Sehen Sie dieses Beispiel , um zu wissen, wie. Wie Robert Rossney sagte, können Sie das auch mit IEditableObject machen - wenn Sie diese Anforderung haben.

43
Arxisos

Eine ItemsControl hört auf CollectionChanged, um die Anzeige der Sammlung von Elementen zu verwalten, die auf dem Bildschirm angezeigt werden. Eine ContentControl hört auf PropertyChanged, um die Anzeige des bestimmten Elements zu verwalten, das auf dem Bildschirm angezeigt wird. Es ist ziemlich einfach, die beiden Konzepte im Kopf zu behalten, wenn Sie dies verstanden haben.

Die Nachverfolgung, ob ein Element bearbeitet wird oder nicht, ist keine der beiden Schnittstellen. Bei Eigenschaftsänderungen handelt es sich nicht um Bearbeitungen, dh sie stellen nicht notwendigerweise eine vom Benutzer initiierte Änderung des Status des Objekts dar. Beispielsweise kann ein Objekt eine ElapsedTime -Eigenschaft haben, die durch einen Zeitgeber ständig aktualisiert wird. Die Benutzeroberfläche muss über diese Eigenschaftsänderungsereignisse benachrichtigt werden, sie stellen jedoch sicherlich keine Änderungen in den zugrunde liegenden Daten des Objekts dar.

Die Standardmethode zum Nachverfolgen, ob ein Objekt bearbeitet wird oder nicht, besteht darin, das Objekt zuerst IEditableObject implementieren zu lassen. Sie können dann intern zur Klasse des Objekts entscheiden, welche Änderungen eine Bearbeitung darstellen (d. H. Sie müssen BeginEdit aufrufen) und welche Änderungen nicht. Sie können dann eine boolesche IsDirty-Eigenschaft implementieren, die gesetzt wird, wenn BeginEdit aufgerufen und beim Aufruf von EndEdit oder CancelEdit gelöscht wird. (Ich verstehe wirklich nicht, warum diese Eigenschaft nicht Teil von IEditableObject ist; ich habe noch kein bearbeitbares Objekt implementiert, für das dies nicht erforderlich war.)

Natürlich müssen Sie diese zweite Abstraktionsebene nicht implementieren, wenn Sie sie nicht benötigen. Sie können das Ereignis PropertyChanged sicherlich anhören und davon ausgehen, dass das Objekt bearbeitet wurde, wenn es ausgelöst wird. Das hängt wirklich von Ihren Anforderungen ab.

10
Robert Rossney

Meine Bearbeitung zu ' diese Antwort ' wird abgelehnt! Also füge ich meine Bearbeitung hier ein:

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 ist nicht mit INotiftyPropertyChanged identisch. Das Ändern der Eigenschaften der zugrundeliegenden Objekte deutet in keiner Weise darauf hin, dass sich die Sammlung geändert hat.

Eine Möglichkeit, dieses Verhalten zu erreichen, besteht darin, eine benutzerdefinierte Sammlung zu erstellen, die das Objekt abfragt, sobald es hinzugefügt wurde, und sich für das INotifyPropertyChanged.PropertyChanged-Ereignis zu registrieren. Es müsste sich dann ordnungsgemäß abmelden, wenn ein Artikel entfernt wird.

Ein Nachteil bei diesem Ansatz ist, dass Ihre Objekte in N Ebenen tief verschachtelt sind. Um dieses Problem zu lösen, müssen Sie im Wesentlichen jede Eigenschaft mit Reflektion abfragen, um zu bestimmen, ob es sich vielleicht noch um eine andere Sammlung handelt, die INotifyCollectionChanged oder einen anderen Container implementiert, der durchlaufen werden muss.

Hier ist ein rudimentäres, unversuchtes Beispiel ...

    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

Ich denke dass das Auflösen der ObservableCollection mit Elementen, die INotifyPropertyChanged implementieren, das CollectionChanged-Ereignis auslöst, wenn ein Element seine PropertyChanged-Benachrichtigung auslöst.

Im Nachhinein denke ich, Sie müssen BindingList<T> verwenden, um die Änderung einzelner Elemente auf diese Weise außerhalb der Box zu verbreiten.

Andernfalls müssen Sie die Änderungsbenachrichtigungen für jedes Element manuell abonnieren und das Ereignis CollectionChanged auslösen. Wenn Sie einen eigenen abgeleiteten ObservableCollection<T> erstellen, müssen Sie sich bei Instantiation und bei Add() und Insert() registrieren und bei Remove(), RemoveAt() und Clear() abmelden. Ansonsten können Sie das Ereignis CollectionChanged abonnieren und die hinzugefügten und entfernten Elemente in den Ereignisargumenten zum Abonnieren/Abbestellen verwenden.

1
Jay

Die einfachste Lösung, die ich für diese Einschränkung gefunden habe, besteht darin, das Element mit RemoveAt(index) zu entfernen und dann das modifizierte Element mit InsertAt(index) für denselben Index hinzuzufügen. Die ObservableCollection benachrichtigt daher die Ansicht.

0
LightTechnician

Verwenden Sie den folgenden Code:

-mein Modell:

 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));
        }
    }
}

Und in meiner Klasse implementieren Sie die PersonList-Methode, die aktiv ist, um den Wert eines Tastendrucks in AppBarControl zu erhöhen

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

Ich hoffe, dass es für jemanden nützlich ist, Beachten Sie Folgendes:

private ObservableCollection<IceCream> Flavors;

-

0

in Winforms ist BindingList Standardpraxis. In WPF & Silverlight arbeiten Sie normalerweise mit ObservableCollection und müssen bei jedem Element auf PropertyChanged achten

0
Robert Levy