Beim Löschen einer ObservableCollection befinden sich keine Elemente in e.OldItems


91

Ich habe hier etwas, das mich wirklich überrascht.

Ich habe eine ObservableCollection von T, die mit Elementen gefüllt ist. Ich habe auch einen Ereignishandler an das CollectionChanged-Ereignis angehängt.

Wenn Sie löschen die Sammlung es verursacht ein Collection Ereignis mit e.Action Set NotifyCollectionChangedAction.Reset. Ok, das ist normal. Aber was seltsam ist, ist, dass weder e.OldItems noch e.NewItems etwas enthalten. Ich würde erwarten, dass e.OldItems mit allen Elementen gefüllt wird, die aus der Sammlung entfernt wurden.

Hat das noch jemand gesehen? Und wenn ja, wie sind sie damit umgegangen?

Einige Hintergrundinformationen: Ich verwende das CollectionChanged-Ereignis, um ein anderes Ereignis anzuhängen und von diesem zu trennen. Wenn ich also keine Elemente in e.OldItems erhalte, kann ich mich nicht von diesem Ereignis trennen.


ERKLÄRUNG: Ich weiß, dass die Dokumentation nicht direkt besagt , dass sie sich so verhalten muss. Aber für jede andere Aktion benachrichtigt es mich darüber, was es getan hat. Ich gehe also davon aus, dass es mir sagen würde ... auch im Fall von Löschen / Zurücksetzen.


Unten finden Sie den Beispielcode, wenn Sie ihn selbst reproduzieren möchten. Zunächst einmal die xaml:

<Window
    x:Class="ObservableCollection.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1"
    Height="300"
    Width="300"
>
    <StackPanel>
        <Button x:Name="addButton" Content="Add" Width="100" Height="25" Margin="10" Click="addButton_Click"/>
        <Button x:Name="moveButton" Content="Move" Width="100" Height="25" Margin="10" Click="moveButton_Click"/>
        <Button x:Name="removeButton" Content="Remove" Width="100" Height="25" Margin="10" Click="removeButton_Click"/>
        <Button x:Name="replaceButton" Content="Replace" Width="100" Height="25" Margin="10" Click="replaceButton_Click"/>
        <Button x:Name="resetButton" Content="Reset" Width="100" Height="25" Margin="10" Click="resetButton_Click"/>
    </StackPanel>
</Window>

Als nächstes der Code dahinter:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;

namespace ObservableCollection
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            _integerObservableCollection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_integerObservableCollection_CollectionChanged);
        }

        private void _integerObservableCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Move:
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Replace:
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Reset:
                    break;
                default:
                    break;
            }
        }

        private void addButton_Click(object sender, RoutedEventArgs e)
        {
            _integerObservableCollection.Add(25);
        }

        private void moveButton_Click(object sender, RoutedEventArgs e)
        {
            _integerObservableCollection.Move(0, 19);
        }

        private void removeButton_Click(object sender, RoutedEventArgs e)
        {
            _integerObservableCollection.RemoveAt(0);
        }

        private void replaceButton_Click(object sender, RoutedEventArgs e)
        {
            _integerObservableCollection[0] = 50;
        }

        private void resetButton_Click(object sender, RoutedEventArgs e)
        {
            _integerObservableCollection.Clear();
        }

        private ObservableCollection<int> _integerObservableCollection = new ObservableCollection<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 };
    }
}

Warum müssen Sie die Veranstaltung abbestellen? In welche Richtung abonnieren Sie? Ereignisse erstellen einen Verweis auf den Abonnenten, der vom Raiser gehalten wird, und nicht umgekehrt. Wenn es sich bei den Raisern um Gegenstände in einer Sammlung handelt, die gelöscht werden, werden sie sicher im Müll gesammelt und die Referenzen verschwinden - kein Leck. Wenn die Elemente Abonnenten sind und von einem Raiser referenziert werden, setzen Sie das Ereignis im Raiser einfach auf Null, wenn Sie einen Reset erhalten - Sie müssen die Elemente nicht einzeln abbestellen.
Aleksandr Dubinsky

Glauben Sie mir, ich weiß, wie das funktioniert. Die fragliche Veranstaltung war auf einem Singleton, der lange Zeit herumhing ... also waren die Gegenstände in der Sammlung die Abonnenten. Ihre Lösung, das Ereignis nur auf null zu setzen, funktioniert nicht ... da das Ereignis noch ausgelöst werden muss ... möglicherweise andere Abonnenten benachrichtigen (nicht unbedingt die in der Sammlung).
Plots

Antworten:


46

Es wird nicht behauptet, die alten Elemente einzuschließen, da Zurücksetzen nicht bedeutet, dass die Liste gelöscht wurde

Dies bedeutet, dass etwas Dramatisches passiert ist und die Kosten für das Ausarbeiten des Hinzufügens / Entfernens höchstwahrscheinlich die Kosten für das erneute Scannen der Liste von Grund auf übersteigen würden. Das sollten Sie also tun.

MSDN schlägt ein Beispiel für die Neusortierung der gesamten Sammlung als Kandidat für das Zurücksetzen vor.

Wiederholen. Zurücksetzen bedeutet nicht klar , es bedeutet, dass Ihre Annahmen über die Liste jetzt ungültig sind. Behandle es so, als wäre es eine völlig neue Liste . Klar ist zufällig eine Instanz davon, aber es könnte auch andere geben.

Einige Beispiele:
Ich hatte eine Liste wie diese mit vielen Elementen und sie wurde an eine WPF gebunden ListView, um sie auf dem Bildschirm anzuzeigen.
Wenn Sie die Liste löschen und das .ResetEreignis auslösen, ist die Leistung ziemlich augenblicklich. Wenn Sie jedoch stattdessen viele einzelne .RemoveEreignisse auslösen, ist die Leistung schrecklich, da WPF die Elemente einzeln entfernt. Ich habe auch .Resetin meinem eigenen Code angegeben, dass die Liste neu sortiert wurde, anstatt Tausende einzelner MoveVorgänge auszugeben . Wie bei Clear gibt es einen großen Leistungseinbruch, wenn viele einzelne Ereignisse ausgelöst werden.


1
Ich werde auf dieser Grundlage respektvoll widersprechen. In der Dokumentation heißt es: Stellt eine dynamische Datenerfassung dar, die Benachrichtigungen bereitstellt, wenn Elemente hinzugefügt, entfernt oder die gesamte Liste aktualisiert werden (siehe msdn.microsoft.com/en-us/library/ms668613(v=VS) .100) .aspx )
cplotts

6
In den Dokumenten wird angegeben, dass Sie benachrichtigt werden sollen, wenn Elemente hinzugefügt / entfernt / aktualisiert werden. Es wird jedoch nicht versprochen, Ihnen alle Details der Elemente mitzuteilen ... nur, dass das Ereignis aufgetreten ist. Unter diesem Gesichtspunkt ist das Verhalten in Ordnung. Persönlich denke ich, dass sie OldItemsbeim Löschen einfach alle Elemente einfügen sollten (es wird nur eine Liste kopiert), aber vielleicht gab es ein Szenario, in dem dies zu teuer war. Auf jeden Fall, wenn Sie eine Sammlung wollen , die nicht Sie alle gelöschten Objekte benachrichtigen, wäre es nicht schwer zu tun.
Orion Edwards

2
Nun, wenn dies Resetauf eine teure Operation hinweisen soll, ist es sehr wahrscheinlich, dass die gleiche Begründung für das Kopieren über die gesamte Liste gilt OldItems.
Pbalaga

7
Lustige Tatsache: seit .NET 4.5 , Resetbedeutet eigentlich „Der Inhalt der Sammlung wurde gelöscht .“ Siehe msdn.microsoft.com/en-us/library/…
Athari

9
Diese Antwort hilft leider nicht viel. Ja, Sie können die gesamte Liste erneut scannen, wenn Sie einen Reset erhalten. Sie haben jedoch keinen Zugriff auf das Entfernen von Elementen, die Sie möglicherweise benötigen, um Ereignishandler aus diesen zu entfernen. Das ist ein großes Problem.
Virus721

22

Wir hatten hier das gleiche Problem. Die Aktion Zurücksetzen in CollectionChanged enthält keine OldItems. Wir hatten eine Problemumgehung: Wir haben stattdessen die folgende Erweiterungsmethode verwendet:

public static void RemoveAll(this IList list)
{
   while (list.Count > 0)
   {
      list.RemoveAt(list.Count - 1);
   }
}

Wir haben die Funktion Clear () nicht unterstützt und eine NotSupportedException im CollectionChanged-Ereignis für Reset-Aktionen ausgelöst. RemoveAll löst im CollectionChanged-Ereignis eine Remove-Aktion mit den richtigen OldItems aus.


Gute Idee. Ich mag es nicht, Clear nicht zu unterstützen, da dies (meiner Erfahrung nach) die Methode ist, die die meisten Leute verwenden ... aber zumindest warnen Sie den Benutzer mit einer Ausnahme.
Plots

Ich stimme zu, dies ist nicht die ideale Lösung, aber wir fanden, dass dies die beste akzeptable Problemumgehung ist.
Decasteljau

Du sollst die alten Gegenstände nicht benutzen! Was Sie tun sollen, ist, alle Daten, die Sie auf der Liste haben, zu sichern und sie erneut zu scannen, als wäre es eine neue Liste!
Orion Edwards

16
Das Problem, Orion, mit Ihrem Vorschlag ... ist der Anwendungsfall, der diese Frage ausgelöst hat. Was passiert, wenn ich Elemente in der Liste habe, von denen ich ein Ereignis trennen möchte? Ich kann die Daten nicht einfach auf die Liste setzen ... dies würde zu Speicherlecks / Druck führen.
Plots

5
Der Hauptnachteil dieser Lösung besteht darin, dass Sie beim Entfernen von 1000 Elementen CollectionChanged 1000-mal auslösen und die Benutzeroberfläche die CollectionView 1000-mal aktualisieren muss (das Aktualisieren von UI-Elementen ist teuer). Wenn Sie keine Angst haben, die ObservableCollection-Klasse zu überschreiben, können Sie sie so gestalten, dass sie das Clear () -Ereignis auslöst, aber die richtigen Ereignisargumente bereitstellt, mit denen der Überwachungscode die Registrierung aller entfernten Elemente aufheben kann.
Alain

13

Eine andere Option besteht darin, das Reset-Ereignis durch ein einzelnes Remove-Ereignis zu ersetzen, dessen gelöschte Elemente in der OldItems-Eigenschaft wie folgt enthalten sind:

public class ObservableCollectionNoReset<T> : ObservableCollection<T>
{
    protected override void ClearItems()
    {
        List<T> removed = new List<T>(this);
        base.ClearItems();
        base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (e.Action != NotifyCollectionChangedAction.Reset)
            base.OnCollectionChanged(e);
    }
    // Constructors omitted
    ...
}

Vorteile:

  1. Keine Notwendigkeit, eine zusätzliche Veranstaltung zu abonnieren (wie in der akzeptierten Antwort erforderlich)

  2. Generiert nicht für jedes entfernte Objekt ein Ereignis (einige andere vorgeschlagene Lösungen führen zu mehreren entfernten Ereignissen).

  3. Der Abonnent muss bei jedem Ereignis nur NewItems & OldItems überprüfen, um nach Bedarf Ereignishandler hinzuzufügen / zu entfernen.

Nachteile:

  1. Kein Reset-Ereignis

  2. Kleiner (?) Overhead beim Erstellen einer Kopie der Liste.

  3. ???

EDIT 2012-02-23

Wenn Sie an WPF-Listen-basierte Steuerelemente gebunden sind, führt das Löschen einer ObservableCollectionNoReset-Auflistung mit mehreren Elementen leider zu einer Ausnahme "Bereichsaktionen werden nicht unterstützt". Um mit Steuerelementen mit dieser Einschränkung verwendet zu werden, habe ich die ObservableCollectionNoReset-Klasse in Folgendes geändert:

public class ObservableCollectionNoReset<T> : ObservableCollection<T>
{
    // Some CollectionChanged listeners don't support range actions.
    public Boolean RangeActionsSupported { get; set; }

    protected override void ClearItems()
    {
        if (RangeActionsSupported)
        {
            List<T> removed = new List<T>(this);
            base.ClearItems();
            base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
        }
        else
        {
            while (Count > 0 )
                base.RemoveAt(Count - 1);
        }                
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (e.Action != NotifyCollectionChangedAction.Reset)
            base.OnCollectionChanged(e);
    }

    public ObservableCollectionNoReset(Boolean rangeActionsSupported = false) 
    {
        RangeActionsSupported = rangeActionsSupported;
    }

    // Additional constructors omitted.
 }

Dies ist nicht so effizient, wenn RangeActionsSupported false ist (Standardeinstellung), da pro Objekt in der Auflistung eine Benachrichtigung zum Entfernen entfernt wird


Ich mag das, aber leider hat das Silverlight 4 NotifyCollectionChangedEventArgs keinen Konstruktor, der eine Liste von Elementen nimmt.
Simon Brangwin

2
Ich habe diese Lösung geliebt, aber sie funktioniert nicht ... Sie dürfen kein NotifyCollectionChangedEventArgs auslösen, bei dem mehr als ein Element geändert wurde, es sei denn, die Aktion lautet "Zurücksetzen". Sie erhalten eine Ausnahme Range actions are not supported.Ich weiß nicht, warum es dies tut, aber jetzt bleibt keine andere Wahl, als jedes Element einzeln zu entfernen ...
Alain

2
@Alain Die ObservableCollection legt diese Einschränkung nicht fest. Ich vermute, es ist das WPF-Steuerelement, an das Sie die Sammlung gebunden haben. Ich hatte das gleiche Problem und bin nie dazu gekommen, ein Update mit meiner Lösung zu veröffentlichen. Ich werde meine Antwort mit der geänderten Klasse bearbeiten, die funktioniert, wenn sie an ein WPF-Steuerelement gebunden ist.
Grantnz

Ich sehe das jetzt. Ich habe tatsächlich eine sehr elegante Lösung gefunden, die das CollectionChanged-Ereignis überschreibt und foreach( NotifyCollectionChangedEventHandler handler in this.CollectionChanged )If durchläuft. handler.Target is CollectionViewDann können Sie den Handler mit Action.ResetArgs auslösen, andernfalls können Sie die vollständigen Args bereitstellen. Das Beste aus beiden Welten auf Handler-für-Handler-Basis :). Ein bisschen
Alain

Ich habe unten meine eigene Lösung veröffentlicht. stackoverflow.com/a/9416535/529618 Vielen Dank an Sie für Ihre inspirierende Lösung. Es hat mich auf halbem Weg dorthin gebracht.
Alain

9

Ich habe eine Lösung gefunden, mit der der Benutzer sowohl die Effizienz des Hinzufügens oder Entfernens vieler Elemente gleichzeitig nutzen kann, während nur ein Ereignis ausgelöst wird, als auch die Anforderungen von UIElements erfüllen kann, um die Action.Reset-Ereignisargumente abzurufen, während alle anderen Benutzer dies tun würden wie eine Liste von Elementen hinzugefügt und entfernt.

Bei dieser Lösung wird das CollectionChanged-Ereignis überschrieben. Wenn wir dieses Ereignis auslösen, können wir tatsächlich das Ziel jedes registrierten Handlers betrachten und dessen Typ bestimmen. Da nur ICollectionView-Klassen Argumente erfordern, NotifyCollectionChangedAction.Resetwenn sich mehr als ein Element ändert, können wir sie herausgreifen und allen anderen die richtigen Ereignisargumente geben, die die vollständige Liste der entfernten oder hinzugefügten Elemente enthalten. Unten ist die Implementierung.

public class BaseObservableCollection<T> : ObservableCollection<T>
{
    //Flag used to prevent OnCollectionChanged from firing during a bulk operation like Add(IEnumerable<T>) and Clear()
    private bool _SuppressCollectionChanged = false;

    /// Overridden so that we may manually call registered handlers and differentiate between those that do and don't require Action.Reset args.
    public override event NotifyCollectionChangedEventHandler CollectionChanged;

    public BaseObservableCollection() : base(){}
    public BaseObservableCollection(IEnumerable<T> data) : base(data){}

    #region Event Handlers
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if( !_SuppressCollectionChanged )
        {
            base.OnCollectionChanged(e);
            if( CollectionChanged != null )
                CollectionChanged.Invoke(this, e);
        }
    }

    //CollectionViews raise an error when they are passed a NotifyCollectionChangedEventArgs that indicates more than
    //one element has been added or removed. They prefer to receive a "Action=Reset" notification, but this is not suitable
    //for applications in code, so we actually check the type we're notifying on and pass a customized event args.
    protected virtual void OnCollectionChangedMultiItem(NotifyCollectionChangedEventArgs e)
    {
        NotifyCollectionChangedEventHandler handlers = this.CollectionChanged;
        if( handlers != null )
            foreach( NotifyCollectionChangedEventHandler handler in handlers.GetInvocationList() )
                handler(this, !(handler.Target is ICollectionView) ? e : new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
    #endregion

    #region Extended Collection Methods
    protected override void ClearItems()
    {
        if( this.Count == 0 ) return;

        List<T> removed = new List<T>(this);
        _SuppressCollectionChanged = true;
        base.ClearItems();
        _SuppressCollectionChanged = false;
        OnCollectionChangedMultiItem(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
    }

    public void Add(IEnumerable<T> toAdd)
    {
        if( this == toAdd )
            throw new Exception("Invalid operation. This would result in iterating over a collection as it is being modified.");

        _SuppressCollectionChanged = true;
        foreach( T item in toAdd )
            Add(item);
        _SuppressCollectionChanged = false;
        OnCollectionChangedMultiItem(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<T>(toAdd)));
    }

    public void Remove(IEnumerable<T> toRemove)
    {
        if( this == toRemove )
            throw new Exception("Invalid operation. This would result in iterating over a collection as it is being modified.");

        _SuppressCollectionChanged = true;
        foreach( T item in toRemove )
            Remove(item);
        _SuppressCollectionChanged = false;
        OnCollectionChangedMultiItem(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new List<T>(toRemove)));
    }
    #endregion
}

9

Okay, ich weiß, dass dies eine sehr alte Frage ist, aber ich habe eine gute Lösung für das Problem gefunden und dachte, ich würde sie teilen. Diese Lösung wurde von vielen der großartigen Antworten hier inspiriert, bietet jedoch die folgenden Vorteile:

  • Sie müssen keine neue Klasse erstellen und Methoden aus ObservableCollection überschreiben
  • Manipuliert nicht die Funktionsweise von NotifyCollectionChanged (also kein Durcheinander mit Reset)
  • Nutzt keine Reflexion

Hier ist der Code:

 public static void Clear<T>(this ObservableCollection<T> collection, Action<ObservableCollection<T>> unhookAction)
 {
     unhookAction.Invoke(collection);
     collection.Clear();
 }

Diese Erweiterungsmethode verwendet einfach eine, Actiondie aufgerufen wird, bevor die Sammlung gelöscht wird.


Sehr schöne Idee. Einfach, elegant.
Plots

7

Ok, obwohl ich mir immer noch wünsche, dass sich ObservableCollection so verhält, wie ich es mir gewünscht habe ... der folgende Code ist das, was ich letztendlich getan habe. Grundsätzlich habe ich eine neue Sammlung von T mit dem Namen TrulyObservableCollection erstellt und die ClearItems-Methode überschrieben, mit der ich dann ein Clearing-Ereignis ausgelöst habe.

In dem Code, der diese TrulyObservableCollection verwendet, verwende ich dieses Clearing-Ereignis, um die Elemente zu durchlaufen, die sich zu diesem Zeitpunkt noch in der Sammlung befinden, um das Trennen für das Ereignis durchzuführen, von dem ich mich trennen wollte.

Hoffe, dieser Ansatz hilft auch jemand anderem.

public class TrulyObservableCollection<T> : ObservableCollection<T>
{
    public event EventHandler<EventArgs> Clearing;
    protected virtual void OnClearing(EventArgs e)
    {
        if (Clearing != null)
            Clearing(this, e);
    }

    protected override void ClearItems()
    {
        OnClearing(EventArgs.Empty);
        base.ClearItems();
    }
}

1
Sie müssen Ihre Klasse umbenennen BrokenObservableCollection, nicht TrulyObservableCollection- Sie verstehen falsch, was die Rücksetzaktion bedeutet.
Orion Edwards

1
@ Orion Edwards: Ich bin anderer Meinung. Siehe meinen Kommentar zu Ihrer Antwort.
Plots

1
@ Orion Edwards: Oh, warte, ich verstehe, du bist lustig. Aber dann sollte ich es wirklich so nennen : ActuallyUsefulObservableCollection. :)
Plots

6
Lol großer Name. Ich bin damit einverstanden, dass dies ein ernstes Versehen im Design ist.
Devios1

1
Wenn Sie ohnehin eine neue ObservableCollection-Klasse implementieren möchten, müssen Sie kein neues Ereignis erstellen, das separat überwacht werden muss. Sie können einfach verhindern, dass ClearItems Ereignisargumente mit Aktion = Zurücksetzen auslöst, und diese durch Ereignisargumente mit Aktion = Entfernen ersetzen, die eine Liste enthalten. E.OldItems aller Elemente in der Liste. Weitere Lösungen finden Sie in dieser Frage.
Alain

4

Ich habe dieses Problem auf eine etwas andere Art und Weise angegangen, da ich mich für ein Ereignis registrieren und alle Hinzufügungen und Entfernungen im Ereignishandler behandeln wollte. Ich habe damit begonnen, das von der Sammlung geänderte Ereignis zu überschreiben und Rücksetzaktionen auf Entfernungsaktionen mit einer Liste von Elementen umzuleiten. Dies alles ging schief, da ich die beobachtbare Sammlung als Elementquelle für eine Sammlungsansicht verwendete und "Bereichsaktionen nicht unterstützt" erhielt.

Ich habe endlich ein neues Ereignis namens CollectionChangedRange erstellt, das sich so verhält, wie ich es von der eingebauten Version erwartet hatte.

Ich kann mir nicht vorstellen, warum diese Einschränkung zulässig ist, und hoffe, dass dieser Beitrag zumindest andere davon abhält, in die Sackgasse zu geraten, die ich getan habe.

/// <summary>
/// An observable collection with support for addrange and clear
/// </summary>
/// <typeparam name="T"></typeparam>
[Serializable]
[TypeConverter(typeof(ExpandableObjectConverter))]
public class ObservableCollectionRange<T> : ObservableCollection<T>
{
    private bool _addingRange;

    [field: NonSerialized]
    public event NotifyCollectionChangedEventHandler CollectionChangedRange;

    protected virtual void OnCollectionChangedRange(NotifyCollectionChangedEventArgs e)
    {
        if ((CollectionChangedRange == null) || _addingRange) return;
        using (BlockReentrancy())
        {
            CollectionChangedRange(this, e);
        }
    }

    public void AddRange(IEnumerable<T> collection)
    {
        CheckReentrancy();
        var newItems = new List<T>();
        if ((collection == null) || (Items == null)) return;
        using (var enumerator = collection.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                _addingRange = true;
                Add(enumerator.Current);
                _addingRange = false;
                newItems.Add(enumerator.Current);
            }
        }
        OnCollectionChangedRange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItems));
    }

    protected override void ClearItems()
    {
        CheckReentrancy();
        var oldItems = new List<T>(this);
        base.ClearItems();
        OnCollectionChangedRange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItems));
    }

    protected override void InsertItem(int index, T item)
    {
        CheckReentrancy();
        base.InsertItem(index, item);
        OnCollectionChangedRange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
    }

    protected override void MoveItem(int oldIndex, int newIndex)
    {
        CheckReentrancy();
        var item = base[oldIndex];
        base.MoveItem(oldIndex, newIndex);
        OnCollectionChangedRange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, item, newIndex, oldIndex));
    }

    protected override void RemoveItem(int index)
    {
        CheckReentrancy();
        var item = base[index];
        base.RemoveItem(index);
        OnCollectionChangedRange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
    }

    protected override void SetItem(int index, T item)
    {
        CheckReentrancy();
        var oldItem = base[index];
        base.SetItem(index, item);
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, oldItem, item, index));
    }
}

/// <summary>
/// A read only observable collection with support for addrange and clear
/// </summary>
/// <typeparam name="T"></typeparam>
[Serializable]
[TypeConverter(typeof(ExpandableObjectConverter))]
public class ReadOnlyObservableCollectionRange<T> : ReadOnlyObservableCollection<T>
{
    [field: NonSerialized]
    public event NotifyCollectionChangedEventHandler CollectionChangedRange;

    public ReadOnlyObservableCollectionRange(ObservableCollectionRange<T> list) : base(list)
    {
        list.CollectionChangedRange += HandleCollectionChangedRange;
    }

    private void HandleCollectionChangedRange(object sender, NotifyCollectionChangedEventArgs e)
    {
        OnCollectionChangedRange(e);
    }

    protected virtual void OnCollectionChangedRange(NotifyCollectionChangedEventArgs args)
    {
        if (CollectionChangedRange != null)
        {
            CollectionChangedRange(this, args);
        }
    }

}

Interessanter Ansatz. Danke, dass du es gepostet hast. Wenn ich jemals auf Probleme mit meinem eigenen Ansatz stoße, denke ich, dass ich Ihren noch einmal besuchen werde.
Plots

3

So funktioniert ObservableCollection. Sie können dies umgehen, indem Sie Ihre eigene Liste außerhalb der ObservableCollection belassen (Hinzufügen zur Liste, wenn Aktion Hinzufügen ist, Entfernen, wenn Aktion Entfernen ist usw.). Anschließend können Sie alle entfernten Elemente (oder hinzugefügten Elemente) abrufen ) Wenn die Aktion zurückgesetzt wird, vergleichen Sie Ihre Liste mit der ObservableCollection.

Eine andere Möglichkeit besteht darin, eine eigene Klasse zu erstellen, die IList und INotifyCollectionChanged implementiert. Anschließend können Sie Ereignisse innerhalb dieser Klasse anhängen und trennen (oder OldItems auf Clear setzen, wenn Sie möchten) - es ist wirklich nicht schwierig, aber es ist viel Tippen.


Ich habe darüber nachgedacht, eine andere Liste im Auge zu behalten, wie Sie zuerst vorgeschlagen haben, aber es scheint eine Menge unnötiger Arbeit zu sein. Ihr zweiter Vorschlag kommt dem sehr nahe, was ich letztendlich gemacht habe ... was ich als Antwort posten werde.
Plots

3

Für das Szenario des Anhängens und Trennens von Ereignishandlern an die Elemente der ObservableCollection gibt es auch eine "clientseitige" Lösung. Im Ereignisbehandlungscode können Sie mithilfe der Contains-Methode überprüfen, ob sich der Absender in der ObservableCollection befindet. Pro: Sie können mit jeder vorhandenen ObservableCollection arbeiten. Nachteile: Die Contains-Methode wird mit O (n) ausgeführt, wobei n die Anzahl der Elemente in der ObservableCollection ist. Dies ist also eine Lösung für kleine ObservableCollections.

Eine andere "clientseitige" Lösung besteht darin, einen Ereignishandler in der Mitte zu verwenden. Registrieren Sie einfach alle Ereignisse im Ereignishandler in der Mitte. Dieser Ereignishandler wiederum benachrichtigt den realen Ereignishandler über einen Rückruf oder ein Ereignis. Wenn eine Rücksetzaktion auftritt, entfernen Sie den Rückruf oder das Ereignis. Erstellen Sie einen neuen Ereignishandler in der Mitte und vergessen Sie den alten. Dieser Ansatz funktioniert auch für große ObservableCollections. Ich habe dies für das PropertyChanged-Ereignis verwendet (siehe Code unten).

    /// <summary>
    /// Helper class that allows to "detach" all current Eventhandlers by setting
    /// DelegateHandler to null.
    /// </summary>
    public class PropertyChangedDelegator
    {
        /// <summary>
        /// Callback to the real event handling code.
        /// </summary>
        public PropertyChangedEventHandler DelegateHandler;
        /// <summary>
        /// Eventhandler that is registered by the elements.
        /// </summary>
        /// <param name="sender">the element that has been changed.</param>
        /// <param name="e">the event arguments</param>
        public void PropertyChangedHandler(Object sender, PropertyChangedEventArgs e)
        {
            if (DelegateHandler != null)
            {
                DelegateHandler(sender, e);
            }
            else
            {
                INotifyPropertyChanged s = sender as INotifyPropertyChanged;
                if (s != null)
                    s.PropertyChanged -= PropertyChangedHandler;
            }   
        }
    }

Ich glaube, bei Ihrem ersten Ansatz würde ich eine andere Liste benötigen, um die Elemente zu verfolgen ... denn sobald Sie das CollectionChanged-Ereignis mit der Aktion Zurücksetzen erhalten ... ist die Sammlung bereits leer. Ich folge Ihrem zweiten Vorschlag nicht ganz. Ich würde ein einfaches Testgeschirr lieben, das es illustriert, aber zum Hinzufügen, Entfernen und Löschen der ObservableCollection. Wenn Sie ein Beispiel erstellen, können Sie mir eine E-Mail mit meinem Vornamen und anschließend meinem Nachnamen unter gmail.com senden.
Plots

2

Bei Betrachtung der NotifyCollectionChangedEventArgs scheint OldItems nur Elemente zu enthalten, die aufgrund der Aktion Ersetzen, Entfernen oder Verschieben geändert wurden. Es zeigt nicht an, dass es irgendetwas auf Clear enthalten wird. Ich vermute, dass Clear das Ereignis auslöst, die entfernten Elemente jedoch nicht registriert und den Code zum Entfernen überhaupt nicht aufruft.


6
Ich habe das auch gesehen, aber ich mag es nicht. Es scheint mir ein klaffendes Loch zu sein.
Plots

Der Entfernungscode wird nicht aufgerufen, da dies nicht erforderlich ist. Zurücksetzen bedeutet "etwas Dramatisches ist passiert, Sie müssen erneut beginnen". Eine klare Operation ist ein Beispiel dafür, aber es gibt noch andere
Orion Edwards

2

Nun, ich habe beschlossen, mich selbst damit schmutzig zu machen.

Microsoft hat viel Arbeit investiert, um sicherzustellen, dass NotifyCollectionChangedEventArgs beim Aufrufen eines Resets keine Daten enthält. Ich gehe davon aus, dass dies eine Entscheidung über Leistung / Gedächtnis war. Wenn Sie eine Sammlung mit 100.000 Elementen zurücksetzen, gehe ich davon aus, dass sie nicht alle diese Elemente duplizieren wollten.

Da meine Sammlungen jedoch nie mehr als 100 Elemente enthalten, sehe ich kein Problem damit.

Wie auch immer, ich habe eine geerbte Klasse mit der folgenden Methode erstellt:

protected override void ClearItems()
{
    CheckReentrancy();
    List<TItem> oldItems = new List<TItem>(Items);

    Items.Clear();

    OnPropertyChanged(new PropertyChangedEventArgs("Count"));
    OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));

    NotifyCollectionChangedEventArgs e =
        new NotifyCollectionChangedEventArgs
        (
            NotifyCollectionChangedAction.Reset
        );

        FieldInfo field =
            e.GetType().GetField
            (
                "_oldItems",
                BindingFlags.Instance | BindingFlags.NonPublic
            );
        field.SetValue(e, oldItems);

        OnCollectionChanged(e);
    }

Das ist cool, würde aber wahrscheinlich nur in einer Umgebung mit vollem Vertrauen funktionieren. Das Nachdenken über private Felder erfordert volles Vertrauen, oder?
Paul

1
Wieso würdest du das machen? Es gibt andere Dinge, die dazu führen können, dass die Aktion "Zurücksetzen" ausgelöst wird - nur weil Sie die Löschmethode deaktiviert haben, heißt das nicht, dass sie weg ist (oder sollte)
Orion Edwards

Interessanter Ansatz, aber Reflexion kann langsam sein.
Plots

2

Die ObservableCollection- und die INotifyCollectionChanged-Schnittstelle sind klar für eine bestimmte Verwendung geschrieben: UI-Erstellung und ihre spezifischen Leistungsmerkmale.

Wenn Sie Benachrichtigungen über Sammlungsänderungen wünschen, sind Sie im Allgemeinen nur an Ereignissen zum Hinzufügen und Entfernen interessiert.

Ich benutze die folgende Schnittstelle:

using System;
using System.Collections.Generic;

/// <summary>
/// Notifies listeners of the following situations:
/// <list type="bullet">
/// <item>Elements have been added.</item>
/// <item>Elements are about to be removed.</item>
/// </list>
/// </summary>
/// <typeparam name="T">The type of elements in the collection.</typeparam>
interface INotifyCollection<T>
{
    /// <summary>
    /// Occurs when elements have been added.
    /// </summary>
    event EventHandler<NotifyCollectionEventArgs<T>> Added;

    /// <summary>
    /// Occurs when elements are about to be removed.
    /// </summary>
    event EventHandler<NotifyCollectionEventArgs<T>> Removing;
}

/// <summary>
/// Provides data for the NotifyCollection event.
/// </summary>
/// <typeparam name="T">The type of elements in the collection.</typeparam>
public class NotifyCollectionEventArgs<T> : EventArgs
{
    /// <summary>
    /// Gets or sets the elements.
    /// </summary>
    /// <value>The elements.</value>
    public IEnumerable<T> Items
    {
        get;
        set;
    }
}

Ich habe auch meine eigene Überladung von Collection geschrieben, wo:

  • ClearItems löst das Entfernen aus
  • InsertItem löst Added aus
  • RemoveItem löst Removing aus
  • SetItem löst das Entfernen und Hinzufügen aus

Natürlich kann auch AddRange hinzugefügt werden.


+1 für den Hinweis, dass Microsoft ObservableCollection für einen bestimmten Anwendungsfall entwickelt hat ... und die Leistung im Auge behält. Genau. Hat ein Loch für andere Situationen hinterlassen, aber ich stimme zu.
Plots

-1 Ich könnte an allen möglichen Dingen interessiert sein. Oft brauche ich den Index der hinzugefügten / entfernten Elemente. Ich möchte vielleicht ersetzen ersetzen. Das Design von INotifyCollectionChanged ist gut. Das Problem, das behoben werden sollte, ist niemand bei MS implementiert.
Aleksandr Dubinsky

1

Ich habe gerade einen Teil des Diagrammcodes in den Silverlight- und WPF-Toolkits durchgesehen und festgestellt, dass sie auch dieses Problem gelöst haben (auf ähnliche Weise) ... und ich dachte, ich würde fortfahren und ihre Lösung veröffentlichen.

Grundsätzlich haben sie auch eine abgeleitete ObservableCollection erstellt und ClearItems überschrieben, wobei bei jedem zu löschenden Element Remove aufgerufen wurde.

Hier ist der Code:

/// <summary>
/// An observable collection that cannot be reset.  When clear is called
/// items are removed individually, giving listeners the chance to detect
/// each remove event and perform operations such as unhooking event 
/// handlers.
/// </summary>
/// <typeparam name="T">The type of item in the collection.</typeparam>
public class NoResetObservableCollection<T> : ObservableCollection<T>
{
    public NoResetObservableCollection()
    {
    }

    /// <summary>
    /// Clears all items in the collection by removing them individually.
    /// </summary>
    protected override void ClearItems()
    {
        IList<T> items = new List<T>(this);
        foreach (T item in items)
        {
            Remove(item);
        }
    }
}

Ich möchte nur darauf hinweisen, dass mir dieser Ansatz nicht so gut gefällt wie der, den ich als Antwort markiert habe ... da Sie ein NotifyCollectionChanged-Ereignis (mit einer Aktion zum Entfernen) erhalten ... für JEDES Element, das entfernt wird.
Plots

1

Dies ist ein heißes Thema ... weil Microsoft meiner Meinung nach seine Arbeit nicht richtig gemacht hat ... noch einmal. Versteht mich nicht falsch, ich mag Microsoft, aber sie sind nicht perfekt!

Ich habe die meisten vorherigen Kommentare gelesen. Ich stimme allen zu, die der Meinung sind, dass Microsoft Clear () nicht richtig programmiert hat.

Zumindest meiner Meinung nach braucht es ein Argument, um Objekte von einem Ereignis trennen zu können ... aber ich verstehe auch die Auswirkungen. Dann habe ich mir diese vorgeschlagene Lösung ausgedacht.

Ich hoffe, es wird alle glücklich machen, oder zumindest die meisten ...

Eric

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Reflection;

namespace WpfUtil.Collections
{
    public static class ObservableCollectionExtension
    {
        public static void RemoveAllOneByOne<T>(this ObservableCollection<T> obsColl)
        {
            foreach (T item in obsColl)
            {
                while (obsColl.Count > 0)
                {
                    obsColl.RemoveAt(0);
                }
            }
        }

        public static void RemoveAll<T>(this ObservableCollection<T> obsColl)
        {
            if (obsColl.Count > 0)
            {
                List<T> removedItems = new List<T>(obsColl);
                obsColl.Clear();

                NotifyCollectionChangedEventArgs e =
                    new NotifyCollectionChangedEventArgs
                    (
                        NotifyCollectionChangedAction.Remove,
                        removedItems
                    );
                var eventInfo =
                    obsColl.GetType().GetField
                    (
                        "CollectionChanged",
                        BindingFlags.Instance | BindingFlags.NonPublic
                    );
                if (eventInfo != null)
                {
                    var eventMember = eventInfo.GetValue(obsColl);
                    // note: if eventMember is null
                    // nobody registered to the event, you can't call it.
                    if (eventMember != null)
                        eventMember.GetType().GetMethod("Invoke").
                            Invoke(eventMember, new object[] { obsColl, e });
                }
            }
        }
    }
}

Ich denke immer noch, dass Microsoft eine Möglichkeit bieten sollte, mit Benachrichtigung zu löschen. Ich denke immer noch, dass sie den Schuss verpassen, wenn sie nicht so vorgehen. Es tut uns leid ! Ich sage nicht, dass klar entfernt werden sollte, da etwas fehlt !!! Um eine niedrige Kopplung zu erreichen, müssen wir manchmal darüber informiert werden, was entfernt wurde.
Eric Ouellet

1

Um es einfach zu halten, warum überschreiben Sie nicht die ClearItem-Methode und tun dort, was Sie wollen, dh trennen Sie die Elemente vom Ereignis.

public class PeopleAttributeList : ObservableCollection<PeopleAttributeDto>,    {
{
  protected override void ClearItems()
  {
    Do what ever you want
    base.ClearItems();
  }

  rest of the code omitted
}

Einfach, sauber und im Sammlungscode enthalten.


Das kommt dem sehr nahe, was ich tatsächlich getan habe ... siehe die akzeptierte Antwort.
Plots

0

Ich hatte das gleiche Problem und dies war meine Lösung. Es scheint zu funktionieren. Hat jemand mögliche Probleme mit diesem Ansatz?

// overriden so that we can call GetInvocationList
public override event NotifyCollectionChangedEventHandler CollectionChanged;

protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
    NotifyCollectionChangedEventHandler collectionChanged = CollectionChanged;
    if (collectionChanged != null)
    {
        lock (collectionChanged)
        {
            foreach (NotifyCollectionChangedEventHandler handler in collectionChanged.GetInvocationList())
            {
                try
                {
                    handler(this, e);
                }
                catch (NotSupportedException ex)
                {
                    // this will occur if this collection is used as an ItemsControl.ItemsSource
                    if (ex.Message == "Range actions are not supported.")
                    {
                        handler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
                    }
                    else
                    {
                        throw ex;
                    }
                }
            }
        }
    }
}

Hier sind einige andere nützliche Methoden in meiner Klasse:

public void SetItems(IEnumerable<T> newItems)
{
    Items.Clear();
    foreach (T newItem in newItems)
    {
        Items.Add(newItem);
    }
    NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}

public void AddRange(IEnumerable<T> newItems)
{
    int index = Count;
    foreach (T item in newItems)
    {
        Items.Add(item);
    }
    NotifyCollectionChangedEventArgs e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<T>(newItems), index);
    NotifyCollectionChanged(e);
}

public void RemoveRange(int startingIndex, int count)
{
    IList<T> oldItems = new List<T>();
    for (int i = 0; i < count; i++)
    {
        oldItems.Add(Items[startingIndex]);
        Items.RemoveAt(startingIndex);
    }
    NotifyCollectionChangedEventArgs e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new List<T>(oldItems), startingIndex);
    NotifyCollectionChanged(e);
}

// this needs to be overridden to avoid raising a NotifyCollectionChangedEvent with NotifyCollectionChangedAction.Reset, which our other lists don't support
new public void Clear()
{
    RemoveRange(0, Count);
}

public void RemoveWhere(Func<T, bool> criterion)
{
    List<T> removedItems = null;
    int startingIndex = default(int);
    int contiguousCount = default(int);
    for (int i = 0; i < Count; i++)
    {
        T item = Items[i];
        if (criterion(item))
        {
            if (removedItems == null)
            {
                removedItems = new List<T>();
                startingIndex = i;
                contiguousCount = 0;
            }
            Items.RemoveAt(i);
            removedItems.Add(item);
            contiguousCount++;
        }
        else if (removedItems != null)
        {
            NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItems, startingIndex));
            removedItems = null;
            i = startingIndex;
        }
    }
    if (removedItems != null)
    {
        NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItems, startingIndex));
    }
}

private void NotifyCollectionChanged(NotifyCollectionChangedEventArgs e)
{
    OnPropertyChanged(new PropertyChangedEventArgs("Count"));
    OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
    OnCollectionChanged(e);
}

0

Ich habe eine andere "einfache" Lösung gefunden, die von ObservableCollection abgeleitet ist, aber sie ist nicht sehr elegant, weil sie Reflection verwendet ... Wenn es Ihnen gefällt, ist hier meine Lösung:

public class ObservableCollectionClearable<T> : ObservableCollection<T>
{
    private T[] ClearingItems = null;

    protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case System.Collections.Specialized.NotifyCollectionChangedAction.Reset:
                if (this.ClearingItems != null)
                {
                    ReplaceOldItems(e, this.ClearingItems);
                    this.ClearingItems = null;
                }
                break;
        }
        base.OnCollectionChanged(e);
    }

    protected override void ClearItems()
    {
        this.ClearingItems = this.ToArray();
        base.ClearItems();
    }

    private static void ReplaceOldItems(System.Collections.Specialized.NotifyCollectionChangedEventArgs e, T[] olditems)
    {
        Type t = e.GetType();
        System.Reflection.FieldInfo foldItems = t.GetField("_oldItems", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        if (foldItems != null)
        {
            foldItems.SetValue(e, olditems);
        }
    }
}

Hier speichere ich die aktuellen Elemente in einem Array-Feld in der ClearItems-Methode, fange dann den Aufruf von OnCollectionChanged ab und überschreibe das private Feld e._oldItems (über Reflections), bevor ich base.OnCollectionChanged starte


0

Sie können die ClearItems-Methode überschreiben und das Ereignis mit Remove action und OldItems auslösen.

public class ObservableCollection<T> : System.Collections.ObjectModel.ObservableCollection<T>
{
    protected override void ClearItems()
    {
        CheckReentrancy();
        var items = Items.ToList();
        base.ClearItems();
        OnPropertyChanged(new PropertyChangedEventArgs("Count"));
        OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, items, -1));
    }
}

Teil der System.Collections.ObjectModel.ObservableCollection<T>Realisierung:

public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
    protected override void ClearItems()
    {
        CheckReentrancy();
        base.ClearItems();
        OnPropertyChanged(CountString);
        OnPropertyChanged(IndexerName);
        OnCollectionReset();
    }

    private void OnPropertyChanged(string propertyName)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    private void OnCollectionReset()
    {
        OnCollectionChanged(new   NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    private const string CountString = "Count";

    private const string IndexerName = "Item[]";
}

-4

http://msdn.microsoft.com/en-us/library/system.collections.specialized.notifycollectionchangedaction(VS.95).aspx

Bitte lesen Sie diese Dokumentation mit offenen Augen und eingeschaltetem Gehirn. Microsoft hat alles richtig gemacht. Sie müssen Ihre Sammlung erneut scannen, wenn eine Reset-Benachrichtigung für Sie ausgelöst wird. Sie erhalten eine Reset-Benachrichtigung, da das Auslösen von Hinzufügen / Entfernen für jedes Element (das aus der Sammlung entfernt und wieder hinzugefügt wird) zu teuer ist.

Orion Edwards hat vollkommen recht (Respekt, Mann). Bitte denken Sie beim Lesen der Dokumentation weiter nach.


5
Ich denke tatsächlich, dass Sie und Orion richtig verstehen, wie Microsoft es entwickelt hat. :) Dieses Design verursachte mir jedoch Probleme, die ich für meine Situation umgehen musste. Diese Situation ist auch häufig ... und warum ich diese Frage gestellt habe.
Plots

Ich denke, Sie sollten sich meine Frage (und die markierte Antwort) etwas genauer ansehen. Ich habe nicht vorgeschlagen, für jeden Artikel zu entfernen.
Plots

Und fürs Protokoll, ich respektiere Orions Antwort ... Ich denke, wir hatten nur ein bisschen Spaß miteinander ... zumindest habe ich das so aufgenommen.
Plots

Eine wichtige Sache: Sie müssen keine Ereignisbehandlungsverfahren von Objekten trennen, die Sie entfernen. Die Ablösung erfolgt automatisch.
Dima

1
Zusammenfassend lässt sich sagen, dass Ereignisse beim Entfernen eines Objekts aus einer Sammlung nicht automatisch getrennt werden.
Plots

-4

Wenn Ihr ObservableCollectionnicht klar wird, können Sie diesen Code unten versuchen. es kann Ihnen helfen:

private TestEntities context; // This is your context

context.Refresh(System.Data.Objects.RefreshMode.StoreWins, context.UserTables); // to refresh the object context
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.