77 votes

RemoveAll pour ObservableCollections ?

Je cherche une méthode Linq (comme la méthode RemoveAll pour la liste) qui permet de supprimer les éléments sélectionnés de mon ObservableCollection.

Je suis trop nouveau pour créer une méthode d'extension pour moi-même. Existe-t-il un moyen de supprimer des éléments d'ObservableCollection en passant par une expression Lambda ?

0 votes

109voto

Daniel Hilgarth Points 90722

Je n'ai pas connaissance d'un moyen de supprimer uniquement les éléments sélectionnés. Mais la création d'une méthode d'extension est simple :

public static class ExtensionMethods
{
    public static int Remove<T>(
        this ObservableCollection<T> coll, Func<T, bool> condition)
    {
        var itemsToRemove = coll.Where(condition).ToList();

        foreach (var itemToRemove in itemsToRemove)
        {
            coll.Remove(itemToRemove);
        }

        return itemsToRemove.Count;
    }
}

Cela supprime tous les éléments de la ObservableCollection qui correspondent à la condition. Vous pouvez l'appeler comme ça :

var c = new ObservableCollection<SelectableItem>();
c.Remove(x => x.IsSelected);

0 votes

Est-il nécessaire d'ajouter itemsToRemove.Clear() avant l'instruction de retour ?

0 votes

@JoanComasFdz : Non. itemstoRemove sera réclamé par le ramasseur d'ordures.

1 votes

@ShaktiPrakashSingh : Bons points. Renvoyer une collection indique que l'original est inchangé et qu'une nouvelle collection a été créée. Comme vous le soulignez, ce n'est pas le cas. En outre, la méthode sur List<T> renvoie le compte, ce serait donc une bonne idée de s'y conformer. J'ai modifié ma réponse en conséquence. Merci pour vos commentaires.

53voto

guams Points 81

L'itération en arrière devrait être plus efficace que la création d'une collection temporaire comme dans la méthode de Daniel Hilgarth. exemple .

public static class ObservableCollectionExtensions
{
    public static void RemoveAll<T>(this ObservableCollection<T> collection,
                                                       Func<T, bool> condition)
    {
        for (int i = collection.Count - 1; i >= 0; i--)
        {
            if (condition(collection[i]))
            {
                collection.RemoveAt(i);
            }
        }
    }
}

2 votes

Dans une application réelle en cours de test, j'ai constaté que cette méthode était effectivement plus rapide que la collecte temporaire - temps moyen de 44 ms et pic de 62 ms par rapport à la collecte temporaire avec un temps moyen de 55 ms et un pic de 93 ms.

10voto

psulek Points 984

Chacune des solutions proposées ici, qui utilise une routine pour retirer les articles un par un, présente un défaut. Imaginez que vous avez beaucoup d'éléments dans une collection observable, disons 10.000 éléments. Vous souhaitez alors supprimer les éléments qui remplissent certaines conditions.

Si vous utilisez solution de Daniel Hilgarth et appeler : c.Remove(x => x.IsSelected); et qu'il y a par exemple 3000 éléments à supprimer, la solution proposée notifiera la suppression de chaque élément. Ceci est dû au fait que l'implémentation interne de Remove(item) notifier ce changement. Et ceci sera appelé pour chacun des 3000 éléments du processus de suppression.

Au lieu de cela, j'ai créé un descendant de ObservableCollection et ajouté une nouvelle méthode RemoveAll(predicate)

[Serializable]
public class ObservableCollectionExt<T> : ObservableCollection<T>
{
    public void RemoveAll(Predicate<T> predicate)
    {
        CheckReentrancy();

        List<T> itemsToRemove = Items.Where(x => predicate(x)).ToList();
        itemsToRemove.ForEach(item => Items.Remove(item));

        OnPropertyChanged(new PropertyChangedEventArgs("Count"));
        OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
}

La ligne intéressante est itemsToRemove.ForEach(item => Items.Remove(item)); . Appeler directement Items.Remove(item) ne notifiera pas le retrait de l'article.

Au lieu de cela, après avoir retiré les éléments requis, les changements sont notifiés immédiatement par des appels :

OnPropertyChanged(new PropertyChangedEventArgs("Count"));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));

0 votes

Note WPF : Lorsque vous utilisez cette fonction avec ItemsControl, le contrôle appelle Clear en interne lors de l'utilisation de NotifyCollectionChangedAction.Reset ; cela a entraîné des effets secondaires indésirables dans mon cas d'utilisation spécifique lorsque j'avais des animations sur les éléments nouvellement ajoutés. referencesource.microsoft.com/PresentationFramework/R/

2 votes

Dans une application réelle testée, j'ai constaté que cette méthode était en fait plus lente que la collection temporaire - temps moyen de 67 ms et pic de 152 ms par rapport à la collection temporaire avec un temps moyen de 55 ms et un pic de 93 ms. Gardez à l'esprit qu'il ne s'agissait que d'une petite collection.

1voto

Ricardo Polo Points 2056

Je veux supprimer tous les objets d'une collection Observable Utiliser

YourObservableCollection.Clear();

1voto

jjrdk Points 1224

Il n'est pas possible de passer une expression à ObservableCollection pour supprimer les éléments correspondants, comme c'est le cas pour une liste générique. ObservableCollection ajoute et supprime un élément à la fois.

Pour ce faire, vous devrez créer votre propre implémentation de INotifyCollectionChanged ou, comme vous l'avez mentionné, créer une méthode d'extension.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X