পর্যবেক্ষণযোগ্য সংগ্রহটি সাফ করার সময়, e.OldItems এ কোনও আইটেম নেই are


91

আমার এখানে কিছু আছে যা আমাকে সত্যিই প্রহরী থেকে ধরে ফেলছে।

আমার কাছে টির একটি পর্যবেক্ষণযোগ্য সংগ্রহ রয়েছে যা আইটেমগুলিতে পূর্ণ। কালেকশন চেঞ্জড ইভেন্টের সাথে আমার একটি ইভেন্ট হ্যান্ডলার সংযুক্ত রয়েছে।

আপনি যখন সংগ্রহটি সাফ করেন তখন এটি একটি কালেকশন-পরিবর্তিত ইভেন্টের কারণ হয়। ঠিক আছে, এটাই স্বাভাবিক। তবে কী অদ্ভুত তা হ'ল e.OldItems বা e.NewItems এর মধ্যে কিছু নেই। আমি আশা করব যে E.OldItems সংগ্রহ থেকে সরানো সমস্ত আইটেম পূরণ করা হবে।

এটা কি আর কেউ দেখেছে? এবং যদি তা হয়, তবে তারা কীভাবে এটি চারপাশে অর্জন করেছে?

কিছু ব্যাকগ্রাউন্ড: আমি অন্য ইভেন্ট থেকে সংযুক্ত করতে এবং আলাদা করার জন্য কালেকশন চেঞ্জড ইভেন্টটি ব্যবহার করছি এবং এইভাবে যদি আমি E.OldItems এ কোনও আইটেম না পাই ... তবে আমি সেই ইভেন্ট থেকে বিচ্ছিন্ন হতে পারব না।


স্পষ্টকরণ: আমি জানি যে ডকুমেন্টেশনটি এইভাবে আচরণ করতে হবে তা প্রকাশ্যভাবে জানায় না । তবে অন্য প্রতিটি ক্রিয়াকলাপের জন্য, এটি এটি কী করেছে তা আমাকে অবহিত করছে। সুতরাং, আমার ধারণাটি হ'ল এটি আমাকে বলবে ... ক্লিয়ার / রিসেটের ক্ষেত্রেও।


নীচে নমুনা কোডটি যদি আপনি নিজের প্রজনন করতে চান তবে নীচে। প্রথমে এক্সএল থেকে:

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

আপনার কেন ইভেন্টটি বাতিল করতে হবে? আপনি কোন দিকে সাবস্ক্রাইব করছেন? ইভেন্টগুলি রাইজারের দ্বারা পরিচালিত গ্রাহককে একটি রেফারেন্স তৈরি করে, অন্যভাবে নয়। যদি রাইজাররা কোনও সংগ্রহের আইটেমগুলি পরিষ্কার হয়ে যায় তবে সেগুলি নিরাপদে আবর্জনা সংগ্রহ করা হবে এবং উল্লেখগুলি অদৃশ্য হয়ে যাবে - কোনও লিক হবে না। আইটেমগুলি যদি গ্রাহক হয় এবং একজন রাইজার দ্বারা রেফারেন্স হয়, তবে আপনি যখন রিসেট পাবেন তখন ইভেন্টটিকে রাইজারের মধ্যে ফেলে দেবে - স্বতন্ত্রভাবে আইটেমগুলি সাবস্ক্রাইব করার দরকার নেই।
আলেকসান্দ্র ডাবিনস্কি

বিশ্বাস করুন, আমি জানি এটি কীভাবে কাজ করে। প্রশ্নে থাকা ইভেন্টটি একটি সিঙ্গলটনে ছিল যা দীর্ঘকাল ধরে আটকে ছিল ... এইভাবে সংগ্রহের আইটেমগুলি গ্রাহক ছিলেন। ইভেন্টটি বাতিল করতে কেবল আপনার সমাধানটি কার্যকর হয় না ... যেহেতু ইভেন্টটিতে এখনও গুলি চালানো দরকার ... সম্ভবত অন্য গ্রাহকদেরকে অবহিত করা প্রয়োজন (সংগ্রহশালায় যারা নেই)।
cplotts

উত্তর:


46

এটি পুরানো আইটেমগুলি অন্তর্ভুক্ত করার দাবি করে না, কারণ রিসেটের অর্থ এই নয় যে তালিকাটি সাফ হয়ে গেছে

এর অর্থ হল যে কিছু নাটকীয় ঘটনা ঘটেছে এবং অ্যাড / রিমুভালগুলি কার্যকর করার ব্যয়টি সম্ভবত স্ক্র্যাচ থেকে তালিকাটিকে পুনরায় স্ক্যান করার ব্যয়কে ছাড়িয়ে যাবে ... সুতরাং আপনার এটাই করা উচিত।

এমএসডিএন পুরো সংগ্রহটি পুনরায় সাজানোর জন্য প্রার্থী হিসাবে পুনঃ সাজানোর একটি উদাহরণ প্রস্তাব করে।

পুনরাবৃত্তি করা। রিসেটের অর্থ পরিষ্কার নয় , এর অর্থ তালিকা সম্পর্কে আপনার অনুমানগুলি এখন অবৈধ। এটির মতো আচরণ করুন যেন এটি সম্পূর্ণ নতুন তালিকা । পরিষ্কার এর এক উদাহরণ হতে পারে, কিন্তু অন্য ভাল থাকতে পারে।

কয়েকটি উদাহরণ:
আমার প্রচুর আইটেম সহ এই জাতীয় একটি তালিকা রয়েছে এবং এটি ListViewঅন-স্ক্রিনে প্রদর্শন করার জন্য ডাব্লুপিএফের কাছে ডেটাবাউন্ড হয়েছে ।
আপনি যদি তালিকাটি সাফ করে দেন এবং .Resetইভেন্টটি উত্থাপন করেন তবে পারফরম্যান্সটি তাত্ক্ষণিকভাবে দেখা যায়, তবে আপনি যদি এর পরিবর্তে অনেকগুলি পৃথক .Removeইভেন্ট উত্থাপন করেন তবে ডেমোকপিটি একের পর এক আইটেমগুলি সরিয়ে দেওয়ায় কার্যকারিতাটি ভয়ানক। আমি .Resetস্বতন্ত্র কোডেও ব্যবহার করেছি যাতে ইঙ্গিত হয় যে তালিকাটি পুনরায় সাজানো হয়েছে, হাজার হাজার স্বতন্ত্র Moveক্রিয়াকলাপ ইস্যু করার পরিবর্তে । সাফের মতো, অনেকগুলি ব্যক্তিগত ইভেন্ট উত্থাপন করার সময় একটি বিশাল পারফরম্যান্স হিট হয়।


4
আমি এই ভিত্তিতে সম্মানের সাথে একমত হতে চলেছি। আপনি যদি ডকুমেন্টেশনের দিকে লক্ষ্য করেন তবে এতে বলা হয়েছে: আইটেমগুলি যুক্ত করা, অপসারণ করা বা পুরো তালিকাটি রিফ্রেশ হওয়ার পরে বিজ্ঞপ্তি সরবরাহ করে এমন একটি গতিশীল ডেটা সংগ্রহের প্রতিনিধিত্ব করে (দেখুন msdn.microsoft.com/en-us/library/ms668613(v=VS .100)
.aspx

6
ডক্সে বলা হয়েছে যে আইটেমগুলি যুক্ত / সরানো / সতেজ হওয়াতে এটি আপনাকে অবহিত করা উচিত, তবে আইটেমগুলির সমস্ত বিবরণ আপনাকে জানাতে প্রতিশ্রুতি দেয় না ... কেবল ঘটনাটি ঘটেছে। এই দৃষ্টিকোন থেকে আচরণ ভাল। ব্যক্তিগতভাবে আমি মনে করি OldItemsক্লিয়ারিংয়ের সময় তাদের কেবল সমস্ত আইটেম রাখা উচিত ছিল (এটি কেবল একটি তালিকাটি অনুলিপি করে) তবে সম্ভবত এমন কিছু দৃশ্য ছিল যেখানে এটি ব্যয়বহুল ছিল। যে কোনও হারে, আপনি যদি এমন কোনও সংগ্রহ চান যা আপনাকে মুছে ফেলা সমস্ত আইটেম সম্পর্কে অবহিত করে , এটি করা কঠিন হবে না।
অরিওন এডওয়ার্ডস

4
ঠিক আছে, যদি Resetকোনও ব্যয়বহুল ক্রিয়াকলাপটি নির্দেশ করা হয় তবে খুব সম্ভবত একই যুক্তি পুরো তালিকাতে অনুলিপি করার ক্ষেত্রে প্রযোজ্য OldItems
pbalaga

7
মজাদার ঘটনা: নেট .৪.৪ থেকে , Resetআসলে "সংগ্রহের বিষয়বস্তু সাফ হয়ে গেছে means " দেখুন msdn.microsoft.com/en-us/library/...
Athari

9
এই উত্তরটি খুব একটা সাহায্য করে না, দুঃখিত। হ্যাঁ আপনি যদি রিসেট পান তবে আপনি পুরো তালিকাটি পুনর্নির্মাণ করতে পারেন, তবে আইটেমগুলি সরাতে আপনার কোনও অ্যাক্সেস নেই, যা আপনাকে সেগুলির থেকে ইভেন্ট হ্যান্ডলারগুলি সরিয়ে নিতে হবে। এটি একটি বড় সমস্যা।
ভাইরাস 721

22

আমাদের এখানেও একই সমস্যা ছিল। কালেকশনচেনজেডে রিসেট ক্রিয়াতে ওল্ড আইটেমগুলি অন্তর্ভুক্ত নয়। আমাদের একটি কার্যনির্বাহ ছিল: আমরা এর পরিবর্তে নিম্নলিখিত এক্সটেনশন পদ্ধতিটি ব্যবহার করেছি:

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

আমরা সাফ () ফাংশনটি সমর্থন না করে এবং পুনরায় সেট ক্রিয়াকলাপের জন্য কালেকশন-চেঞ্জড ইভেন্টে একটি নটসপোর্টডএক্সপেনশন ছুঁড়ে ফেলেছি। সরানএল সঠিক ওল্ড আইটেম সহ কালেকশন-চেঞ্জড ইভেন্টে একটি সরান ক্রিয়াকে ট্রিগার করবে।


ভাল ধারণা. আমি ক্লিয়ার সাপোর্ট না করা পছন্দ করি না কারণ এটি (আমার অভিজ্ঞতায়) বেশিরভাগ লোকেরা পদ্ধতিটি ব্যবহার করেন ... তবে কমপক্ষে আপনি ব্যতিক্রম সহ ব্যবহারকারীকে সতর্ক করছেন।
cplotts

আমি সম্মতি জানাই, এটি আদর্শ সমাধান নয়, তবে আমরা এটি সেরা গ্রহণযোগ্য কাজের ভিত্তিতে পেয়েছি।
ডেসটেলজৌ

আপনি পুরানো আইটেম ব্যবহার করার কথা না! আপনার যা করা উচিত তা হ'ল তালিকায় থাকা আপনার যে কোনও ডেটা ফেলে দিন এবং এটি আবার নতুন স্ক্যান করে যেন এটি কোনও নতুন তালিকা!
অরিওন এডওয়ার্ডস

16
আপনার পরামর্শের সাথে ওরিওন সমস্যা ... হ'ল ব্যবহারের বিষয়টি যা এই প্রশ্নটিকে উত্সাহ দেয়। তালিকায় থাকা আইটেমগুলি যখন আমি একটি ইভেন্ট থেকে আলাদা করতে চাই তখন কী হবে? আমি কেবলমাত্র তালিকায় ডেটা ফেলে দিতে পারি না ... এর ফলে মেমরি ফাঁস / চাপ সৃষ্টি হবে।
cplotts

4
এই সমাধানটির প্রধান অবক্ষয় হ'ল আপনি যদি 1000 টি আইটেম সরিয়ে ফেলেন তবে আপনি কালেকশন পরিবর্তন করেছেন 1000 বার এবং ইউআইকে কালেকশন ভিউ 1000 বার আপডেট করতে হবে (ইউআই উপাদানগুলি আপডেট করা ব্যয়বহুল)। আপনি যদি পর্যবেক্ষণযোগ্য সংগ্রহের ক্লাসটি ওভাররাইড করতে ভয় পান না, আপনি এটি তৈরি করতে পারেন যাতে এটি ক্লিয়ার () ইভেন্টটিকে আগুন ধরিয়ে দেয় তবে মনিটরিং কোডটিকে সমস্ত সরানো উপাদানগুলি নিবন্ধভুক্ত করার অনুমতি দেয় এমন সঠিক ইভেন্ট আরোগুলি সরবরাহ করে।
আলাইন

13

অন্য বিকল্পটি হ'ল রিসেট ইভেন্টটিকে একটি একক সরান ইভেন্টের সাথে প্রতিস্থাপন করা হবে যা এর পুরানো আইটেম সম্পত্তিতে সমস্ত সাফ আইটেম রয়েছে:

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. প্রয়োজন অনুসারে ইভেন্ট হ্যান্ডলারগুলি যুক্ত / সরানোর জন্য গ্রাহককে যে কোনও ইভেন্টে কেবলমাত্র নিউ আইটেম এবং ওল্ড আইটেমগুলি পরীক্ষা করতে হবে।

অসুবিধাগুলি:

  1. কোনও রিসেট ইভেন্ট নেই

  2. ছোট (?) ওভারহেড তালিকার অনুলিপি তৈরি করছে।

  3. ???

সম্পাদনা 2012-02-23

দুর্ভাগ্যক্রমে, ডাব্লুপিএফ তালিকাভিত্তিক নিয়ন্ত্রণের সাথে আবদ্ধ থাকা অবস্থায়, একাধিক উপাদানগুলির সাথে পর্যবেক্ষণযোগ্য কালেকশন নয় রিসেট সংগ্রহ সাফ করার ফলে একটি ব্যতিক্রম হবে "রেঞ্জ ক্রিয়াগুলি সমর্থিত নয়" exception এই সীমাবদ্ধতার সাথে নিয়ন্ত্রণগুলির সাথে ব্যবহার করার জন্য, আমি পর্যবেক্ষণযোগ্য সংগ্রহ নোট রিসেট ক্লাসটি এতে পরিবর্তন করেছি:

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

রেঞ্জএকশনস্পপোর্টডটি মিথ্যা (ডিফল্ট) হলে এটি কার্যকর হয় না কারণ সংগ্রহের প্রতিটি বস্তুতে একটি সরান বিজ্ঞপ্তি উত্পন্ন হয়


আমি এটি পছন্দ করি তবে দুর্ভাগ্যক্রমে সিলভারলাইট 4 নোটাইফাই ক্যালকশনচেনজড এভেন্টআরগসের কোনও কনস্ট্রাক্টর নেই যা আইটেমের একটি তালিকা নেয়।
সাইমন ব্র্যাংউইন

4
আমি এই সমাধানটি পছন্দ করেছিলাম, তবে এটি কাজ করে না ... ক্রিয়াকলাপটি "রিসেট" না করা হলে আপনাকে একটি নোটিফাইকোলেকশন চ্যাঞ্জডএভেন্টআর্গস বাড়াতে দেওয়া হবে না যা একাধিক আইটেম পরিবর্তন করেছে। আপনি একটি ব্যতিক্রম পেতে Range actions are not supported.আমি জানি না কেন এটা এই আছে এই পাতার কোনো বিকল্প, কিন্তু এখন কিন্তু একটি সময়ে প্রতিটি আইটেমের এক ... মুছে ফেলার জন্য
তথ্যের

4
অ্যালেন দ্য অবজারভেবল কালেকশন এই বিধিনিষেধ আরোপ করে না। আমার সন্দেহ হয় যে আপনি এই ডাব্লুপিএফ নিয়ন্ত্রণটি সংগ্রহকে আবদ্ধ করেছেন। আমারও একই সমস্যা ছিল এবং আমার সমাধান সহ কোনও আপডেট পোস্ট করতে কখনই যায় নি। আমি আমার উত্তরটি সংশোধিত শ্রেণীর সাথে সম্পাদনা করব যা ডাব্লুপিএফ নিয়ন্ত্রণের সাথে আবদ্ধ হওয়ার সাথে কাজ করে।
গ্রান্টজ

আমি এখন এটি দেখতে। আমি আসলে একটি খুব মার্জিত সমাধান পেয়েছি যা কালেকশন-চেঞ্জড ইভেন্টটিকে ওভাররাইড করে এবং foreach( NotifyCollectionChangedEventHandler handler in this.CollectionChanged )যদি লুপ করে handler.Target is CollectionView, তবে আপনি হ্যান্ডলারটি Action.Resetআরগস দিয়ে সরিয়ে দিতে পারেন, অন্যথায়, আপনি সম্পূর্ণ আরোগুলি সরবরাহ করতে পারেন। হ্যান্ডলার ভিত্তিতে হ্যান্ডলারে উভয় বিশ্বের সেরা :)। এখানে কি ধরনের মত: stackoverflow.com/a/3302917/529618
আলাইন

আমি আমার নিজের সমাধানটি নীচে পোস্ট করেছি। stackoverflow.com/a/9416535/529618 আপনার অনুপ্রেরণামূলক সমাধানের জন্য আপনাকে একটি বিশাল ধন্যবাদ। এটি আমার অর্ধেক পথ পেয়েছে।
আলাইন

10

ঠিক আছে, আমি জানি এটি একটি খুব পুরানো প্রশ্ন তবে আমি সমস্যার একটি ভাল সমাধান নিয়ে এসেছি এবং ভেবেছি আমি ভাগ করে নেব। এই সমাধানটি এখানে প্রচুর দুর্দান্ত উত্তরের কাছ থেকে অনুপ্রেরণা নিয়েছে তবে এর নিম্নলিখিত সুবিধা রয়েছে:

  • পর্যবেক্ষণযোগ্য সংগ্রহ থেকে নতুন শ্রেণি তৈরি ও ওভাররাইড পদ্ধতি তৈরি করার দরকার নেই
  • বিজ্ঞপ্তি সংগ্রহের পরিবর্তন নিয়ে কাজ করে না (তাই রিসেটের সাথে গোলযোগ নেই)
  • প্রতিবিম্ব ব্যবহার করে না

কোডটি এখানে:

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

এই এক্সটেনশান পদ্ধতিটি সহজভাবে Actionসংগ্রহ করে যা সংগ্রহ সাফ হওয়ার আগে ডাকা হবে।


খুব সুন্দর ধারণা। সরল, মার্জিত।
cplotts

9

আমি একটি সমাধান পেয়েছি যা ব্যবহারকারীর কেবলমাত্র একটি ইভেন্টের গুলি চালানোর সময় এক সাথে অনেকগুলি আইটেম যুক্ত বা মুছে ফেলার দক্ষতা উভয়কেই উপলব্ধি করতে দেয় - এবং অ্যাকশন পেতে UIE উপাদানগুলির প্রয়োজনীয়তা পূরণ করে event ইভেন্ট আরগগুলি পুনরায় সেট করুন অন্য সমস্ত ব্যবহারকারীরা যোগ ও সরানো উপাদানের তালিকার মতো।

এই সমাধানটিতে কালেকশন চেঞ্জ করা ইভেন্টটিকে ওভাররাইড করা জড়িত। আমরা যখন এই ইভেন্টটি চালাতে যাই, আমরা প্রকৃতপক্ষে প্রতিটি নিবন্ধিত হ্যান্ডলারের লক্ষ্যটি দেখতে পারি এবং তাদের ধরণ নির্ধারণ করতে পারি। যেহেতু 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

ঠিক আছে, যদিও আমি এখনও চাইছি যে পর্যবেক্ষণযোগ্য সংগ্রহটি আমার ইচ্ছামত আচরণ করবে ... নীচের কোডটি আমি শেষ করে যাচ্ছি। মূলত, আমি টি এর একটি নতুন সংগ্রহ তৈরি করেছি যার নাম ট্রেলোওবার্সেবল কালেকশন এবং ক্লিয়ারআইটেমস পদ্ধতিটি আমি ওভাররাইড করেছিলাম যা পরে আমি একটি ক্লিয়ারিং ইভেন্ট উত্থাপন করতে ব্যবহার করি।

এই ট্রুলিও অবজার্ভেশনাল কালেকশনটি যে কোডটি ব্যবহার করে, আমি এই ক্লিয়ারিং ইভেন্টটি এখনও সংগ্রহের মধ্যে থাকা আইটেমগুলির মধ্য দিয়ে লুপ করতে ব্যবহার করি সেখান থেকে আমি বিচ্ছিন্ন হয়ে যাওয়ার ইচ্ছাকৃত ইভেন্টটি আলাদা করতে।

আশা করি এই পদ্ধতিটি অন্য কাউকেও সহায়তা করবে helps

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

4
আপনার ক্লাসটির নতুন নামকরণ করা দরকার BrokenObservableCollection, না TrulyObservableCollection- আপনি রিসেট ক্রিয়াটির অর্থ কী তা বোঝাচ্ছেন।
ওরিওন এডওয়ার্ডস

4
@ অরিয়ন এডওয়ার্ডস: আমি একমত নই আপনার উত্তর আমার মন্তব্য দেখুন।
cplotts

4
@ ওরিয়ন এডওয়ার্ডস: ওহ, অপেক্ষা করুন, আমি দেখছি, আপনি মজার হচ্ছেন। কিন্তু তারপর আমি সত্যিই এটা কল করা উচিত: ActuallyUsefulObservableCollection। :)
cplotts

6
Lol দুর্দান্ত নাম। আমি একমত যে এটি ডিজাইনের একটি গুরুতর পর্যবেক্ষণ।
ডিভাইস 1

4
যদি আপনি যাইহোক কোনও নতুন পর্যবেক্ষণযোগ্য সংগ্রহের শ্রেণি প্রয়োগ করতে চলেছেন তবে কোনও নতুন ইভেন্ট তৈরি করার দরকার নেই যা অবশ্যই আলাদাভাবে পর্যবেক্ষণ করা উচিত। আপনি ক্লিয়ার আইটেমগুলিকে কেবল অ্যাকশন = ইভেন্টের আরগগুলি পুনরায় সেট করতে এবং কোনও ক্রিয়া দিয়ে প্রতিস্থাপন করতে পারেন = ইভেন্টের আরগগুলিতে সরান যা তালিকায় থাকা সমস্ত আইটেমের তালিকা e.OldItems রয়েছে। এই প্রশ্নের অন্যান্য সমাধান দেখুন।
আলাইন

4

আমি এই ইভেন্টটিকে কিছুটা ভিন্নভাবে মোকাবিলা করেছি কারণ আমি একটি ইভেন্টে নিবন্ধকরণ করতে এবং ইভেন্ট হ্যান্ডলারের সমস্ত সংযোজন এবং অপসারণগুলি পরিচালনা করতে চেয়েছিলাম। আমি সংগ্রহটির পরিবর্তিত ইভেন্টটি ওভারটাইড করা শুরু করেছি এবং আইটেমের একটি তালিকা সহ রিসেট ক্রিয়াকে অপসারণ ক্রিয়ায় পুনঃনির্দেশিত করব। সংগ্রহের দৃশ্যের জন্য আমি পর্যবেক্ষণযোগ্য সংগ্রহটিকে আইটেম উত্স হিসাবে ব্যবহার করছিলাম এবং "রেঞ্জের ক্রিয়াগুলি সমর্থিত নয়" পেয়েছি বলে এই সব ভুল হয়ে গেছে।

অবশেষে আমি কালেকশন চ্যাঞ্জডরেঞ্জ নামে একটি নতুন ইভেন্ট তৈরি করেছি যা ইনবিল্ট সংস্করণটি অভিনয় করার প্রত্যাশায় কাজ করে।

কেন এই সীমাবদ্ধতার অনুমতি দেওয়া হবে তা আমি কল্পনা করতে পারি না এবং আশা করি যে এই পোস্টটি কমপক্ষে অন্যদের আমার যে মৃত পরিণতি ঘটাচ্ছে তা থেকে কমিয়ে দেয়।

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

এটি পর্যবেক্ষণযোগ্য সংগ্রহ কীভাবে কাজ করে আপনি পর্যবেক্ষণযোগ্য সংগ্রহের বাইরে নিজস্ব তালিকা রেখে (কাজটি যুক্ত হওয়ার পরে তালিকায় যুক্ত করা, ক্রিয়া সরানো হলে সরানো ইত্যাদি ইত্যাদি) কাজ করে আপনি মুছে ফেলা সমস্ত আইটেম (বা যুক্ত আইটেমগুলি) পেতে পারেন ) পর্যবেক্ষণযোগ্য সংগ্রহের সাথে আপনার তালিকার তুলনা করে যখন ক্রিয়াটি পুনরায় সেট করা হয়।

আরেকটি বিকল্প হ'ল আপনার নিজস্ব শ্রেণি তৈরি করা যা আইলিস্ট এবং আইএনটিফাই কলকেশন পরিবর্তন করে, তারপরে আপনি সেই শ্রেণীর মধ্যে থেকে ইভেন্টগুলি সংযুক্ত এবং বিচ্ছিন্ন করতে পারেন (বা ওল্ড আইটেমগুলি সাফ করুন যদি আপনি চান তবে সেট করুন) - এটি সত্যিই কঠিন নয়, তবে এটি প্রচুর টাইপিং।


আপনি প্রথমে পরামর্শ দেওয়ার পাশাপাশি আমি অন্য তালিকার উপর নজর রাখার বিষয়টি বিবেচনা করেছি, তবে এটি অনেক অপ্রয়োজনীয় কাজ বলে মনে হচ্ছে। আপনার দ্বিতীয় পরামর্শটি আমি যা দিয়ে শেষ করেছি তার খুব কাছাকাছি ... যা আমি উত্তর হিসাবে পোস্ট করব।
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;
            }   
        }
    }

আমি আপনার প্রথম পদ্ধতির সাথে বিশ্বাস করি, আইটেমগুলি ট্র্যাক করার জন্য আমার অন্য তালিকার দরকার হবে ... কারণ আপনি একবার রিসেট ক্রিয়া সহ কালেকশন-ইভেন্ট ইভেন্টটি পান ... সংগ্রহটি ইতিমধ্যে খালি already আমি আপনার দ্বিতীয় পরামর্শটি পুরোপুরি অনুসরণ করি না। আমি এটির উদাহরণ দিয়ে একটি সাধারণ পরীক্ষার জোতা পছন্দ করব তবে পর্যবেক্ষণযোগ্য সংগ্রহটি যোগ করার, অপসারণ এবং সাফ করার জন্য। আপনি যদি উদাহরণ তৈরি করেন তবে আপনি আমার প্রথম নামটি পরে আমার শেষ নামটি gmail.com এ আমাকে ইমেল করতে পারেন।
cplotts

2

এ খুঁজছি NotifyCollectionChangedEventArgs , দেখে মনে হচ্ছে OldItems শুধুমাত্র প্রতিস্থাপন করুন, সরাতে বা সরান কর্ম ফলে পরিবর্তিত আইটেম রয়েছে। এটি এতে পরিষ্কার করে না যে এতে কোনও কিছু থাকবে indicate আমি সন্দেহ করি যে ক্লিয়ার ইভেন্টটি আগুন ধরিয়ে দেয়, তবে সরানো আইটেমগুলি নিবন্ধভুক্ত করে না এবং সরান কোডটি মোটেও আবেদন করে না।


6
আমি এটিও দেখেছি, তবে আমি এটি পছন্দ করি না। এটি আমার কাছে ফাঁক গর্তের মতো মনে হচ্ছে।
cplotts

এটি সরানোর কোডটি চাইবে না কারণ এটির প্রয়োজন নেই। রিসেটের অর্থ "নাটকীয় কিছু ঘটেছে, আপনার আবার শুরু করা দরকার"। একটি পরিষ্কার অপারেশন এর একটি উদাহরণ, তবে আরও কিছু রয়েছে
অরিয়ন এডওয়ার্ডস

2

ঠিক আছে, আমি নিজেই এটিকে নোংরা করার সিদ্ধান্ত নিয়েছি।

মাইক্রোসফ্ট সর্বদা নিশ্চিত করে রাখার জন্য যে রিসেট কল করার সময় নোটিফোলকেশন চ্যাঞ্জডএভেন্টআরগসের কোনও ডেটা নেই making আমি ধরে নিচ্ছি এটি একটি পারফরম্যান্স / মেমরির সিদ্ধান্ত ছিল। আপনি যদি 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);
    }

এটি দুর্দান্ত, তবে সম্ভবত একটি সম্পূর্ণ বিশ্বাসের পরিবেশ ব্যতীত আর কোনও কাজ করবে না। ব্যক্তিগত ক্ষেত্রগুলি প্রতিফলিত করতে পুরো ভরসার প্রয়োজন আছে, তাই না?
পল

4
কেন আপনি এই করবেন? রিসেট ক্রিয়াকলাপটি আগুনের কারণ হতে পারে এমন অন্যান্য জিনিস রয়েছে - আপনি পরিষ্কার পদ্ধতিটি অক্ষম করেছেন বলে কেবল এটি চলে গেছে (বা এটি হওয়া উচিত)
অরিয়ন এডওয়ার্ডস

আকর্ষণীয় পদ্ধতির, তবে প্রতিফলন ধীর হতে পারে।
cplotts

2

পর্যবেক্ষণযোগ্য সংগ্রহের পাশাপাশি আইএনটিফাইওকোলকেশন চেঞ্জ করা ইন্টারফেসটি স্পষ্টভাবে একটি নির্দিষ্ট ব্যবহারের কথা মাথায় রেখে লেখা হয়েছে: ইউআই বিল্ডিং এবং এর নির্দিষ্ট কর্মক্ষমতা বৈশিষ্ট্য।

আপনি যখন সংগ্রহের পরিবর্তনের বিজ্ঞপ্তিগুলি চান তবে আপনি সাধারণত ইভেন্টগুলি যুক্ত করুন এবং সরানোর বিষয়ে আগ্রহী হন।

আমি নিম্নলিখিত ইন্টারফেস ব্যবহার:

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

আমি সংগ্রহের নিজস্ব ওভারলোডও লিখেছি যেখানে:

  • ক্লিয়ার আইটেমগুলি সরানো হয়
  • InsertItem উত্থাপিত যোগ
  • সরানো আইটেম অপসারণ উত্থাপন
  • সেটআইটিম সরানো এবং যুক্ত করা উত্থাপন করে

অবশ্যই, অ্যাডর্যাঞ্জ পাশাপাশি যুক্ত করা যেতে পারে।


মাইক্রোসফ্ট একটি নির্দিষ্ট ব্যবহারের কেস মাথায় রেখে ... এবং কর্মক্ষমতা উপর নজর রেখে পর্যবেক্ষণযোগ্য সংগ্রহের নকশা তৈরি করার জন্য +1 দেখানোর জন্য 1 আমি রাজী. অন্যান্য পরিস্থিতিতে গর্ত ছেড়ে, কিন্তু আমি সম্মত।
cplotts

-1 আমি সব ধরণের বিষয়ে আগ্রহী হতে পারি। প্রায়শই আমার যুক্ত / সরানো আইটেমের সূচি প্রয়োজন। আমি প্রতিস্থাপন অনুকূল করতে পারেন ইত্যাদি আইএনটিফাই কলকেশন চেঞ্জড ডিজাইনটি ভাল। যে সমস্যাটি সমাধান করা উচিত তা হ'ল এমএস এটি প্রয়োগ করেছে।
আলেকসান্দ্র ডাবিনস্কি

1

আমি সিলভারলাইট এবং ডাব্লুপিএফ টুলকিটগুলিতে কেবল কয়েকটি চার্টিং কোডটি দিয়ে যাচ্ছিলাম এবং লক্ষ্য করেছি যে তারাও এই সমস্যাটি (একই ধরণের একরকমভাবে) সমাধান করেছে ... এবং আমি ভেবেছিলাম যে আমি এগিয়ে গিয়ে তাদের সমাধান পোস্ট করব।

মূলত, তারা একটি উত্পন্ন পর্যবেক্ষণযোগ্য সংগ্রহ এবং ওভাররড ক্লিয়ার আইটেমগুলিও তৈরি করে, প্রতিটি আইটেম সাফ হওয়ার পরে মুছে ফেলার কল করে।

কোডটি এখানে:

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

এটি একটি উত্তপ্ত বিষয় ... কারণ আমার মতে মাইক্রোসফ্ট তার কাজটি সঠিকভাবে করেনি ... আবারও। আমাকে ভুল বুঝবেন না, আমি মাইক্রোসফ্ট পছন্দ করি তবে তারা নিখুঁত নয়!

আগের বেশিরভাগ মন্তব্য পড়েছি। মাইক্রোসফ্ট ক্লিয়ার () সঠিকভাবে প্রোগ্রাম করা হয়নি বলে মনে করি তাদের সকলের সাথে আমি একমত।

আমার মতে, কমপক্ষে, কোনও ইভেন্ট থেকে জিনিসগুলিকে বিচ্ছিন্ন করা সম্ভব করার জন্য এটির পক্ষে একটি যুক্তি প্রয়োজন ... তবে আমি এর প্রভাব কী তাও বুঝতে পারি। তারপরে, আমি এই প্রস্তাবিত সমাধানটি ভেবেছিলাম।

আমি আশা করি এটি সবাইকে বা কমপক্ষে সবাইকে আনন্দিত করবে ...

এরিক

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

আমি এখনও মনে করি যে মাইক্রোসফ্টের বিজ্ঞপ্তি দিয়ে সাফ করার জন্য একটি উপায় প্রদান করা উচিত। আমি এখনও মনে করি যে তারা সেইভাবে সরবরাহ না করে শটটি মিস করে। দুঃখিত! আমি বলছি না যে পরিষ্কারটি অপসারণ করা উচিত, কোনও কিছু অনুপস্থিত থাকুন !!! কম সংশ্লেষ পেতে, আমাদের মাঝে মাঝে কী সরানো হয়েছিল সে সম্পর্কে পরামর্শ দিতে হবে।
এরিক ওয়েললেট

1

এটিকে সহজ রাখতে আপনি ক্লিয়ার আইটেম পদ্ধতিটি কেন ওভাররাইড করবেন না এবং সেখানে যা চান তাই করুন না কেন ইভেন্ট থেকে আইটেমগুলি আলাদা করে নিন।

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

এখানে আমি ক্লিয়ারআইটেমস পদ্ধতিতে একটি অ্যারে ক্ষেত্রে বর্তমান উপাদানগুলিকে সংরক্ষণ করি, তারপরে আমি বেস কল চালু করার আগে অনকলেশনচেন্জড কলটি ওপরিচিত করে e._oldItems ব্যক্তিগত ক্ষেত্রটি (প্রতিচ্ছবিগুলির মাধ্যমে) ওভাররাইট করব nঅনেকশন সংগ্রহ পরিবর্তন


0

আপনি ClearItems পদ্ধতি ওভাররাইড করতে পারেন এবং সরান কর্ম এবং ওল্ড আইটেমগুলির সাথে ইভেন্টটি বাড়িয়ে তুলতে পারেন।

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.sp विशेषज्ञized.notifycollectionchangedaction(VS.95).aspx

আপনার চোখ খোলা এবং আপনার মস্তিষ্ক চালু আছে দয়া করে এই ডকুমেন্টেশন পড়ুন। মাইক্রোসফ্ট সবকিছু ঠিকঠাক করেছে। আপনার সংগ্রহ পুনরায় স্ক্যান করতে হবে যখন এটি আপনার জন্য পুনরায় সেট করার বিজ্ঞপ্তি দেয়। আপনি রিসেট বিজ্ঞপ্তি পেয়েছেন কারণ প্রতিটি আইটেমের জন্য অ্যাড / রিমুভ নিক্ষেপ করা (এখান থেকে সরানো এবং সংগ্রহে ফিরে যোগ করা) খুব ব্যয়বহুল।

অরিওন এডওয়ার্ডস পুরোপুরি সঠিক (সম্মান, মানুষ)। ডকুমেন্টেশন পড়ার সময় দয়া করে আরও প্রশস্ত মনে করুন।


4
আমি আসলে মনে করি যে মাইক্রোসফ্ট কীভাবে এটি কাজ করতে ডিজাইন করেছে সে সম্পর্কে আপনার বোঝার ক্ষেত্রে আপনি এবং ওরিয়ন সঠিক। :) এই নকশাটি আমার এমন সমস্যা তৈরি করেছে যা আমার পরিস্থিতির জন্য আমার প্রায়শই কাজ করা দরকার। এই পরিস্থিতিটিও একটি সাধারণ বিষয় ... এবং আমি কেন এই প্রশ্নটি পোস্ট করেছি।
cplotts

আমার মনে হয় আপনার আমার প্রশ্নটি (এবং চিহ্নিত উত্তর) আরও কিছুটা দেখার উচিত। আমি প্রতিটি আইটেমের জন্য সরানোর পরামর্শ দিচ্ছিলাম না।
cplotts

এবং রেকর্ডের জন্য, আমি অরিওনের উত্তরটিকে সম্মান করি ... আমি মনে করি আমরা একে অপরের সাথে সামান্য মজা করছিলাম ... কমপক্ষে আমি এটি এটি গ্রহণ করেছি।
cplotts

একটি গুরুত্বপূর্ণ বিষয়: আপনি যে বিষয়গুলি অপসারণ করছেন সেগুলি থেকে আপনাকে ইভেন্ট হ্যান্ডলিং পদ্ধতিগুলি আলাদা করতে হবে না। বিচ্ছিন্নতা স্বয়ংক্রিয়ভাবে সম্পন্ন হয়।
ডিমা

4
সুতরাং সংক্ষেপে, কোনও সংগ্রহ থেকে কোনও সামগ্রী অপসারণ করার সময় ইভেন্টগুলি স্বয়ংক্রিয়ভাবে বিচ্ছিন্ন হয় না।
cplotts

-4

যদি আপনার ObservableCollectionস্পষ্ট হচ্ছে না, তবে আপনি নীচের কোডটি চেষ্টা করে দেখতে পারেন। এটি আপনাকে সাহায্য করতে পারে:

private TestEntities context; // This is your context

context.Refresh(System.Data.Objects.RefreshMode.StoreWins, context.UserTables); // to refresh the object context
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.