2012-11-09 11 views
31

के एक संग्रह को जोड़ने से मैं ObservableCollection<T> संग्रह है, और मैं, मैं कर सकता तत्वों का एक नया संग्रह के साथ सभी तत्वों को बदलना चाहते हैं:फ़ायरिंग कैसे बचें कई बार ObservableCollection.CollectionChanged जब सभी तत्वों की जगह या तत्वों

collection.Clear(); 

या:

collection.ClearItems(); 

मैं भीइस्तेमाल कर सकते हैं (btw, इन दो विधियों के बीच अंतर क्या है?)से collection.Add एक-एक करके, लेकिन यह कई बार

तत्वों का संग्रह जोड़ते समय भी आग लग जाएगा।

संपादित करें:

मैं एक अच्छा पुस्तकालय मिली: Enhanced ObservableCollection with ability to delay or disable notifications लेकिन ऐसा लगता है कि यह Silverlight समर्थन नहीं करता।

+0

यदि आप अन्य संग्रहों का उपयोग करके सभी संग्रहों को पहले संग्रह में बदलना चाहते हैं, तो बस नए को सीधे एक को असाइन नहीं कर सकते हैं? – m4ngl3r

+2

यदि आप ऐसा करते हैं, तो संग्रह स्वयं ही बदला जाएगा, इस स्थिति में आप संग्रह के संदर्भ को खो देंगे, यानी, आपका संग्रह बदल दिया गया ईवेंट चला गया है। –

+1

उस ईवेंट हैंडलर को फिर से सौंपें? – m4ngl3r

उत्तर

42

कॉलिन अपने सभी सूचनाओं के साथ सही है। मैं केवल ObservableCollection का अपना सबक्लास जोड़ना चाहता हूं जिसका उपयोग मैं इस विशिष्ट मामले के लिए करता हूं।

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

+1 गिनती और वस्तुओं के लिए संपत्ति को बढ़ाने के लिए याद रखने के लिए +1 [] - मुझे अपने स्वयं के प्रोजेक्ट्स में उपयोग किए जाने वाले कोड को अपडेट करना होगा ;-) – ColinE

+0

@ जेहोफ: क्या यह ठीक है कि आप अपने कोड को काटकर चिपकाएं एक प्रोजेक्ट। या यह केवल संदर्भ के लिए है? मैं यहां अनुमति मांग रहा हूं। –

+1

@AdrianRatnapala इसका उपयोग करने के लिए स्वतंत्र है। का आनंद लें। यह भी देखें [यह] (http://creativecommons.org/licenses/by-sa/3.0/) और [यह] (http://blog.stackoverflow.com/2009/06/attribution-required/) – Jehof

7

आप इसे ObservableCollection उपclassing और अपने ReplaceAll विधि को लागू करके प्राप्त कर सकते हैं। इस विधियों के कार्यान्वयन में सभी वस्तुओं को आंतरिक Items संपत्ति के भीतर बदल दिया जाएगा, फिर CollectionChanged ईवेंट को आग लगें। इसी प्रकार, आप AddRange विधि जोड़ सकते हैं। इस के एक कार्यान्वयन के लिए, इस सवाल का जवाब देखें:

ObservableCollection Doesn't support AddRange method, so I get notified for each item added, besides what about INotifyCollectionChanging?

Collection.Clear और Collection.ClearItems के बीच का अंतर Clear एक सार्वजनिक एपीआई विधि है, जबकि ClearItems सुरक्षित है है, यह एक विस्तार मुद्दा यह है कि अनुमति देता है आपके Clear के व्यवहार को बढ़ाने/संशोधित करने के लिए।

4

यहाँ है कि मैं क्या अन्य लोगों 'संदर्भ के लिए लागू किया है:

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

मैं पिछले उत्तरों के अभी तक टिप्पणी नहीं कर सकता, इसलिए मैं यहाँ है कि ऊपर SmartCollection कार्यान्वयन की एक RemoveRange अनुकूलन जोड़ रहा एक सी # अवैध ओपेरेशन अपवाद नहीं फेंक देगा: संग्रह संशोधित किया गया था। यह जांचने के लिए एक अनुमान का उपयोग करता है कि आइटम को हटाया जाना चाहिए, जो मेरे मामले में, हटाए गए मानदंडों को पूरा करने वाले आइटमों का सबसेट बनाने से अधिक अनुकूल है।

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

उदाहरण:

LogEntries.RemoveRange(i => closeFileIndexes.Contains(i.fileIndex)); 
0

पिछले कुछ वर्षों से मैं एक अधिक सामान्य समाधान का उपयोग कर रहा एक बैच परिवर्तन आपरेशन बनाने और कार्रवाई रीसेट के साथ पर्यवेक्षकों को सूचित करके भी कई ObservableCollection अधिसूचना समाप्त करने के लिए:

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

का उपयोग करते हुए यह स्पष्ट है:

var collection = new ExtendedObservableCollection<string>(new[] 
{ 
    "Test", 
    "Items", 
    "Here" 
}); 
collection.Execute(items => { 
    items.RemoveAt(1); 
    items.Insert(1, "Elements"); 
    items.Add("and there"); 
}); 

कॉलिंग निष्पादन एक ही अधिसूचना उत्पन्न करेगा लेकिन एक दोष के साथ - सूची पूरी तरह से यूआई में अपडेट की जाएगी, न केवल संशोधित तत्वों। यह वस्तुओं के लिए यह सही बनाता है। क्लीयर() आइटम के बाद। AddRange (newItems)।

संबंधित मुद्दे