जब एक वेधशाला को साफ़ करते हैं, तो E.OldItems में कोई आइटम नहीं होते हैं


91

मैं यहाँ कुछ है कि वास्तव में मुझे गार्ड से पकड़ रहा है।

मेरे पास T का एक ऑब्ज़र्वेबल कॉलेक्शन है जो वस्तुओं से भरा है। मेरे पास एक इवेंट हैंडलर भी है जो कलेक्शन इवेंट से जुड़ा हुआ है।

जब आप संग्रह को साफ़ करते हैं , तो यह संग्रह के साथ एक संग्रहित घटना का कारण बनता है। ई। सेट के साथ NotifyCollectionChangedAction.Retet। ठीक है, यह सामान्य है। लेकिन अजीब बात यह है कि न तो e.OldItems या e.NewItems में कुछ भी नहीं है। मुझे उम्मीद है कि संग्रह से हटाए गए सभी आइटमों से e.ldItems भरा जाएगा।

क्या किसी और ने इसे देखा है? और यदि हां, तो वे इसके चारों ओर कैसे गए हैं?

कुछ पृष्ठभूमि: मैं किसी अन्य ईवेंट से अटैच और अलग करने के लिए CollectionChanged इवेंट का उपयोग कर रहा हूं और इस प्रकार यदि मुझे e.OldItems में कोई आइटम नहीं मिलता है ... तो मैं उस ईवेंट से अलग नहीं हो पाऊंगा।


विशिष्टता: मुझे पता है कि प्रलेखन एकमुश्त नहीं बताता है कि उसे इस तरह से व्यवहार करना है। लेकिन हर दूसरी कार्रवाई के लिए, यह मुझे सूचित नहीं करता है कि उसने क्या किया है। तो, मेरी धारणा यह है कि यह मुझे बताएगा ... क्लियर / रीसेट के मामले में भी।


नीचे नमूना कोड है यदि आप इसे खुद को पुन: पेश करना चाहते हैं। सबसे पहले 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>

अगला, पीछे का कोड:

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

आपको घटना को रद्द करने की आवश्यकता क्यों है? आप किस दिशा में सदस्यता ले रहे हैं? ईवेंट रेज़र द्वारा रखे गए सब्सक्राइबर का संदर्भ बनाते हैं, न कि दूसरे तरीके से। यदि राईज़र एक संग्रह में आइटम हैं जो साफ़ हो जाते हैं, तो वे सुरक्षित रूप से एकत्र किए गए कचरा हो जाएंगे और संदर्भ गायब हो जाएंगे - कोई रिसाव नहीं। यदि आइटम सब्सक्राइबर हैं और एक raiser द्वारा संदर्भित किया गया है, तो जब आप रीसेट प्राप्त करते हैं, तो इवेंट को raiser में शून्य करने के लिए सेट करें - व्यक्तिगत रूप से आइटम को अनसब्सक्राइब करने की कोई आवश्यकता नहीं है।
अलेक्सांद्र डबिन्सकी

मेरा विश्वास करो, मुझे पता है कि यह कैसे काम करता है। विचाराधीन घटना एक सिंगलटन पर थी जो लंबे समय के लिए चारों ओर अटक गई ... इस प्रकार संग्रह में आइटम ग्राहक थे। घटना को शून्य में सेट करने का आपका समाधान काम नहीं करता है ... क्योंकि घटना को अभी भी आग लगाने की ज़रूरत है ... संभवतः अन्य ग्राहकों को सूचित करना (जरूरी नहीं कि वे संग्रह में हों)।
cplotts

जवाबों:


46

यह पुरानी वस्तुओं को शामिल करने का दावा नहीं करता है, क्योंकि रीसेट का मतलब यह नहीं है कि सूची को मंजूरी दे दी गई है

इसका मतलब है कि कुछ नाटकीय बात हुई है, और ऐड को हटाने / बाहर काम करने की लागत सबसे अधिक संभावना है कि स्क्रैच से सूची को फिर से स्कैन करने की लागत से अधिक हो ... इसलिए कि आपको क्या करना चाहिए।

MSDN रीसेट के लिए एक उम्मीदवार के रूप में पूरे संग्रह को फिर से क्रमबद्ध करने का एक उदाहरण बताता है।

बार बार कहना। रीसेट का मतलब स्पष्ट नहीं है , इसका मतलब है कि सूची के बारे में आपकी धारणाएं अब अमान्य हैं। इसे ऐसा मानें कि यह पूरी तरह से नई सूची है । स्पष्ट इस का एक उदाहरण है, लेकिन वहाँ अच्छी तरह से दूसरों हो सकता है।

कुछ उदाहरण:
मेरे पास इस तरह की बहुत सारी वस्तुओं के साथ एक सूची है, और इसमें एक डब्लूएफएफ है ListViewजिसे ई-स्क्रीन प्रदर्शित करने के लिए डिटैबाउंड किया गया है ।
यदि आप सूची को साफ़ करते हैं और .Resetघटना को बढ़ाते हैं , तो प्रदर्शन बहुत ही त्वरित है, लेकिन यदि आप इसके बजाय कई व्यक्तिगत .Removeघटनाओं को बढ़ाते हैं, तो प्रदर्शन बहुत ही भयानक है, क्योंकि WPF एक-एक करके आइटम निकालता है। मैंने .Resetअपने स्वयं के कोड में यह भी इंगित करने के लिए उपयोग किया है कि हजारों व्यक्तिगत Moveऑपरेशन जारी करने के बजाय सूची को फिर से क्रमबद्ध किया गया है । स्पष्ट के साथ, कई व्यक्तिगत घटनाओं को बढ़ाते समय एक बड़ा प्रदर्शन होता है।


1
मैं इस आधार पर सम्मानपूर्वक असहमत होने जा रहा हूं। यदि आप उस दस्तावेज़ को देखते हैं जो यह बताता है: एक डायनेमिक डेटा संग्रह का प्रतिनिधित्व करता है जो आइटमों को जोड़ने, हटाने या पूरी सूची ताज़ा होने पर सूचनाएं प्रदान करता है (देखें msdn.microsoft.com/en-us/library/ms668613(v.VS) .100) .aspx )
cplotts

6
डॉक्स यह बताता है कि आइटमों के जुड़ने / हटाए जाने / ताज़ा होने पर आपको इसकी सूचना देनी चाहिए, लेकिन यह आपको आइटमों के सभी विवरणों को बताने का वादा नहीं करता है ... बस यह कि घटना हुई। इस दृष्टि से व्यवहार ठीक है। व्यक्तिगत रूप से मुझे लगता है कि उन्हें OldItemsसमाशोधन करते समय सभी वस्तुओं को रखना चाहिए था , (यह सिर्फ एक सूची की नकल है), लेकिन शायद कुछ परिदृश्य था जहां यह बहुत महंगा था। किसी भी दर पर, यदि आप एक संग्रह चाहते हैं जो आपको हटाए गए सभी वस्तुओं के बारे में सूचित करता है , तो ऐसा करना मुश्किल नहीं होगा।
ओरियन एडवर्ड्स

2
ठीक है, अगर Resetएक महंगे ऑपरेशन को इंगित करना है, तो यह बहुत संभावना है कि पूरी सूची पर नकल करने के लिए एक ही तर्क लागू होता है OldItems
पगला

7
मजेदार तथ्य: .NET 4.5 के बाद से , Resetवास्तव में "संग्रह की सामग्री को मंजूरी दे दी गई थी ।" देखें msdn.microsoft.com/en-us/library/...
Athari

9
यह जवाब बहुत मदद नहीं करता है, क्षमा करें। हाँ, यदि आप एक रीसेट प्राप्त करते हैं, तो आप पूरी सूची को फिर से शुरू कर सकते हैं, लेकिन आपके पास वस्तुओं को हटाने के लिए कोई पहुंच नहीं है, जो आपको इवेंट हैंडलर को उनसे निकालने की आवश्यकता हो सकती है। यह बहुत बड़ी समस्या है।
Virus721

22

हमारे यहाँ भी यही मुद्दा था। CollectionChanged में रीसेट कार्रवाई में OldItems शामिल नहीं है। हमारे पास वर्कअराउंड था: हमने निम्नलिखित एक्सटेंशन विधि के बजाय उपयोग किया:

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

हमने क्लियर () फ़ंक्शन का समर्थन नहीं किया, और रीसेट क्रियाओं के लिए संग्रह में बदल दिया गया NotSupportedException को फेंकना समाप्त कर दिया। RemoveAll कलेक्शनचेंज किए गए ईवेंट में उचित OldItems के साथ एक निकालें कार्रवाई ट्रिगर करेगा।


अच्छा विचार। मुझे यह पसंद नहीं है कि स्पष्ट का समर्थन न करें क्योंकि यह विधि है (मेरे अनुभव में) ज्यादातर लोग उपयोग करते हैं ... लेकिन कम से कम आप एक अपवाद के साथ उपयोगकर्ता को चेतावनी दे रहे हैं।
cplotts

मैं सहमत हूं, यह आदर्श समाधान नहीं है, लेकिन हमने इसे सबसे अच्छा स्वीकार्य समाधान माना।
डेकास्टलजॉ

आप पुरानी वस्तुओं का उपयोग करने वाले नहीं हैं! आप जो करने वाले हैं, वह सूची में आपके पास जो भी डेटा है, उसे डंप करें और इसे फिर से स्कैन करें जैसे कि यह एक नई सूची हो!
ओरियन एडवर्ड्स

16
समस्या, ओरियन, आपके सुझाव के साथ ... उपयोग मामला है जिसने इस प्रश्न को प्रेरित किया। क्या होता है जब मेरे पास सूची में आइटम होते हैं जिन्हें मैं किसी घटना से अलग करना चाहता हूं? मैं सूची में केवल डेटा डंप नहीं कर सकता ... यह मेमोरी लीक / दबाव में परिणाम देगा।
cplotts

5
इस समाधान की प्रमुख कमी यह है कि यदि आप 1000 आइटम निकालते हैं, तो आप 1000 बार कलेक्शन को आग लगा देते हैं और यूआई को कलेक्शन व्यू को 1000 बार अपडेट करना पड़ता है (यूआई तत्व महंगे हैं)। यदि आप ऑब्जर्वेबल कॉलेक्शन क्लास को ओवरराइड करने से डरते नहीं हैं, तो आप इसे बना सकते हैं ताकि यह क्लियर () ईवेंट को फेयर कर सके लेकिन सही ईवेंट आर्ग्स प्रदान करता है जिससे सभी हटाए गए तत्वों की निगरानी कोड को मॉनिटर करने की अनुमति मिलती है।
Alain

13

एक अन्य विकल्प रीसेट ईवेंट को एक एकल निकालें घटना से बदलना है, जिसकी OldItems संपत्ति में सभी क्लीयर आइटम निम्नानुसार हैं:

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
    ...
}

लाभ:

  1. अतिरिक्त घटना की सदस्यता लेने की आवश्यकता नहीं है (जैसा कि स्वीकृत उत्तर द्वारा आवश्यक है)

  2. हटाए गए प्रत्येक ऑब्जेक्ट के लिए एक घटना उत्पन्न नहीं करता है (कुछ हटाए गए घटनाओं में कुछ अन्य प्रस्तावित समाधान परिणाम)।

  3. सब्सक्राइबर को केवल इवेंट हैंडलर्स को आवश्यकतानुसार जोड़ने / हटाने के लिए किसी भी घटना पर NewItems & OldItems की जांच करने की आवश्यकता होती है।

नुकसान:

  1. कोई रीसेट घटना नहीं

  2. सूची की प्रतिलिपि बनाने वाली छोटी (?) ओवरहेड।

  3. ???

EDIT 2012-02-23

दुर्भाग्य से, जब WPF सूची आधारित नियंत्रणों के लिए बाध्य होता है, तो कई तत्वों के साथ एक ऑब्जर्वेबलकोलिनेटेशन नोऑसेट संग्रह को क्लियर करने के परिणामस्वरूप एक अपवाद होगा "रेंज क्रियाएं समर्थित नहीं हैं"। इस सीमा के साथ नियंत्रण के साथ प्रयोग करने के लिए, मैंने ऑब्जर्वेबलकोलिनेटेशन नोएसेटसेट क्लास को बदल दिया:

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.
 }

यह उतना कुशल नहीं है जब RangeActionsSupported गलत (डिफ़ॉल्ट) है क्योंकि संग्रह में प्रति ऑब्जेक्ट एक निकालें सूचना उत्पन्न होती है


मुझे यह पसंद है, लेकिन दुर्भाग्य से सिल्वरलाइट 4 नोटिफ़िकलशनचेंजडेनवेंटएग्र्स में एक कंस्ट्रक्टर नहीं है जो आइटम की एक सूची लेता है।
साइमन ब्रानगविन

2
मैं इस समाधान से प्यार करता था, लेकिन यह काम नहीं करता है ... आपको एक NotifyCollectionChangedEventArgs बढ़ाने की अनुमति नहीं है, जब तक कि एक आइटम से अधिक नहीं बदला जाता है जब तक कि कार्रवाई "रीसेट" न हो। आपको एक अपवाद मिलता है Range actions are not supported.मुझे नहीं पता कि यह ऐसा क्यों करता है, लेकिन अब यह एक विकल्प को एक बार में प्रत्येक आइटम को हटाने के अलावा कोई विकल्प नहीं छोड़ता है ...
Alain

2
@Alain The ObservableCollection इस प्रतिबंध को नहीं लगाता है। मुझे संदेह है कि यह WPF नियंत्रण है जिसे आपने संग्रह के लिए बाध्य किया है। मुझे भी यही समस्या थी और अपने समाधान के साथ अपडेट पोस्ट करने के लिए कभी नहीं मिला। मैं अपने उत्तर को संशोधित वर्ग के साथ संपादित करूँगा जो कि WPF नियंत्रण के लिए बाध्य है।
अनुदान

मैं अब वही देखता हूं। मुझे वास्तव में एक बहुत ही सुंदर समाधान मिला जो संग्रहित घटना को ओवरराइड करता है और foreach( NotifyCollectionChangedEventHandler handler in this.CollectionChanged )यदि ऊपर लूप होता है handler.Target is CollectionView, तो आप हैंडलर को आर्ग से बंद Action.Resetकर सकते हैं, अन्यथा, आप पूर्ण आर्ग प्रदान कर सकते हैं। हैंडलर आधार द्वारा एक हैंडलर पर दोनों दुनिया के सर्वश्रेष्ठ :)। की तरह यहाँ क्या है: stackoverflow.com/a/3302917/529618
Alain

मैंने नीचे अपना समाधान पोस्ट किया। stackoverflow.com/a/9416535/529618 अपने प्रेरक समाधान के लिए आपका बहुत-बहुत धन्यवाद। यह मुझे वहाँ आधे रास्ते मिल गया।
Alain

10

ठीक है, मुझे पता है कि यह एक बहुत पुराना सवाल है लेकिन मैं इस मुद्दे का एक अच्छा समाधान लेकर आया हूं और सोचा था कि मैं साझा करूंगा। यह समाधान यहाँ बहुत सारे शानदार उत्तरों से प्रेरणा लेता है लेकिन इसके निम्नलिखित फायदे हैं:

  • ओब्जर्वेबल कलैक्शन से नए वर्ग और ओवरराइड विधियों को बनाने की आवश्यकता नहीं है
  • NotifyCollectionChanged (रीसेट के साथ कोई गड़बड़ नहीं है) के कामकाज के साथ छेड़छाड़ नहीं करता है
  • प्रतिबिंब का उपयोग नहीं करता है

यहाँ कोड है:

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

यह एक्सटेंशन विधि सरलता Actionसे संग्रहित होने से पहले ही लागू हो जाएगी।


बहुत अच्छा विचार है। सरल, सुरुचिपूर्ण।
cplotts

9

मुझे एक ऐसा समाधान मिला है जो उपयोगकर्ता को केवल एक घटना को फायर करते समय कई वस्तुओं को जोड़ने या निकालने की दक्षता को भुनाने की अनुमति देता है - और कार्रवाई के लिए UIElements की जरूरतों को पूरा करने के लिए संतुष्ट करें। अन्य सभी उपयोगकर्ताओं के होते हुए। जोड़ा और हटाए गए तत्वों की सूची की तरह।

इस समाधान में CollectionChanged इवेंट को ओवरराइड करना शामिल है। जब हम इस घटना की आग में जाते हैं, तो हम वास्तव में प्रत्येक पंजीकृत हैंडलर के लक्ष्य को देख सकते हैं और उनके प्रकार का निर्धारण कर सकते हैं। चूँकि केवल ICollectionView वर्गों को आवश्यकता होती है NotifyCollectionChangedAction.Resetजब एक से अधिक आइटम बदलने पर हम उन्हें बाहर निकाल सकते हैं, और बाकी सभी को उचित ईवेंट देता है जिसमें हटाए गए या जोड़े गए आइटम की पूरी सूची होती है। नीचे कार्यान्वयन है।

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
}

7

ठीक है, भले ही मैं अभी भी चाहता हूं कि ओब्जर्वेबल कल्बनेशन ने जैसा व्यवहार किया है, मैं चाहता हूं ... नीचे दिया गया कोड वह है जो मैंने किया। मूल रूप से, मैंने TrulyObservableCollection नामक T का एक नया संग्रह बनाया और ClearItems पद्धति को उखाड़ फेंका जिसका उपयोग मैंने तब एक समाशोधन घटना को बढ़ाने के लिए किया था।

इस TrulyObservableCollection का उपयोग करने वाले कोड में, मैं इस क्लीयरिंग ईवेंट का उपयोग उन आइटमों के माध्यम से लूप करने के लिए करता हूं जो अभी भी उस बिंदु पर संग्रह में हैं जो कि उस घटना पर डिटैच करने के लिए जिसे मैं अलग करना चाहता था।

आशा है कि यह दृष्टिकोण किसी और की भी मदद करेगा।

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
आपको अपनी कक्षा का नाम बदलने की आवश्यकता है BrokenObservableCollection, न कि TrulyObservableCollection- आप गलत समझ रहे हैं कि रीसेट क्रिया का क्या अर्थ है।
ओरियन एडवर्ड्स

1
@ ओरियन एडवर्ड्स: मैं असहमत हूं। आपके उत्तर के लिए मेरी टिप्पणी देखें।
cplotts

1
@ ओरियन एडवर्ड्स: ओह, रुको, मैं देखता हूं, तुम मजाकिया हो रहे हो। लेकिन तब मुझे वास्तव में इसे कॉल करना चाहिए ActuallyUsefulObservableCollection:। :)
3'10

6
लोल महान नाम। मैं मानता हूं कि यह डिजाइन का एक गंभीर निरीक्षण है।
devios1

1
यदि आप किसी भी तरह से एक नया ऑब्जर्वर कॉलेक्शन क्लास लागू करने जा रहे हैं, तो एक नया ईवेंट बनाने की कोई आवश्यकता नहीं है, जिसे गंभीरता से मॉनिटर किया जाना चाहिए। आप केवल ActionIt को ट्रिगर करने से ClearItems को रोक सकते हैं = ईवेंट रीसेट करें और इसे एक्शन के साथ प्रतिस्थापित करें = ईवेंट आर्ग को हटाएं जिसमें एक सूची होती है। सूची में सभी आइटमों के ई-मेल। इस प्रश्न में अन्य समाधान देखें।
Alain

4

मैंने इसे एक अलग तरीके से निपटाया क्योंकि मैं एक इवेंट में पंजीकरण करना चाहता था और ईवेंट हैंडलर में सभी अतिरिक्त और रिमूवल संभालना चाहता था। मैंने संग्रह परिवर्तित इवेंट को ओवरराइड करना शुरू कर दिया और आइटम की सूची के साथ कार्रवाई को हटाने के लिए रीसेट कार्यों को पुनर्निर्देशित किया। यह सब गलत हो गया क्योंकि मैं एक संग्रह दृश्य के लिए आइटम स्रोत के रूप में अवलोकन संग्रह का उपयोग कर रहा था और "रेंज एक्शन समर्थित नहीं था"।

मैंने आखिरकार CollectionChangedRange नामक एक नया ईवेंट बनाया, जो उस तरीके से काम करता है, जिससे मुझे लगता है कि इनबिल्ट वर्जन एक्ट करेगा।

मैं कल्पना नहीं कर सकता कि इस सीमा की अनुमति क्यों दी जाएगी और आशा है कि यह पद दूसरों को कम से कम उस मृत अंत तक जाने से रोकता है जो मैंने किया था।

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

}

दिलचस्प दृष्टिकोण। इसे पोस्ट करने के लिए धन्यवाद। यदि मैं कभी भी अपने दृष्टिकोण के साथ समस्याओं में भागता हूं, तो मुझे लगता है कि मैं आपका फिर से स्वागत करूंगा।
cplotts

3

यह है कि ऑब्जर्वेबल कॉलेक्शन कैसे काम करता है, आप ऑब्जर्वेबल कॉलेक्शन के बाहर अपनी खुद की लिस्ट रखकर इसके आसपास काम कर सकते हैं (सूची में जोड़ने पर एक्शन ऐड होने पर हटाएं, जब एक्शन हटाएं आदि) तो आप हटाए गए सभी आइटम (या जोड़े गए आइटम) प्राप्त कर सकते हैं। ) जब आपकी सूची को ऑब्जर्वेबल काबिलेक्शन के साथ तुलना करके रीसेट किया जाता है।

एक अन्य विकल्प यह है कि आप अपनी खुद की क्लास बना सकते हैं जो IList और INotifyCollectionChanged को लागू करता है, फिर आप उस क्लास में ईवेंट को अटैच और अलग कर सकते हैं (या अगर आपको पसंद है तो OldItems सेट करें) - यह वास्तव में मुश्किल नहीं है, लेकिन यह बहुत टाइपिंग है।


मैंने एक और सूची पर नज़र रखने के साथ-साथ आपको पहले सुझाव देने पर भी विचार किया, लेकिन यह बहुत अनावश्यक काम की तरह लगता है। आपका दूसरा सुझाव मेरे साथ चल रहे अंत के बहुत करीब है ... जिसे मैं उत्तर के रूप में पोस्ट करूंगा।
cplotts

3

ऑबजर्वरकॉलेक्शन के तत्वों के लिए इवेंट हैंडलर संलग्न करने और अलग करने के परिदृश्य के लिए एक "क्लाइंट-साइड" समाधान भी है। ईवेंट हैंडलिंग कोड में आप देख सकते हैं कि प्रेषक में कंसरेन्स विधि का उपयोग करके ऑब्जर्वेबल कॉलेलेशन में है या नहीं। प्रो: आप किसी भी मौजूदा ऑब्जर्वेबल कॉलेक्शन के साथ काम कर सकते हैं। विपक्ष: समास विधि O (n) के साथ चलती है जहां n वेधशाला में तत्वों की संख्या है। तो यह छोटे वेधशाला के लिए एक समाधान है।

एक और "क्लाइंट-साइड" समाधान बीच में एक घटना हैंडलर का उपयोग करना है। बस बीच में इवेंट हैंडलर को सभी घटनाओं को पंजीकृत करें। यह ईवेंट हैंडलर बदले में वास्तविक ईवेंट हैंडलर को कॉलबैक या ईवेंट को सूचित करता है। यदि रीसेट कार्रवाई होती है तो कॉलबैक या ईवेंट हटा दें और बीच में एक नया ईवेंट हैंडलर बनाएं और पुराने के बारे में भूल जाएं। यह दृष्टिकोण बड़े ऑब्ज़र्वेबल कॉलेक्शन के लिए भी काम करता है। मैंने प्रॉपर्टीचेंज इवेंट के लिए इसका इस्तेमाल किया (नीचे कोड देखें)।

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

मुझे विश्वास है कि आपके पहले दृष्टिकोण के साथ, मुझे वस्तुओं को ट्रैक करने के लिए एक और सूची की आवश्यकता होगी ... क्योंकि एक बार जब आप रीसेट एक्शन के साथ कलेक्शन की घटना को प्राप्त कर लेंगे ... संग्रह पहले से ही खाली है। मैं आपके दूसरे सुझाव का पालन नहीं करता। मुझे एक सरल परीक्षण पसंद होगा, जो इसे दिखाता है, लेकिन ऑब्जर्वेबल कोलेलिशन को जोड़ने, हटाने और साफ़ करने के लिए। यदि आप एक उदाहरण बनाते हैं, तो आप मुझे मेरे पहले नाम पर ईमेल कर सकते हैं और उसके बाद मेरे अंतिम नाम gmail.com पर।
cplotts

2

NotifyCollectionChangedEventArgs को देखते हुए , ऐसा प्रतीत होता है कि OldItems में केवल प्रतिस्थापन, निकालें या क्रिया के परिणामस्वरूप परिवर्तित आइटम होते हैं। यह इंगित नहीं करता है कि इसमें Clear पर कुछ भी होगा। मुझे संदेह है कि स्पष्ट ईवेंट को आग लगा देता है, लेकिन हटाए गए आइटम को पंजीकृत नहीं करता है और निकालें कोड को बिल्कुल भी लागू नहीं करता है।


6
मैंने वह भी देखा, लेकिन मुझे यह पसंद नहीं है। ऐसा लगता है कि मेरे लिए एक छेद है।
cplotts

इसे हटाने के कोड का आह्वान नहीं है क्योंकि इसकी आवश्यकता नहीं है। रीसेट का अर्थ है "कुछ नाटकीय हुआ है, आपको फिर से शुरू करने की आवश्यकता है"। एक स्पष्ट ऑपरेशन इसका एक उदाहरण है, लेकिन अन्य हैं
ओरियन एडवर्ड्स

2

खैर, मैंने खुद इसके साथ गंदा होने का फैसला किया।

Microsoft ने हमेशा यह सुनिश्चित करने के लिए बहुत सारे काम किए कि NotifyCollectionChangedEventArgs में कोई भी डेटा नहीं है जब एक रीसेट को कॉल किया जाए। मैं मान रहा हूँ कि यह एक प्रदर्शन / स्मृति निर्णय था। यदि आप 100,000 तत्वों के साथ एक संग्रह को रीसेट कर रहे हैं, तो मुझे लगता है कि वे उन सभी तत्वों की नकल नहीं करना चाहते थे।

लेकिन मेरे संग्रह को 100 से अधिक तत्वों के रूप में देखने के बाद, मुझे इसके साथ कोई समस्या नहीं दिख रही है।

वैसे भी मैंने निम्नलिखित विधि के साथ एक विरासत वर्ग बनाया:

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

यह अच्छा है, लेकिन शायद पूर्ण विश्वास वाले वातावरण में कुछ भी काम नहीं करेगा। निजी क्षेत्रों को प्रतिबिंबित करने के लिए पूर्ण-विश्वास की आवश्यकता होती है, है ना?
पॉल

1
आप ऐसा क्यों करेंगे? अन्य चीजें हैं जो रीसेट कार्रवाई को आग का कारण बन सकती हैं - सिर्फ इसलिए कि आपने स्पष्ट पद्धति को अक्षम कर दिया है इसका मतलब यह नहीं है कि यह दूर चला गया है (या यह होना चाहिए)
ओरियन एडवर्ड्स

दिलचस्प दृष्टिकोण, लेकिन प्रतिबिंब धीमा हो सकता है।
cplotts

2

ओब्जर्वेबल कॉलेक्शन और साथ ही INotifyCollectionChanged इंटरफ़ेस स्पष्ट रूप से एक विशिष्ट उपयोग को ध्यान में रखते हुए लिखा गया है: UI बिल्डिंग और इसकी विशिष्ट प्रदर्शन विशेषताएँ।

जब आप संग्रह परिवर्तनों की सूचनाएं चाहते हैं तो आप आमतौर पर केवल ऐड और निकालें घटनाओं में रुचि रखते हैं।

मैं निम्नलिखित इंटरफ़ेस का उपयोग करता हूं:

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

मैंने संग्रह का अपना अधिभार भी लिखा है जहाँ:

  • ClearItems रिमूवल बढ़ाता है
  • InsertItem जोड़ा गया
  • निष्कासन को हटाता है
  • SetItem रिमूवल और जोड़ा गया

बेशक, AddRange को भी जोड़ा जा सकता है।


+1 यह इंगित करने के लिए कि Microsoft ने एक विशिष्ट उपयोग मामले को ध्यान में रखते हुए ... और प्रदर्शन पर नज़र रखकर Microsoft का अवलोकन किया। मैं सहमत हूँ। अन्य स्थितियों के लिए एक छेद छोड़ दिया, लेकिन मैं सहमत हूं।
cplotts

-1 मुझे हर तरह की चीजों में दिलचस्पी हो सकती है। अक्सर मुझे अतिरिक्त / हटाए गए आइटम के सूचकांक की आवश्यकता होती है। मैं प्रतिस्थापित करना चाहता हूँ। आदि INotifyCollectionChanged का डिज़ाइन अच्छा है। जो समस्या ठीक होनी चाहिए वह एमएस पर लागू नहीं है।
अलेक्सांद्र डबलिनस्की

1

मैं सिल्वरलाइट और WPF टूलकिट में कुछ चार्टिंग कोड के माध्यम से जा रहा था और ध्यान दिया कि उन्होंने इस समस्या को भी हल कर दिया (एक तरह के तरीके से) ... और मुझे लगा कि मैं आगे जाकर उनका समाधान पोस्ट करूंगा।

मूल रूप से, उन्होंने एक व्युत्पन्न ओब्जर्वेबल कॉलेक्शन और ओवररोड क्लीयरटाइम्स भी बनाए, जिसमें प्रत्येक आइटम को हटाए जाने को हटाएं।

यहाँ कोड है:

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

मैं केवल यह बताना चाहता हूं कि मुझे यह दृष्टिकोण उतना पसंद नहीं है, जितना कि मैंने एक उत्तर के रूप में चिह्नित किया है ... क्योंकि आपको एक नोटिफ़ेकलेक्शनकैंशन की गई घटना मिलती है (एक निकालें कार्रवाई के साथ) ... प्रत्येक आइटम को हटाए जाने के लिए।
cplotts

1

यह एक गर्म विषय है ... क्योंकि मेरी राय में, Microsoft ने अपना काम ठीक से नहीं किया ... फिर भी। मुझे गलत न समझें, मुझे Microsoft पसंद है, लेकिन वे परिपूर्ण नहीं हैं!

मैंने पिछली टिप्पणियों के अधिकांश भाग को पढ़ा। मैं उन सभी से सहमत हूं जो सोचते हैं कि Microsoft ने Clear () को ठीक से प्रोग्राम नहीं किया था।

मेरी राय में, कम से कम, यह एक घटना से वस्तुओं को अलग करना संभव बनाने के लिए एक तर्क की आवश्यकता है ... लेकिन मैं इसके प्रभाव को भी समझता हूं। फिर, मैंने इस प्रस्तावित समाधान के बारे में सोचा।

मुझे उम्मीद है कि यह सभी को खुश करेगा, या कम से कम, सभी को ...

एरिक

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

मुझे अभी भी लगता है कि Microsoft को अधिसूचना के साथ स्पष्ट करने में सक्षम होने का एक तरीका प्रदान करना चाहिए। मुझे अभी भी लगता है कि वे उस तरह से शॉट नहीं देकर शॉट को मिस करते हैं। माफ़ करना ! मैं यह नहीं कह रहा हूँ कि स्पष्ट को हटा दिया जाना चाहिए, कुछ याद आ रहा है !!! कम युग्मन प्राप्त करने के लिए, हमें कभी-कभी सलाह दी जाती है कि क्या हटाया गया था।
एरिक ओउलेट

1

यह सरल रखने के लिए कि आप ClearItem पद्धति को ओवरराइड क्यों नहीं करते हैं और जो कुछ भी आप चाहते हैं, वहाँ करें अर्थात घटना से आइटम अलग करें।

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

  rest of the code omitted
}

संग्रह कोड के भीतर सरल, स्वच्छ और सम्‍मिलित है।


यह वास्तव में मैंने जो किया उसके बहुत करीब है ... स्वीकृत उत्तर देखें।
cplotts

0

मेरे पास एक ही मुद्दा था, और यह मेरा समाधान था। यह काम करने लगता है। किसी को भी इस दृष्टिकोण के साथ किसी भी संभावित समस्याओं को देखता है?

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

यहाँ मेरी कक्षा में कुछ अन्य उपयोगी तरीके दिए गए हैं:

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

मुझे ऑब्जर्वेबलकोलेक्शन से निकलने वाला एक और "सरल" समाधान मिला, लेकिन यह बहुत सुरुचिपूर्ण नहीं है क्योंकि यह रिफ्लेक्शन का उपयोग करता है ... अगर आपको यह पसंद है तो यहां मेरा समाधान है:

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

यहाँ मैं ClearItems विधि में एक सरणी फ़ील्ड में वर्तमान तत्वों को सहेजता हूं, फिर मैं OnCollectionChanged की कॉल को इंटरसेप्ट करता हूं और आधार लॉन्च करने से पहले e._oldItems निजी फ़ील्ड (प्रतिबिंबों के माध्यम से) को अधिलेखित कर देता हूं। TheCollectionChanged


0

आप ClearItems विधि को ओवरराइड कर सकते हैं और निकालें कार्रवाई और OldItems के साथ घटना को बढ़ा सकते हैं।

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

System.Collections.ObjectModel.ObservableCollection<T>बोध का हिस्सा :

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

कृपया इस दस्तावेज़ को अपनी आँखें खोलकर पढ़ें और आपका मस्तिष्क चालू हो गया। Microsoft ने सब कुछ ठीक किया। जब यह आपके लिए रीसेट सूचना फेंकता है तो आपको अपना संग्रह फिर से स्कैन करना होगा। आपको एक रीसेट सूचना मिलती है क्योंकि प्रत्येक आइटम के लिए जोड़ें / निकालें फेंकना (हटाए जाने और संग्रह में वापस जोड़ा जाना) बहुत महंगा है।

ओरियन एडवर्ड्स पूरी तरह से सही है (सम्मान, आदमी)। कृपया डॉक्यूमेंट पढ़ते समय व्यापक सोचें।


5
मुझे वास्तव में लगता है कि Microsoft के काम करने के तरीके को समझने में आपकी और ओरियन की समझ सही है। :) इस डिजाइन ने मुझे उन समस्याओं का कारण बनाया जो मुझे अपनी स्थिति के लिए काम करने की आवश्यकता थी। यह स्थिति एक सामान्य भी है ... और मैंने यह प्रश्न क्यों पोस्ट किया।
6'11

मुझे लगता है कि आपको मेरे प्रश्न (और चिह्नित उत्तर) को थोड़ा और देखना चाहिए। मैं हर आइटम के लिए हटाने का सुझाव नहीं दे रहा था।
cplotts

और रिकॉर्ड के लिए, मैं ओरियन के जवाब का सम्मान करता हूं ... मुझे लगता है कि हम सिर्फ एक-दूसरे के साथ थोड़ा मजा कर रहे थे ... कम से कम यह है कि मैंने इसे कैसे लिया।
cplotts

एक महत्वपूर्ण बात: आपको हटाने वाली वस्तुओं से ईवेंट हैंडलिंग प्रक्रियाओं को अलग करने की आवश्यकता नहीं है। डिटैचमेंट अपने आप हो जाता है।
दिमा

1
इसलिए सारांश में, किसी संग्रह से किसी वस्तु को हटाते समय घटनाओं को स्वचालित रूप से अलग नहीं किया जाता है।
15

-4

यदि आपकी ObservableCollectionस्थिति स्पष्ट नहीं हो रही है, तो आप नीचे दिए गए कोड की कोशिश कर सकते हैं। यह आपकी मदद कर सकता है:

private TestEntities context; // This is your context

context.Refresh(System.Data.Objects.RefreshMode.StoreWins, context.UserTables); // to refresh the object context
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.