19

MVVM データ バインディングを使用する WPF アプリケーションがあります。私はObservableCollection<...>実際にそれらの多くにアイテムを追加しています。

コレクションに追加するたびに、即座にイベントが発生し、不要なオーバーヘッドが発生するのだろうか? もしそうなら、どうにかしてイベント通知を一時的に無効にし、コードの最後で手動で 1 回起動して、1 万回ではなく 1 回だけ起動されるようにすることはできますか?

更新:私はこのクラスを持ってみました:

using System;
using System.Linq;
using System.Collections.Specialized;
using System.Collections.Generic;

namespace MyProject
{

    /// <summary> 
    /// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    public class ObservableCollection<T> : System.Collections.ObjectModel.ObservableCollection<T>
    {

        /// <summary> 
        /// Adds the elements of the specified collection to the end of the ObservableCollection(Of T). 
        /// </summary> 
        public void AddRange(IEnumerable<T> collection)
        {
            foreach (var i in collection) Items.Add(i);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, collection.ToList()));
        }

        /// <summary> 
        /// Removes the first occurence of each item in the specified collection from ObservableCollection(Of T). 
        /// </summary> 
        public void RemoveRange(IEnumerable<T> collection)
        {
            foreach (var i in collection) Items.Remove(i);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, collection.ToList()));
        }

        /// <summary> 
        /// Clears the current collection and replaces it with the specified item. 
        /// </summary> 
        public void Replace(T item)
        {
            ReplaceRange(new T[] { item });
        }
        /// <summary> 
        /// Clears the current collection and replaces it with the specified collection. 
        /// </summary> 
        public void ReplaceRange(IEnumerable<T> collection)
        {
            List<T> old = new List<T>(Items);
            Items.Clear();
            foreach (var i in collection) Items.Add(i);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, collection.ToList()));
        }

        /// <summary> 
        /// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class. 
        /// </summary> 
        public ObservableCollection() : base() { }

        /// <summary> 
        /// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection. 
        /// </summary> 
        /// <param name="collection">collection: The collection from which the elements are copied.</param> 
        /// <exception cref="System.ArgumentNullException">The collection parameter cannot be null.</exception> 
        public ObservableCollection(IEnumerable<T> collection) : base(collection) { }
    }
}

私は今、このエラーが発生します:

追加情報: 範囲アクションはサポートされていません。

エラーはここに来ます:

OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, collection.ToList()));
4

5 に答える 5

23

この ObservableCollection の拡張により、問題は簡単に解決されます。

これはパブリックの SupressNotification プロパティを公開して、CollectionChanged 通知が抑制されるタイミングをユーザーが制御できるようにします。

範囲の挿入/削除は提供されませんが、CollectionChanged 通知が抑制されると、ほとんどの場合、コレクションで範囲操作を行う必要がなくなります。

この実装は、抑制されたすべての通知をリセット通知に置き換えます。これは論理的に理にかなっています。ユーザーが通知を抑制し、一括変更を行ってから再度有効にする場合は、再送信通知を送信するのが適切です。

public class ObservableCollectionEx<T> : ObservableCollection<T>
{
    private bool _notificationSupressed = false;
    private bool _supressNotification = false;
    public bool SupressNotification
    {
        get
        {
            return _supressNotification;
        }
        set
        {
            _supressNotification = value;
            if (_supressNotification == false && _notificationSupressed)
            {
                this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
                _notificationSupressed = false;
            }
        }
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (SupressNotification)
        {
            _notificationSupressed = true;
            return;
        }
        base.OnCollectionChanged(e);
    }
}
于 2015-04-28T05:08:01.123 に答える
18

非常に迅速かつ簡単な方法は、ObservableCollection をサブクラス化し、AddRange が呼び出されたときに通知を一時停止することです。明確にするために、次のブログ投稿を参照してください。

于 2012-05-13T15:34:59.663 に答える
8

一種の「トリッキーな」方法がありますが、私の意見では、これを達成するためにかなり正確です。あなた自身を書いて、取り扱いObservableCollectionを実行することです。AddRange

このようにして、すべての10k要素を「ホルダーコレクション」に追加し、終了したら、を使用AddRangeしてそれを行うことができます ObservableColleciton

これについての詳細は、このリンクで見つけることができます:

ObservableCollectionはAddRangeメソッドをサポートしていません...。

またはこれも

AddRangeとObservableCollection

于 2012-05-13T14:32:43.353 に答える
0

申し訳ありませんが、完全な実装の詳細を提供しないため、これをコメントとして投稿したかったのですが、少し長すぎます。

「サポートされていない範囲アクション」については、これはListCollectionViewWPF がバインディングに使用している から来ていますが、これは実際には範囲アクションをサポートしていません。ただし、通常CollectionViewはそうです。

WPFListCollectionViewは、バインドされたコレクションが非ジェネリックIListインターフェイスを実装するときに使用することを選択します。したがって、基本的にソリューションを機能させるには、 ObservableCollection を (インターライトするのではなく)完全にAddRange再実装する必要がありますが、非ジェネリック インターフェイスは使用しません。

public class MyObservableCollection<T> :
    IList<T>,
    IReadOnlyList<T>,
    INotifyCollectionChanged,
    INotifyPropertyChanged
{
   // ...
}

dotPeek または同等のツールを使用すれば、これを実装するのにそれほど時間はかかりません。CollectionViewa の代わりに aを使用するという事実から、おそらくいくつかの最適化を失っていることに注意してください。ListCollectionViewしかし、私自身の経験から、そのようなクラスをグローバルに使用すると、パフォーマンスが完全に改善されました。

于 2018-04-05T03:03:03.037 に答える