2011-12-22 26 views
26

Tôi có một số ObservableCollection của các mục được ràng buộc với một điều khiển danh sách trong chế độ xem của tôi.Thêm một loạt các giá trị vào một ObservableCollection hiệu quả

Tôi có một tình huống mà tôi cần thêm một đoạn giá trị vào đầu bộ sưu tập. Tài liệu Collection<T>.Insert chỉ định mỗi lần chèn dưới dạng hoạt động O (n) và mỗi lần chèn cũng tạo ra thông báo CollectionChanged. Vì vậy, tôi lý tưởng muốn chèn toàn bộ phạm vi của các mục trong một lần di chuyển, có nghĩa là chỉ có một shuffle của danh sách cơ bản, và hy vọng một thông báo CollectionChanged (có lẽ là "thiết lập lại").

Collection<T> không hiển thị bất kỳ phương pháp nào để thực hiện việc này. List<T>InsertRange(), nhưng IList<T>, rằng Collection<T> hiển thị thông qua thuộc tính Items của nó không.

Có cách nào để làm điều này không?

+0

Nếu bạn có một trường ủng hộ cho sở hữu bộ sưu tập - bạn có thể gán một trường hợp mới vào nó và sau đó nâng cao 'OnPropertyChanged' cho bộ sưu tập proeprty tay – sll

+1

liên quan/có thể trùng lặp: http: // stackoverflow.com/questions/670577/observablecollection-doesnt-support-addrange-method-so-i-get-notified-for-each – Adam

+2

+1 nếu 'ObservableCollection' làm cho bạn nghĩ về cơ học lượng tử và thí nghiệm khe đôi. – rfmodulator

Trả lời

50

ObservableCollection cho thấy thuộc tính được bảo vệ Items là bộ sưu tập cơ bản không có ngữ nghĩa thông báo. Điều này có nghĩa bạn có thể xây dựng một bộ sưu tập mà những gì bạn muốn bằng cách kế thừa ObservableCollection:

class RangeEnabledObservableCollection<T> : ObservableCollection<T> 
{ 
    public void InsertRange(IEnumerable<T> items) 
    { 
     this.CheckReentrancy(); 
     foreach(var item in items) 
      this.Items.Add(item); 
     this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); 
    } 
} 

Cách sử dụng:

void Main() 
{ 
    var collection = new RangeEnabledObservableCollection<int>(); 
    collection.CollectionChanged += (s,e) => Console.WriteLine("Collection changed"); 
    collection.InsertRange(Enumerable.Range(0,100)); 
    Console.WriteLine("Collection contains {0} items.", collection.Count); 
} 
+6

Câu trả lời khác này [http://stackoverflow.com/questions/13302933/how -to-avoid-firing-observablecollection-collectionschanged-multiple-times-when-r) thành một câu hỏi tương tự được đề xuất để thêm mã sau đây để thông báo cho các thay đổi về thuộc tính đếm và chỉ mục: 'this.OnPropertyChanged (new PropertyChangedEventArgs (" Count "))); ' ' this.OnPropertyChanged (new PropertyChangedEventArgs ("Item []")); ' – bouvierr

7

Để thực hiện các câu trả lời ở trên hữu ích w/o phát sinh một lớp cơ sở mới sử dụng phản ánh, đây là một ví dụ:

public static void InsertRange<T>(this ObservableCollection<T> collection, IEnumerable<T> items) 
{ 
    var enumerable = items as List<T> ?? items.ToList(); 
    if (collection == null || items == null || !enumerable.Any()) 
    { 
    return; 
    } 

    Type type = collection.GetType(); 

    type.InvokeMember("CheckReentrancy", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, collection, null); 
    var itemsProp = type.BaseType.GetProperty("Items", BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance); 
    var privateItems = itemsProp.GetValue(collection) as IList<T>; 
    foreach (var item in enumerable) 
    { 
    privateItems.Add(item); 
    } 

    type.InvokeMember("OnPropertyChanged", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, 
    collection, new object[] { new PropertyChangedEventArgs("Count") }); 

    type.InvokeMember("OnPropertyChanged", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, 
    collection, new object[] { new PropertyChangedEventArgs("Item[]") }); 

    type.InvokeMember("OnCollectionChanged", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, 
    collection, new object[]{ new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)}); 
} 
+1

Có gì sai với inheritenance? Tại sao sử dụng sử dụng sự phản chiếu thay vì tạo ra một lớp mới? – ventiseis

+3

Không có gì sai với thừa kế, tất nhiên. Đây chỉ là một thay thế cho điều đó. Nó có thể hữu ích nếu bạn có một tấn mã kế thừa và tuần tự hóa của bạn (nhị phân) phụ thuộc vào loại ObservableCollection. Dễ dàng hơn và sự lựa chọn thực dụng hơn nhiều để mở rộng ObservableCollection trong trường hợp đó. – outbred

+0

Xin chào @outbred. xin lỗi nhưng làm thế nào để bạn "sử dụng" ví dụ của bạn.Cảm ơn – NevilleDastur

-2

Ví dụ: bước mong muốn 0,10,20,30,40,50,60,70,80,90,100 -> min = 0, max = 100, bước = 11

static int min = 0; 
    static int max = 100; 
    static int steps = 11; 

    private ObservableCollection<string> restartDelayTimeList = new ObservableCollection<string> (
     Enumerable.Range(0, steps).Select(l1 => (min + (max - min) * ((double)l1/(steps - 1))).ToString()) 
    ); 
3

answer không hiển thị cho tôi các mục nhập mới trong DataGrid. Đây OnCollectionChanged công trình cho tôi:

public class SilentObservableCollection<T> : ObservableCollection<T> 
{ 
    public void AddRange(IEnumerable<T> enumerable) 
    { 
     CheckReentrancy(); 

     int startIndex = Count; 

     foreach (var item in enumerable) 
      Items.Add(item); 

     OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<T>(enumerable), startIndex)); 
     OnPropertyChanged(new PropertyChangedEventArgs("Count")); 
     OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); 
    } 
} 
+0

Có vẻ như các thông báo thay đổi phù hợp hơn. – OttPrime

Các vấn đề liên quan