2010-01-20 28 views
12

Trong một ứng dụng WPF, ObservableCollection được lấp đầy và cập nhật bởi các truy vấn LINQ to SQL. Sau đó, các đối tượng UI được cập nhật bằng cách sử dụng các giá trị từ ObservableCollection này.Cập nhật một ObservableCollection trong một luồng riêng biệt

Có thể và hợp lý rằng các hoạt động cập nhật ObservableCollection này bằng các truy vấn LINQ to SQL đã được thực thi trong một chuỗi riêng biệt?

Nếu có, sẽ, trong trường hợp này, đó là một và cùng một thể hiện của ObservableCollection này? (Ý tôi là, nếu nó không giống với việc lấy giá trị từ dữ liệu địa lý LINQ và một cho việc đưa ra giá trị cập nhật giao diện người dùng, thì tôi sẽ không thể cập nhật giao diện người dùng)

Trả lời

24

Với lớp được xây dựng trong ObservableCollection<T>, bạn không thể thay đổi nội dung từ một chuỗi riêng biệt nếu giao diện người dùng bị ràng buộc vào bộ sưu tập. Tôi đã viết một AsyncObservableCollection<T> class để xử lý trường hợp này. Nó hoạt động bằng cách gọi các xử lý sự kiện vào bối cảnh đồng bộ hóa giao diện người dùng

+0

một cách thay thế dễ thấy http://stackoverflow.com/questions/12881489/asynchronously-adding-to-observablecollection-or-an-alternative – Narkha

+0

Hoặc bạn có thể thử tính năng này an toàn chỉ với chủ đề, hoạt động từ bất kỳ chủ đề nào và có thể databound bởi nhiều chủ đề giao diện người dùng: http://www.codeproject.com/Articles/64936/Multithreaded-ObservableImmutableCollection – Anthony

+0

Công việc tuyệt vời trên bộ sưu tập !! –

6

Trong ứng dụng của chúng ta, chúng ta có một TreeView liên kết với ObservableCollection, mà chúng tôi thường xuyên cập nhật trong một chuỗi nền, yêu cầu dữ liệu từ bộ nhớ của chúng tôi. Nó hoạt động hoàn hảo!

Rất tiếc. Tôi đã được thông báo sai =))

Phải, chúng tôi đang thực sự phân lớp ObservableCollection<T> và ghi đè phương pháp OnCollectionChanged để tránh ngoại lệ trộn chéo giao diện người dùng. Chúng tôi đang sử dụng this solution:

public class MTObservableCollection<T> : ObservableCollection<T> 
{ 
    public override event NotifyCollectionChangedEventHandler CollectionChanged; 
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) 
    { 
     var eh = CollectionChanged; 
     if (eh != null) 
     { 
     Dispatcher dispatcher = (from NotifyCollectionChangedEventHandler nh in eh.GetInvocationList() 
       let dpo = nh.Target as DispatcherObject 
       where dpo != null 
       select dpo.Dispatcher).FirstOrDefault(); 

     if (dispatcher != null && dispatcher.CheckAccess() == false) 
     { 
      dispatcher.Invoke(DispatcherPriority.DataBind, (Action)(() => OnCollectionChanged(e))); 
     } 
     else 
     { 
      foreach (NotifyCollectionChangedEventHandler nh in eh.GetInvocationList()) 
       nh.Invoke(this, e); 
     } 
    } 
    } 
} 

Nếu không ghi đè mà bạn muốn có được một ngoại lệ như thế

System.NotSupportedException: Đây loại CollectionView không thay đổi hỗ trợ cho SourceCollection của nó từ một chủ đề khác với chuỗi điều phối.

Bây giờ vấn đề duy nhất chúng ta có là vị trí mục đã chọn, trong một số trường hợp nếu mục đang được chọn sẽ bị xóa khỏi bộ sưu tập TreeView di chuyển lựa chọn đến mục tiếp theo (mà nguyên nhân một số hành động UI không cần thiết khác trong của chúng tôi ứng dụng). Nhưng đó là một vấn đề nhỏ.

+2

thế nào là tốt? Chắc chắn bạn đang marshaling trở lại thread UI để áp dụng những thay đổi từ lưu trữ dữ liệu của bạn? Nếu không, bạn sẽ nhận được một ngoại lệ ... –

+3

Bạn phải tạo một lớp con của ObservableCollection để khắc phục sự kiện CollectionChanged trở lại luồng giao diện người dùng. Tôi thấy rằng điều này dễ quản lý hơn là việc cập nhật quản lý ViewModel. –

+0

Cảm ơn, Kent, Nigel - bạn đã đúng! Tôi đã sửa chữa câu trả lời của tôi, đó là sai lầm của tôi. –

2

Đang cố gắng để hiểu câu hỏi của bạn ở đây:

 
Scenario 1 
1. LINQ to SQL retrieves data set from database and adds to ObservableCollection A. 
2. Periodically, more data is retrieved from database and added to A. Old data is removed from A. 

Scenario 2 
1. LINQ to SQL retrieves data set from database and adds to ObservableCollection A. 
2. Periodically, data in A is updated with new data from database (no add/remove). 

Với Kịch bản 1, bạn sẽ phải sử dụng thread UI. Chuỗi giao diện người dùng sở hữu ObservableCollection và bạn sẽ nhận được ngoại lệ nếu bạn cố gắng sử dụng nó trong một chuỗi khác.

Với kịch bản 2, hãy bật lên. Miễn là bạn không cố gắng thêm hoặc xóa các mục khỏi bộ sưu tập, bạn có thể cập nhật mục nhiều như bạn muốn trong một chuỗi nền.

15

.Net 4.5 cung cấp giải pháp trong lớp BindingOperations.

Bây giờ bạn có thể sử dụng BindingOperations.Phương pháp EnableCollectionSynchronization như sau:

private readonly object _personCollectionLock; 
private ObservableCollection<Person> _personCollection; 

public ObservableCollection<Person> PersonCollection 
{ 
    get { return _personCollection; } 
    set 
    { 
    _personCollection = value; 
    BindingOperations.EnableCollectionSynchronization(_personCollection, _personCollectionLock); 
    } 

Tôi vừa thử điều này trong môi trường phát triển nhưng mọi thứ có vẻ hoạt động chính xác ngay bây giờ khi tôi cập nhật bộ sưu tập từ chuỗi nền.

Có một cuộc thảo luận sâu hơn về giải pháp này tại địa chỉ: http://10rem.net/blog/2012/01/16/wpf-45-observable-collection-cross-thread-change-notification

Mục MSDN cho phương pháp này là: https://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.enablecollectionsynchronization(v=vs.110).aspx

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