2008-09-11 17 views
31

Đã xem xét lớp System.Collections.ObjectModel ObservableCollection<T>. Điều này là lạ bởi vìBộ sưu tập .Net nào để thêm nhiều đối tượng cùng một lúc và được thông báo?

  • nó có phương thức bổ sung chỉ cần một mặt hàng. Không có AddRange hoặc tương đương.
  • các đối số sự kiện thông báo có một tài sản NewItems, mà là một IList (các đối tượng .. không T)

nhu cầu của tôi ở đây là thêm một loạt các đối tượng đến một bộ sưu tập và người nghe cũng được lô như một phần của thông báo. Tôi có thiếu cái gì đó với ObservableCollection không? Có lớp học nào khác đáp ứng thông số của tôi không?

Cập nhật: Không muốn cuộn của riêng tôi càng xa càng tốt. Tôi sẽ phải xây dựng thêm/xóa/thay đổi vv .. toàn bộ rất nhiều thứ.


Q liên quan:
https://stackoverflow.com/questions/670577/observablecollection-doesnt-support-addrange-method-so-i-get-notified-for-each

+4

Gishu, cẩn thận, nếu bạn liên kết với một listview hầu hết các triển khai ở đây sẽ thổi lên. –

Trả lời

1

Kế thừa từ Danh sách < T> và ghi đè Add() và AddRange() phương pháp để nâng cao một sự kiện?

2

Nếu bạn muốn kế thừa từ một bộ sưu tập sắp xếp, bạn có thể tốt hơn khi kế thừa từ System.Collections.ObjectModel.Collection vì nó cung cấp các phương thức ảo để ghi đè. Bạn sẽ phải tô bóng các phương thức trong Danh sách nếu bạn đi tuyến đường đó.

Tôi không biết về bất kỳ tích hợp trong bộ sưu tập mà cung cấp chức năng này, mặc dù tôi muốn chào đón được sửa chữa :)

18

Dường như giao diện INotifyCollectionChanged cho phép cập nhật khi nhiều mặt hàng được thêm vào, vì vậy tôi 'không chắc chắn lý do tại sao ObservableCollection<T> không có AddRange. Bạn có thể tạo phương thức mở rộng cho AddRange, nhưng điều đó sẽ gây ra sự kiện cho mọi mục được thêm vào. Nếu điều đó không được chấp nhận, bạn sẽ có thể kế thừa từ ObservableCollection<T> như sau:

public class MyObservableCollection<T> : ObservableCollection<T> 
{ 
    // matching constructors ... 

    bool isInAddRange = false; 

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) 
    { 
     // intercept this when it gets called inside the AddRange method. 
     if (!isInAddRange) 
      base.OnCollectionChanged(e); 
    } 


    public void AddRange(IEnumerable<T> items) 
    { 
     isInAddRange = true; 
     foreach (T item in items) 
      Add(item); 
     isInAddRange = false; 

     var e = new NotifyCollectionChangedEventArgs(
      NotifyCollectionChangedAction.Add, 
      items.ToList()); 
     base.OnCollectionChanged(e); 
    } 
} 
+0

Đoạn mã cần chỉnh sửa .. không có ToList() trong IEnumerable và AddRange cần có ICollection để nhất quán ... Vì tôi phải cuộn qua vùng tạm thời này để lên kế hoạch hàng tuần , đăng mẫu mã của tôi .. ngắn hơn một chút. – Gishu

+2

Gishu, phương thức ToList() là một phương thức mở rộng LINQ có sẵn trên IEnumerable. –

+0

OK ... Bạn cần đặt cài đặt dự án để sử dụng .NET 3.5 và thêm tham chiếu lắp ráp LINQ và sử dụng chỉ thị để lấy nó. – Gishu

-1

Phương pháp mở rộng Man để cứu hộ!

/// <summary> 
    /// Adds all given items to the collection 
    /// </summary> 
    /// <param name="collection">The collection.</param> 
    /// <param name="toAdd">Objects to add.</param> 
    public static void AddAll<T>(this IList<T> collection, params T[] toAdd) 
    { 
     foreach (var o in toAdd) 
      collection.Add(o); 
    } 
+0

Đây là sự chậm chạp ... ví dụ: thử thêm 3000 mục vào một quan sát được bộ sưu tập (nói lời tạm biệt với ui trong 3 phút) –

+1

Nếu bạn bị ràng buộc với giao diện người dùng, vâng, rất có thể. http://blogs.msdn.com/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx – Will

+0

Bổ sung tốt vào các giải pháp khác nhưng có lẽ không phải là tốt nhất về hiệu suất. Miễn là việc tạo ra các bộ sưu tập của riêng bạn là một lựa chọn, tôi nghĩ rằng các giải pháp khác có lẽ tốt hơn trong thời gian dài. – jpierson

4

Không chỉ là System.Collections.ObjectModel.Collection<T> cược tốt, nhưng trong các tài liệu giúp đỡ có an example làm thế nào để ghi đè các phương pháp bảo vệ khác nhau của nó để nhận được thông báo. (Cuộn xuống Ví dụ 2.)

+0

cảm ơn - liên kết hữu ích ... đã đưa ra một lưu ý về tinh thần. – Gishu

6

Vâng ý tưởng cũng giống như ý tưởng của fryguybob - kỳ lạ là ObservableCollection được thực hiện một nửa. Các args sự kiện cho điều này thậm chí không sử dụng Generics .. làm cho tôi sử dụng một IList (đó là như vậy .. ngày hôm qua :) Tested Snippet sau ...

using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Collections.Specialized; 

namespace MyNamespace 
{ 
    public class ObservableCollectionWithBatchUpdates<T> : ObservableCollection<T> 
    { 
     public void AddRange(ICollection<T> obNewItems) 
     { 
      IList<T> obAddedItems = new List<T>(); 
      foreach (T obItem in obNewItems) 
      { 
       Items.Add(obItem); 
       obAddedItems.Add(obItem); 
      } 
      NotifyCollectionChangedEventArgs obEvtArgs = new NotifyCollectionChangedEventArgs(
       NotifyCollectionChangedAction.Add, 
       obAddedItems as System.Collections.IList); 
      base.OnCollectionChanged(obEvtArgs); 
     } 

    } 
} 
+4

Tôi đã thử phương pháp này trước đây. Thật không may điều này sẽ không làm việc cho các ràng buộc WPF, bởi vì thông báo cho một số mục không được hỗ trợ. Xem [lỗi này] (https://connect.microsoft.com/WPF/feedback/details/514922/range-actions-not-supported-in-collectionview) trên MS Connect –

4

Nếu bạn sử dụng bất kỳ triển khai trên đó gửi một lệnh thêm phạm vi và ràng buộc các observablecolletion vào một listview bạn sẽ nhận được lỗi này khó chịu.

 
NotSupportedException 
    at System.Windows.Data.ListCollectionView.ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs e) 
    at System.Windows.Data.ListCollectionView.ProcessCollectionChanged(NotifyCollectionChangedEventArgs args) 
    at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e) 
    at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e) 

Việc thực hiện tôi đã đi với sử dụng các sự kiện Đặt lại được đồng đều hơn thực hiện xung quanh khuôn khổ WPF:

public void AddRange(IEnumerable<T> collection) 
    { 
     foreach (var i in collection) Items.Add(i); 
     OnPropertyChanged("Count"); 
     OnPropertyChanged("Item[]"); 
     OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); 
    } 
+0

bạn có thể đi kèm với một add cho mỗi mục, nhưng giao diện người dùng của bạn sẽ thu thập dữ liệu để tạm dừng –

+0

Thực ra, không có giao diện người dùng WPF sẽ hiển thị theo lô, dưới dạng đăng tải. Chức năng này sẽ chấm dứt trước khi giao diện người dùng được chọn để làm mới. – cunningdave

+0

Bất cứ ai đã thử điều này với xags WPF datagrid? – springy76

0

Đối nhanh thêm bạn có thể sử dụng:

((List<Person>)this.Items).AddRange(NewItems); 
3

Tôi đã thấy loại câu hỏi nhiều lần, và tôi tự hỏi tại sao ngay cả Microsoft đang xúc tiến ObservableCollection khắp nơi nơi nào khác có một bộ sưu tập tốt hơn rồi thats sẵn ..

BindingList<T>

nào cho phép bạn tắt các thông báo và làm opera số lượng lớn và sau đó bật thông báo.

+1

Điểm rất tốt về 'BindingList ' nhưng thật không may nó triển khai 'IBindingList ' thay vì 'IObservable ' ... Sau đó là cần thiết cho các ứng dụng WPF bằng cách sử dụng mvvm. – RonnBlack

1

Một giải pháp tương tự như mô hình CollectionView:

public class DeferableObservableCollection<T> : ObservableCollection<T> 
{ 
    private int deferLevel; 

    private class DeferHelper<T> : IDisposable 
    { 
     private DeferableObservableCollection<T> owningCollection; 
     public DeferHelper(DeferableObservableCollection<T> owningCollection) 
     { 
      this.owningCollection = owningCollection; 
     } 

     public void Dispose() 
     { 
      owningCollection.EndDefer(); 
     } 
    } 

    private void EndDefer() 
    { 
     if (--deferLevel <= 0) 
     { 
      deferLevel = 0; 
      OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); 
     } 
    } 

    public IDisposable DeferNotifications() 
    { 
     deferLevel++; 
     return new DeferHelper<T>(this); 
    } 

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) 
    { 
     if (deferLevel == 0) // Not in a defer just send events as normally 
     { 
      base.OnCollectionChanged(e); 
     } // Else notify on EndDefer 
    } 
} 
Các vấn đề liên quan