J'ai ObservableCollection<T>
collection, et je veux remplacer tous les éléments par une nouvelle collection d'éléments, je pourrais faire:
collection.Clear();
OU:
collection.ClearItems();
(BTW, quelle est la différence entre ces deux méthodes?)
Je pourrais également utiliser foreach
pour collection.Add
un par un, mais cela se déclenchera plusieurs fois
Idem lors de l'ajout d'une collection d'éléments.
ÉDITER:
J'ai trouvé une bonne bibliothèque ici: Enhanced ObservableCollection avec possibilité de retarder ou de désactiver les notifications mais il semble qu'il ne prend pas en charge Silverlight.
ColinE a raison avec toutes ses informations. Je veux seulement ajouter ma sous-classe de ObservableCollection
que j'utilise pour ce cas spécifique.
public class SmartCollection<T> : ObservableCollection<T> {
public SmartCollection()
: base() {
}
public SmartCollection(IEnumerable<T> collection)
: base(collection) {
}
public SmartCollection(List<T> list)
: base(list) {
}
public void AddRange(IEnumerable<T> range) {
foreach (var item in range) {
Items.Add(item);
}
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void Reset(IEnumerable<T> range) {
this.Items.Clear();
AddRange(range);
}
}
Vous pouvez y parvenir en sous-classant ObservableCollection
et en implémentant votre propre méthode ReplaceAll
. L'implémentation de ces méthodes remplacerait tous les éléments de la propriété interne Items
, puis déclencherait un événement CollectionChanged
. De même, vous pouvez ajouter une méthode AddRange
. Pour une implémentation de ceci, voir la réponse à cette question:
La différence entre Collection.Clear
et Collection.ClearItems
est que Clear
est une méthode d'API publique, tandis que ClearItems
est protégé, c'est un point d'extension qui vous permet d'étendre/modifier le comportement de Clear
.
Voici ce que j'ai implémenté pour la référence d'autres personnes:
// http://stackoverflow.com/questions/13302933/how-to-avoid-firing-observablecollection-collectionchanged-multiple-times-when-r
// http://stackoverflow.com/questions/670577/observablecollection-doesnt-support-addrange-method-so-i-get-notified-for-each
public class ObservableCollectionFast<T> : ObservableCollection<T>
{
public ObservableCollectionFast()
: base()
{
}
public ObservableCollectionFast(IEnumerable<T> collection)
: base(collection)
{
}
public ObservableCollectionFast(List<T> list)
: base(list)
{
}
public virtual void AddRange(IEnumerable<T> collection)
{
if (collection.IsNullOrEmpty())
return;
foreach (T item in collection)
{
this.Items.Add(item);
}
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
// Cannot use NotifyCollectionChangedAction.Add, because Constructor supports only the 'Reset' action.
}
public virtual void RemoveRange(IEnumerable<T> collection)
{
if (collection.IsNullOrEmpty())
return;
bool removed = false;
foreach (T item in collection)
{
if (this.Items.Remove(item))
removed = true;
}
if (removed)
{
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
// Cannot use NotifyCollectionChangedAction.Remove, because Constructor supports only the 'Reset' action.
}
}
public virtual void Reset(T item)
{
this.Reset(new List<T>() { item });
}
public virtual void Reset(IEnumerable<T> collection)
{
if (collection.IsNullOrEmpty() && this.Items.IsNullOrEmpty())
return;
// Step 0: Check if collection is exactly same as this.Items
if (IEnumerableUtils.Equals<T>(collection, this.Items))
return;
int count = this.Count;
// Step 1: Clear the old items
this.Items.Clear();
// Step 2: Add new items
if (!collection.IsNullOrEmpty())
{
foreach (T item in collection)
{
this.Items.Add(item);
}
}
// Step 3: Don't forget the event
if (this.Count != count)
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
Je ne peux pas encore commenter les réponses précédentes, donc j'ajoute ici une adaptation RemoveRange des implémentations SmartCollection ci-dessus qui ne lèvera pas une exception C # InvalidOperationException: Collection Was Modified. Il utilise un prédicat pour vérifier si l'élément doit être supprimé, ce qui, dans mon cas, est plus optimal que la création d'un sous-ensemble d'éléments qui répondent aux critères de suppression.
public void RemoveRange(Predicate<T> remove)
{
// iterates backwards so can remove multiple items without invalidating indexes
for (var i = Items.Count-1; i > -1; i--) {
if (remove(Items[i]))
Items.RemoveAt(i);
}
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
Exemple:
LogEntries.RemoveRange(i => closeFileIndexes.Contains(i.fileIndex));
Au cours des dernières années, j'utilise une solution plus générique pour éliminer trop de notifications ObservableCollection en créant une opération de modification par lots et en notifiant les observateurs avec une action de réinitialisation:
public class ExtendedObservableCollection<T>: ObservableCollection<T>
{
public ExtendedObservableCollection()
{
}
public ExtendedObservableCollection(IEnumerable<T> items)
: base(items)
{
}
public void Execute(Action<IList<T>> itemsAction)
{
itemsAction(Items);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
Son utilisation est simple:
var collection = new ExtendedObservableCollection<string>(new[]
{
"Test",
"Items",
"Here"
});
collection.Execute(items => {
items.RemoveAt(1);
items.Insert(1, "Elements");
items.Add("and there");
});
Appeler Execute générera une notification unique mais avec un inconvénient - la liste sera mise à jour dans l'interface utilisateur dans son ensemble, pas seulement les éléments modifiés. Cela le rend parfait pour les éléments.Clear () suivi par items.AddRange (newItems).