Khi xóa một Bộ sưu tập có thể quan sát, Không có Mục nào trong e.OldItems


91

Tôi có một cái gì đó ở đây khiến tôi thực sự mất cảnh giác.

Tôi có một ObservableCollection của T chứa đầy các mục. Tôi cũng có một trình xử lý sự kiện được đính kèm với sự kiện CollectionChanged.

Khi bạn Xóa bộ sưu tập, nó gây ra sự kiện CollectionChanged với e.Action được đặt thành NotifyCollectionChangedAction.Reset. Ok, đó là bình thường. Nhưng điều kỳ lạ là cả e.OldItems hay e.NewItems đều không có bất cứ thứ gì trong đó. Tôi mong đợi e.OldItems chứa đầy tất cả các mục đã bị xóa khỏi bộ sưu tập.

Đã có người khác nhìn thấy điều này? Và nếu vậy, họ đã làm thế nào để vượt qua nó?

Một số thông tin cơ bản: Tôi đang sử dụng sự kiện CollectionChanged để đính kèm và tách khỏi sự kiện khác và do đó nếu tôi không nhận được bất kỳ mục nào trong e.OldItems ... tôi sẽ không thể tách khỏi sự kiện đó.


XÁC NHẬN: Tôi biết rằng tài liệu không nói rõ rằng nó phải hoạt động theo cách này. Nhưng đối với mọi hành động khác, nó sẽ thông báo cho tôi về những gì nó đã làm. Vì vậy, giả định của tôi là nó sẽ cho tôi biết ... trong trường hợp Xóa / Đặt lại.


Dưới đây là mã mẫu nếu bạn muốn tự sao chép lại. Trước hết 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>

Tiếp theo, mã đằng sau:

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

Tại sao bạn cần hủy đăng ký sự kiện? Bạn đang đăng ký theo hướng nào? Các sự kiện tạo ra một tham chiếu đến người đăng ký do người xếp hạng nắm giữ chứ không phải ngược lại. Nếu người chăn nuôi là các mục trong bộ sưu tập được dọn sạch, chúng sẽ được thu gom rác một cách an toàn và các tham chiếu sẽ biến mất - không bị rò rỉ. Nếu các mục là người đăng ký và được tham chiếu bởi một người xếp hạng, thì chỉ cần đặt sự kiện thành vô hiệu trong người xếp hạng khi bạn nhận được Đặt lại - không cần phải hủy đăng ký từng mục.
Aleksandr Dubinsky,

Tin tôi đi, tôi biết nó hoạt động như thế nào. Sự kiện được đề cập là trên một singleton đã tồn tại trong một thời gian dài ... do đó các mục trong bộ sưu tập là người đăng ký. Giải pháp của bạn là chỉ đặt sự kiện thành null không hoạt động ... vì sự kiện vẫn cần kích hoạt ... có thể thông báo cho những người đăng ký khác (không nhất thiết phải là những người trong bộ sưu tập).
cplotts

Câu trả lời:


46

Nó không yêu cầu bao gồm các mục cũ, bởi vì Đặt lại không có nghĩa là danh sách đã bị xóa

Nó có nghĩa là một số điều kịch tính đã xảy ra, và chi phí để tính toán thêm / bớt rất có thể sẽ vượt quá chi phí chỉ quét lại danh sách từ đầu ... vì vậy đó là điều bạn nên làm.

MSDN gợi ý một ví dụ về toàn bộ bộ sưu tập được sắp xếp lại như một ứng cử viên để đặt lại.

Để nhắc lại. Đặt lại không có nghĩa là rõ ràng , nó có nghĩa là các giả định của bạn về danh sách bây giờ không hợp lệ. Coi nó như thể đó là một danh sách hoàn toàn mới . Rõ ràng là một trường hợp của điều này, nhưng cũng có thể có những trường hợp khác.

Một số ví dụ:
Tôi đã có một danh sách như thế này với rất nhiều mục trong đó và nó đã được lập cơ sở dữ liệu thành một WPF ListViewđể hiển thị trên màn hình.
Nếu bạn xóa danh sách và nâng cao .Resetsự kiện, hiệu suất là khá nhiều ngay lập tức, nhưng nếu bạn tăng nhiều .Removesự kiện riêng lẻ , hiệu suất rất khủng khiếp, vì WPF loại bỏ từng mục một. Tôi cũng đã sử dụng .Resetmã của riêng mình để chỉ ra rằng danh sách đã được sắp xếp lại, thay vì đưa ra hàng nghìn Movethao tác riêng lẻ . Như với Clear, có một hiệu suất lớn khi nâng cao nhiều sự kiện riêng lẻ.


1
Tôi sẽ tôn trọng không đồng ý trên cơ sở này. Nếu bạn xem tài liệu, nó cho biết: Đại diện cho một bộ sưu tập dữ liệu động cung cấp thông báo khi các mục được thêm, xóa hoặc khi toàn bộ danh sách được làm mới (xem msdn.microsoft.com/en-us/library/ms668613(v=VS .100) .aspx )
cplotts

6
Tài liệu nói rằng nó sẽ thông báo cho bạn khi các mục được thêm / xóa / làm mới, nhưng nó không hứa hẹn sẽ cho bạn biết tất cả chi tiết của các mục ... chỉ là sự kiện đã xảy ra. Từ quan điểm này, hành vi là tốt. Cá nhân tôi nghĩ rằng họ nên đưa tất cả các mục vào OldItemskhi thanh toán bù trừ, (nó chỉ là sao chép một danh sách), nhưng có lẽ đã có một số trường hợp mà điều này quá đắt. Dù sao đi nữa, nếu bạn muốn có một bộ sưu tập mà không thông báo cho bạn của tất cả các mục đã xóa, nó sẽ không khó để làm.
Orion Edwards,

2
Chà, nếu Resetlà để chỉ ra một phép toán đắt tiền, rất có thể lý do tương tự cũng áp dụng cho việc sao chép toàn bộ danh sách sang OldItems.
pbalaga

7
Thực tế là: kể từ .NET 4.5 , Resetthực sự có nghĩa là "Nội dung của bộ sưu tập đã bị xóa ." Xem msdn.microsoft.com/en-us/library/…
Athari

9
Câu trả lời này không giúp được nhiều, xin lỗi. Có, bạn có thể quét lại toàn bộ danh sách nếu bạn nhận được Đặt lại, nhưng bạn không có quyền truy cập để xóa các mục, bạn có thể cần xóa các trình xử lý sự kiện khỏi chúng. Đây là một vấn đề lớn.
Virus721

22

Chúng tôi đã có cùng một vấn đề ở đây. Hành động Reset trong CollectionChanged không bao gồm OldItems. Chúng tôi đã có một cách giải quyết: thay vào đó chúng tôi đã sử dụng phương pháp mở rộng sau:

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

Cuối cùng, chúng tôi đã không hỗ trợ hàm Clear () và ném sự kiện NotSupportedException trong CollectionChanged cho các hành động Đặt lại. RemoveAll sẽ kích hoạt hành động Xóa trong sự kiện CollectionChanged, với các OldItems thích hợp.


Ý tưởng tốt. Tôi không thích việc không hỗ trợ Clear vì đó là phương pháp (theo kinh nghiệm của tôi) mà hầu hết mọi người sử dụng ... nhưng ít nhất bạn đang cảnh báo người dùng với một ngoại lệ.
cplotts 22/10/08

Tôi đồng ý, đây không phải là giải pháp lý tưởng, nhưng chúng tôi thấy nó là giải pháp tốt nhất có thể chấp nhận được.
decasteljau 22/10/08

Bạn không nên sử dụng các mặt hàng cũ! Những gì bạn phải làm là kết xuất bất kỳ dữ liệu nào bạn có trong danh sách và quét lại nó như thể đó là một danh sách mới!
Orion Edwards

16
Vấn đề, Orion, với gợi ý của bạn ... là trường hợp sử dụng đã gợi ra câu hỏi này. Điều gì xảy ra khi tôi có các mục trong danh sách mà tôi muốn tách một sự kiện khỏi đó? Tôi không thể chỉ kết xuất dữ liệu trong danh sách ... nó sẽ dẫn đến rò rỉ / áp lực bộ nhớ.
cplotts

5
Vấn đề chính của giải pháp này là nếu bạn xóa 1000 mục, bạn kích hoạt CollectionChanged 1000 lần và giao diện người dùng phải cập nhật CollectionView 1000 lần (cập nhật các phần tử giao diện người dùng rất tốn kém). Nếu bạn không ngại ghi đè lớp ObservableCollection, bạn có thể làm cho nó kích hoạt sự kiện Clear () nhưng cung cấp các Args sự kiện chính xác cho phép mã giám sát hủy đăng ký tất cả các phần tử đã loại bỏ.
Alain

13

Một tùy chọn khác là thay thế sự kiện Đặt lại bằng một sự kiện Xóa có tất cả các mục đã xóa trong thuộc tính OldItems của nó như sau:

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

Ưu điểm:

  1. Không cần đăng ký sự kiện bổ sung (theo yêu cầu của câu trả lời được chấp nhận)

  2. Không tạo sự kiện cho từng đối tượng đã bị xóa (một số giải pháp được đề xuất khác dẫn đến nhiều sự kiện Đã xóa).

  3. Người đăng ký chỉ cần kiểm tra NewItems & OldItems trên bất kỳ sự kiện nào để thêm / xóa trình xử lý sự kiện theo yêu cầu.

Nhược điểm:

  1. Không có sự kiện Đặt lại

  2. Chi phí nhỏ (?) Tạo bản sao danh sách.

  3. ???

CHỈNH SỬA 2012-02-23

Thật không may, khi bị ràng buộc với các điều khiển dựa trên danh sách WPF, việc xóa tập hợp ObservableCollectionNoReset với nhiều phần tử sẽ dẫn đến ngoại lệ "Hành động phạm vi không được hỗ trợ". Để được sử dụng với các điều khiển có giới hạn này, tôi đã thay đổi lớp ObservableCollectionNoReset thành:

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

Điều này không hiệu quả khi RangeActionsSupported là false (mặc định) vì một thông báo Xóa được tạo cho mỗi đối tượng trong bộ sưu tập


Tôi thích điều này nhưng tiếc là Silverlight 4 NotifyCollectionChangedEventArgs không có hàm tạo lấy danh sách các mục.
Simon Brangwin

2
Tôi thích giải pháp này, nhưng nó không hoạt động ... Bạn không được phép tạo NotifyCollectionChangedEventArgs đã thay đổi nhiều mục trừ khi hành động là "Đặt lại". Bạn có một ngoại lệ, Range actions are not supported.tôi không biết tại sao nó lại làm như vậy, nhưng bây giờ điều này không còn tùy chọn nào khác ngoài việc xóa từng mục một ...
Alain

2
@Alain ObservableCollection không áp đặt hạn chế này. Tôi nghi ngờ đó là điều khiển WPF mà bạn đã ràng buộc bộ sưu tập. Tôi đã gặp vấn đề tương tự và chưa bao giờ đăng cập nhật giải pháp của mình. Tôi sẽ chỉnh sửa câu trả lời của mình với lớp đã sửa đổi hoạt động khi bị ràng buộc với điều khiển WPF.
Grantnz

Tôi thấy điều đó bây giờ. Tôi thực sự đã tìm thấy một giải pháp rất thanh lịch là ghi đè sự kiện CollectionChanged và lặp lại foreach( NotifyCollectionChangedEventHandler handler in this.CollectionChanged )If handler.Target is CollectionView, sau đó bạn có thể loại bỏ trình xử lý với Action.Resetargs, nếu không, bạn có thể cung cấp args đầy đủ. Tốt nhất của cả hai thế giới trên cơ sở xử lý theo trình xử lý :). Giống như những gì ở đây: stackoverflow.com/a/3302917/529618
Alain

Tôi đã đăng giải pháp của riêng tôi dưới đây. stackoverflow.com/a/9416535/529618 Rất cảm ơn bạn vì giải pháp đầy cảm hứng của bạn. Nó đã đưa tôi đến đó một nửa.
Alain

10

Được rồi, tôi biết đây là một câu hỏi rất cũ nhưng tôi đã đưa ra một giải pháp tốt cho vấn đề và nghĩ rằng tôi sẽ chia sẻ. Giải pháp này lấy cảm hứng từ rất nhiều câu trả lời tuyệt vời ở đây nhưng có những ưu điểm sau:

  • Không cần tạo một lớp mới và ghi đè các phương thức từ ObservableCollection
  • Không ảnh hưởng đến hoạt động của NotifyCollectionChanged (vì vậy không gây rối với Đặt lại)
  • Không sử dụng phản xạ

Đây là mã:

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

Phương thức mở rộng này chỉ cần lấy một Actionsẽ được gọi trước khi bộ sưu tập bị xóa.


Ý kiến ​​rất hay. Đơn giản, thanh lịch.
cplotts

9

Tôi đã tìm thấy một giải pháp cho phép người dùng tận dụng cả hiệu quả của việc thêm hoặc xóa nhiều mục cùng một lúc trong khi chỉ kích hoạt một sự kiện - và đáp ứng nhu cầu của UIElements để có được Hành động. như danh sách các yếu tố được thêm vào và loại bỏ.

Giải pháp này liên quan đến việc ghi đè sự kiện CollectionChanged. Khi bắt đầu sự kiện này, chúng tôi thực sự có thể xem xét mục tiêu của từng trình xử lý đã đăng ký và xác định loại của chúng. Vì chỉ các lớp ICollectionView yêu cầu các NotifyCollectionChangedAction.Resetargs khi nhiều mục thay đổi, chúng tôi có thể tách chúng ra và cung cấp cho mọi người các args sự kiện thích hợp khác chứa danh sách đầy đủ các mục đã bị xóa hoặc thêm. Dưới đây là cách thực hiện.

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

Ok, mặc dù tôi vẫn ước rằng ObservableCollection hoạt động như tôi mong muốn ... đoạn mã dưới đây là những gì tôi đã làm. Về cơ bản, tôi đã tạo một bộ sưu tập T mới được gọi là TrulyObservableCollection và ghi đè phương thức ClearItems mà sau đó tôi đã sử dụng để tăng sự kiện Xóa.

Trong mã sử dụng TrulyObservableCollection này, tôi sử dụng sự kiện Xóa này để lặp qua các mục vẫn còn trong bộ sưu tập tại thời điểm đó để thực hiện tách đối với sự kiện mà tôi muốn tách khỏi.

Hy vọng cách tiếp cận này cũng giúp ích cho người khác.

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
Bạn cần đổi tên lớp của mình thành BrokenObservableCollection, không phải TrulyObservableCollection- bạn đang hiểu sai ý nghĩa của hành động đặt lại.
Orion Edwards

1
@Orion Edwards: Tôi không đồng ý. Xem bình luận của tôi cho câu trả lời của bạn.
cplotts

1
@Orion Edwards: Ồ, chờ đã, tôi hiểu rồi, bạn đang rất hài hước. Nhưng sau đó tôi thực sự nên gọi nó là: ActuallyUsefulObservableCollection. :)
cplotts

6
Lol tên tuyệt vời. Tôi đồng ý rằng đây là một sự giám sát nghiêm túc trong thiết kế.
defos1

1
Nếu bạn vẫn đang triển khai một lớp ObservableCollection mới, thì không cần phải tạo một sự kiện mới phải được theo dõi riêng biệt. Bạn có thể chỉ cần ngăn ClearItems kích hoạt một Hành động = Đặt lại các nhóm sự kiện và thay thế nó bằng một Hành động = Xóa các nhóm sự kiện có chứa danh sách e.OldItems của tất cả các mục có trong danh sách. Xem các giải pháp khác trong câu hỏi này.
Alain

4

Tôi đã giải quyết vấn đề này theo một cách hơi khác vì tôi muốn đăng ký một sự kiện và xử lý tất cả các bổ sung và xóa trong trình xử lý sự kiện. Tôi bắt đầu ghi đè sự kiện đã thay đổi bộ sưu tập và chuyển hướng các hành động đặt lại thành các hành động xóa với một danh sách các mục. Tất cả điều này đã sai vì tôi đang sử dụng bộ sưu tập có thể quan sát làm nguồn mục cho chế độ xem bộ sưu tập và nhận được "Hành động phạm vi không được hỗ trợ".

Cuối cùng tôi đã tạo một sự kiện mới có tên là CollectionChangedRange hoạt động theo cách mà tôi mong đợi phiên bản sẵn có sẽ hoạt động.

Tôi không thể tưởng tượng tại sao giới hạn này lại được cho phép và hy vọng rằng bài đăng này ít nhất ngăn những người khác đi vào ngõ cụt như tôi đã làm.

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

}

Cách tiếp cận thú vị. Cảm ơn vì đã đăng nó. Nếu tôi gặp vấn đề với cách tiếp cận của riêng mình, tôi nghĩ tôi sẽ xem lại cách tiếp cận của bạn.
cplotts

3

Đây là cách hoạt động của ObservableCollection, bạn có thể giải quyết vấn đề này bằng cách giữ danh sách của riêng bạn bên ngoài ObservableCollection (thêm vào danh sách khi hành động là Thêm, xóa khi hành động là Xóa, v.v.) sau đó bạn có thể nhận được tất cả các mục đã xóa (hoặc các mục đã thêm ) khi hành động được Đặt lại bằng cách so sánh danh sách của bạn với ObservableCollection.

Một tùy chọn khác là tạo lớp của riêng bạn triển khai IList và INotifyCollectionChanged, sau đó bạn có thể đính kèm và tách các sự kiện từ bên trong lớp đó (hoặc đặt OldItems thành Clear nếu bạn thích) - nó thực sự không khó, nhưng phải nhập rất nhiều.


Tôi đã xem xét việc theo dõi một danh sách khác cũng như bạn đề xuất trước, nhưng có vẻ như rất nhiều việc không cần thiết. Đề xuất thứ hai của bạn rất gần với những gì tôi đã kết thúc với ... mà tôi sẽ đăng như một câu trả lời.
cplotts

3

Đối với kịch bản gắn và tách các trình xử lý sự kiện với các phần tử của ObservableCollection, cũng có một giải pháp "phía máy khách". Trong mã xử lý sự kiện, bạn có thể kiểm tra xem người gửi có ở trong ObservableCollection hay không bằng cách sử dụng phương thức Contains. Chuyên nghiệp: bạn có thể làm việc với bất kỳ ObservableCollection nào hiện có. Nhược điểm: phương thức Contains chạy với O (n) với n là số phần tử trong ObservableCollection. Vì vậy, đây là một giải pháp cho các Bộ sưu tập quan sát nhỏ.

Một giải pháp "phía máy khách" khác là sử dụng trình xử lý sự kiện ở giữa. Chỉ cần đăng ký tất cả các sự kiện vào trình xử lý sự kiện ở giữa. Đến lượt nó, trình xử lý sự kiện này sẽ thông báo cho trình xử lý sự kiện thực một cuộc gọi lại hoặc một sự kiện. Nếu một hành động Đặt lại xảy ra, hãy xóa lệnh gọi lại hoặc sự kiện tạo một trình xử lý sự kiện mới ở giữa và quên đi cái cũ. Cách tiếp cận này cũng hoạt động đối với các Bộ sưu tập quan sát lớn. Tôi đã sử dụng điều này cho sự kiện PropertyChanged (xem mã bên dưới).

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

Tôi tin rằng với cách tiếp cận đầu tiên của bạn, tôi sẽ cần một danh sách khác để theo dõi các mục ... bởi vì khi bạn nhận được sự kiện CollectionChanged với hành động Đặt lại ... thì bộ sưu tập đã trống. Tôi không hoàn toàn làm theo gợi ý thứ hai của bạn. Tôi rất thích một khai thác thử nghiệm đơn giản minh họa nó, nhưng để thêm, xóa và xóa ObservableCollection. Nếu bạn xây dựng một ví dụ, bạn có thể gửi email cho tôi theo tên của tôi sau đó là họ của tôi tại gmail.com.
cplotts

2

Nhìn vào NotifyCollectionChangedEventArgs , có vẻ như OldItems chỉ chứa các mục được thay đổi do hành động Thay thế, Xóa hoặc Di chuyển. Nó không chỉ ra rằng nó sẽ chứa bất kỳ thứ gì trên Clear. Tôi nghi ngờ rằng Clear kích hoạt sự kiện, nhưng không đăng ký các mục đã xóa và hoàn toàn không gọi mã Xóa.


6
Tôi cũng thấy vậy, nhưng tôi không thích nó. Nó dường như là một lỗ hổng đối với tôi.
cplotts

Nó không gọi mã loại bỏ vì nó không cần thiết. Reset có nghĩa là "điều gì đó kịch tính đã xảy ra, bạn cần bắt đầu lại". Một hoạt động rõ ràng là một ví dụ về điều này, nhưng có những người khác
Orion Edwards

2

Chà, tôi quyết định tự mình làm bẩn nó.

Microsoft đã nỗ lực rất nhiều để luôn đảm bảo NotifyCollectionChangedEventArgs không có bất kỳ dữ liệu nào khi gọi đặt lại. Tôi giả định đây là một quyết định về hiệu suất / bộ nhớ. Nếu bạn đang đặt lại một bộ sưu tập có 100.000 phần tử, tôi cho rằng họ không muốn sao chép tất cả các phần tử đó.

Nhưng vì bộ sưu tập của tôi không bao giờ có nhiều hơn 100 phần tử nên tôi không thấy có vấn đề gì với nó.

Dù sao, tôi đã tạo một lớp kế thừa với phương thức sau:

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

Điều này thật tuyệt, nhưng có lẽ sẽ không hoạt động trong bất cứ điều gì ngoài một môi trường đầy tin tưởng. Phản ánh trên các lĩnh vực riêng tư đòi hỏi sự tin tưởng hoàn toàn, phải không?
Paul

1
Tại sao bạn sẽ làm điều này? Có những thứ khác có thể gây ra những hành động Reset để lửa - chỉ vì bạn đã vô hiệu hóa các phương pháp rõ ràng không có nghĩa là nó biến mất (hoặc là nó nên)
Orion Edwards

Cách tiếp cận thú vị, nhưng phản ánh có thể chậm.
cplotts

2

Giao diện ObservableCollection cũng như giao diện INotifyCollectionChanged được viết rõ ràng với mục đích sử dụng cụ thể: xây dựng giao diện người dùng và các đặc điểm hiệu suất cụ thể của nó.

Khi bạn muốn thông báo về những thay đổi trong bộ sưu tập thì bạn thường chỉ quan tâm đến các sự kiện Thêm và Xóa.

Tôi sử dụng giao diện sau:

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

Tôi cũng đã viết quá tải Bộ sưu tập của riêng mình trong đó:

  • ClearItems tăng loại bỏ
  • InsertItem tăng Đã thêm
  • RemoveItem tăng Gỡ bỏ
  • SetItem tăng Xóa và Thêm

Tất nhiên, AddRange cũng có thể được thêm vào.


+1 vì chỉ ra rằng Microsoft đã thiết kế ObservableCollection với một trường hợp sử dụng cụ thể ... và chú ý đến hiệu suất. Tôi đồng ý. Để lại một lỗ hổng cho các tình huống khác, nhưng tôi đồng ý.
cplotts

-1 Tôi có thể quan tâm đến tất cả mọi thứ. Thường thì tôi cần chỉ mục của các mục được thêm / bớt. Tôi có thể muốn tối ưu hóa thay thế. Vv. Thiết kế của INotifyCollectionChanged rất tốt. Vấn đề cần được khắc phục là không có ai ở MS thực hiện nó.
Aleksandr Dubinsky

1

Tôi vừa xem qua một số mã biểu đồ trong bộ công cụ Silverlight và WPF và nhận thấy rằng chúng cũng giải quyết được vấn đề này (theo một cách tương tự) ... và tôi nghĩ rằng tôi sẽ tiếp tục và đăng giải pháp của họ.

Về cơ bản, họ cũng tạo ra một ObservableCollection có nguồn gốc và ghi đè ClearItems, gọi Remove trên mỗi mục được xóa.

Đây là mã:

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

Tôi chỉ muốn chỉ ra rằng tôi không thích cách tiếp cận này nhiều như cách tôi đã đánh dấu là câu trả lời ... vì bạn nhận được sự kiện NotifyCollectionChanged (với hành động Xóa) ... cho TỪNG mục bị xóa.
cplotts

1

Đây là một chủ đề nóng ... bởi vì theo quan điểm của tôi, Microsoft đã không làm đúng chức năng của mình ... một lần nữa. Đừng hiểu lầm tôi, tôi thích Microsoft, nhưng chúng không hoàn hảo!

Tôi đã đọc hầu hết các bình luận trước đây. Tôi đồng ý với tất cả những người nghĩ rằng Microsoft đã không lập trình Clear () đúng cách.

Theo tôi, ít nhất, nó cần một lập luận để có thể tách các đối tượng ra khỏi một sự kiện ... nhưng tôi cũng hiểu tác động của nó. Sau đó, tôi nghĩ ra giải pháp đề xuất này.

Tôi hy vọng nó sẽ làm cho mọi người hạnh phúc, hoặc ít nhất, hầu hết mọi người ...

Eric

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

Tôi vẫn nghĩ rằng Microsoft nên cung cấp một cách để có thể thông báo rõ ràng. Tôi vẫn nghĩ rằng họ bắn trượt khi không cung cấp theo cách đó. Lấy làm tiếc ! Tôi không nói rằng rõ ràng nên được loại bỏ, có thể thiếu một cái gì đó !!! Để có được khớp nối thấp, đôi khi chúng tôi phải được tư vấn về những gì đã được loại bỏ.
Eric Ouellet

1

Để đơn giản, tại sao bạn không ghi đè phương thức ClearItem và làm bất cứ điều gì bạn muốn ở đó, tức là Tách các mục khỏi sự kiện.

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

  rest of the code omitted
}

Đơn giản, sạch sẽ và chứa trong mã bộ sưu tập.


Điều đó rất gần với những gì tôi đã thực sự ... hãy xem câu trả lời được chấp nhận.
cplotts

0

Tôi đã gặp vấn đề tương tự, và đây là giải pháp của tôi. Nó dường như hoạt động. Có ai thấy bất kỳ vấn đề tiềm ẩn nào với cách tiếp cận này không?

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

Dưới đây là một số phương pháp hữu ích khác trong lớp của tôi:

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

Tôi đã tìm thấy một giải pháp "đơn giản" khác bắt nguồn từ ObservableCollection, nhưng nó không được thanh lịch lắm vì nó sử dụng Reflection ... Nếu bạn thích nó thì đây là giải pháp của tôi:

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

Ở đây tôi lưu các phần tử hiện tại trong một trường mảng trong phương thức ClearItems, sau đó tôi chặn lệnh gọi của OnCollectionChanged và ghi đè trường cá nhân e._oldItems (thông qua Reflections) trước khi khởi chạy base.OnCollectionChanged


0

Bạn có thể ghi đè phương thức ClearItems và nâng cao sự kiện với Hành động xóa và 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));
    }
}

Một phần của System.Collections.ObjectModel.ObservableCollection<T>hiện thực:

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

Vui lòng đọc tài liệu này với đôi mắt của bạn mở và bộ não của bạn hoạt động. Microsoft đã làm mọi thứ đúng. Bạn phải quét lại bộ sưu tập của mình khi bộ sưu tập đưa ra thông báo Đặt lại cho bạn. Bạn nhận được thông báo Đặt lại vì việc bổ sung Thêm / Xóa cho từng mục (bị xóa và thêm trở lại bộ sưu tập) quá đắt.

Orion Edwards hoàn toàn đúng (tôn trọng, anh bạn). Vui lòng suy nghĩ rộng hơn khi đọc tài liệu.


5
Tôi thực sự nghĩ rằng bạn và Orion đã đúng khi hiểu về cách Microsoft thiết kế nó hoạt động. :) Tuy nhiên, thiết kế này gây ra cho tôi những vấn đề mà tôi cần phải giải quyết cho tình huống của mình. Tình huống này cũng là một tình huống phổ biến ... và tại sao tôi đăng câu hỏi này.
cplotts

Tôi nghĩ bạn nên xem xét câu hỏi của tôi (và câu trả lời được đánh dấu) nhiều hơn một chút. Tôi đã không đề xuất loại bỏ cho mọi mục.
cplotts

Và về phần ghi âm, tôi tôn trọng câu trả lời của Orion ... Tôi nghĩ chúng tôi chỉ vui vẻ với nhau một chút thôi ... ít nhất đó là cách tôi nắm bắt nó.
cplotts

Một điều quan trọng: bạn không phải tách các thủ tục xử lý sự kiện khỏi các đối tượng mà bạn đang loại bỏ. Việc tách được thực hiện tự động.
Dima

1
Vì vậy, tóm lại, các sự kiện không tự động được tách ra khi xóa một đối tượng khỏi một tập hợp.
cplotts

-4

Nếu bạn ObservableCollectionkhông hiểu rõ, thì bạn có thể thử đoạn mã dưới đây. nó có thể giúp bạn:

private TestEntities context; // This is your context

context.Refresh(System.Data.Objects.RefreshMode.StoreWins, context.UserTables); // to refresh the object context
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.