2011-02-10 20 views
10

Trong WPF của tôi, hãy cố gắng phân tách logic lớp của tôi khỏi bất kỳ dữ liệu giao diện nào và chỉ cung cấp các thuộc tính ObservableCollection cho ràng buộc.Sự tách biệt mã và bản trình bày khi sử dụng Dispatcher

Vấn đề là khi tôi truy cập vào các OC được ràng buộc từ các chủ đề khác, tôi bắt buộc phải thực hiện điều đó thông qua điều phối viên. Kết quả là tôi được yêu cầu thêm nhiều cuộc gọi Dispatcher.Invoke(), ẩn bên trong các lớp của tôi, bất cứ khi nào một trong các phương thức cố gắng cập nhật các OC.

Làm cách nào tôi có thể thực hiện điều đó một cách rõ ràng và tách biệt hơn, vì vậy các cuộc gọi điều phối được trừu tượng khỏi các phương pháp của tôi?

Trả lời

2

Lựa chọn 1

Tôi nghĩ bạn nên xem xét một tách tốt hơn của mã của bạn bằng cách sử dụng mô hình MVVM, nếu bạn không quen thuộc với nó, tôi đánh giá cao đề nghị để xem following video vì nó giải thích chính xác những gì Bạn đang tìm.

Cụ thể, tuy nhiên, trong trường hợp của bạn, bạn phải có lớp mô hình với bộ sưu tập thông thường (ví dụ: danh sách) mà bạn thực hiện tất cả công việc trong chuỗi. ViewModel của bạn nên chứa ObservableCollections và kết nối lỏng lẻo với các bộ sưu tập tồn tại trong mô hình, ví dụ: bạn có thể chọn đăng ký qua sự kiện từ ViewModel của bạn đến một logic cập nhật nhất định trong mô hình của bạn. Bạn sẽ STILL cần phải sử dụng Dispatcher để cập nhật OC, nhưng bạn sẽ chỉ cần làm điều đó một lần.

Lựa chọn 2

Bạn có thể thay vì chỉ sử dụng các giải pháp được mô tả here. Về cơ bản, ông đã tạo ra một lớp dẫn xuất mới từ OC cho phép bạn gửi các thay đổi từ mã tự động mà không cần phải tự cập nhật trình điều phối.

+1

+1 vì bạn đã tham chiếu video tuyệt vời trên MVVM từ Jason :). Tôi nghĩ rằng tôi sẽ upvote mọi câu trả lời có chứa một liên kết đến nó. Không thể nhấn mạnh đủ tốt như thế nào. Vâng, và câu trả lời của bạn là chính xác. – Anvaka

0

tôi sợ rằng bạn sẽ phải chờ đợi cho phiên bản tiếp theo của WPF

Từ this post:

Một vài cốm chúng ta có thể mong đợi để xem trong phiên bản tiếp theo của WPF bao gồm:

  • Lưu trữ nội dung Silverlight với phần tử SilverlightHost mới, không có vấn đề về không gian (không thể chồng chéo nội dung WPF lên nội dung Windows hWnd bản địa)
  • Nhìn chung quản lý tốt hơn không phận với tổ chức có nguồn gốc nội dung hWnd-based như WebBrowser, HwndHost và WindowsFormsHost
  • Kích hoạt thông báo ràng buộc và sự thay đổi cho các bộ sưu tập được tạo ra trên một sợi nền
  • Better tích hợp với ảo hóa UI
  • tích hợp điều khiển Ribbon
  • Và hơn thế nữa
3

tôi không có một viên đạn bạc. Nhưng nếu bạn chắc chắn và sẵn sàng chịu trách nhiệm về ủy quyền giao diện người dùng tiềm ẩn, bạn luôn có thể kế thừa từ ObservableCollection, ghi đè các phương thức và gửi tất cả các yêu cầu đến giao diện người dùng.

Nhưng đoạn mã sau khiến tôi đáng sợ:

// somewhere in thread pool: 
for(int i = 0; i < 1000; i++) 
{ 
    _dispatcherAwareCollection.Add(i); 
} 

Có vẻ như vô tội, nhưng dưới các khối mui xe nó gọi chủ đề 1000 lần. Giải pháp thay thế có thể là phương pháp cụ thể của bạn BulkXXX(), điều này sẽ trì hoãn thông báo cho đến khi tất cả các yếu tố được xử lý. Giải pháp này cũng không hoàn hảo, vì bạn muốn có một trừu tượng có thể cho phép bạn liên tục trao đổi các bộ sưu tập, nhưng các phương thức BulkXXX() rất cụ thể cho bộ sưu tập mới.

+0

Quên về tùy chọn này :) +1 –

0

Sử dụng SynchronizationContext thay vì Bộ điều phối. SynchronizationContext là tính năng phổ biến cho đồng bộ hóa các chủ đề trong .NET, trong khi đó Dispatcher được cố ý phát triển cho WPF.

1

Bạn cũng có thể tự viết cho mình AsyncObservableCollection, nếu bạn biết cách viết chủ đề an toàn. Sau đó, bạn có thể đóng gói các cuộc gọi Dispatcher trong đó. Vấn đề là bạn sẽ không sử dụng tiêu chuẩn ObservableCollection được phân phối trong .Net - Framework. Nó sẽ làm tăng nguy cơ lỗi trong ứng dụng của bạn.

Một tùy chọn khác là triển khai WrapperClass, chứa và hiển thị ObservableCollection để ràng buộc và có phương pháp sửa đổi bộ sưu tập.


public class WrapperClass<T> 
{ 
    public ObservableCollection<T> Collection {get; set;} 

    public void Add(T item) 
    { 
     //do your dispatcher magic here 
    } 
    ... 
} 

Để sửa đổi bộ sưu tập bạn triển khai phương pháp trong đó. Vấn đề ở đây là, không có sự bảo đảm, rằng những người khác cũng sẽ sử dụng những phương pháp này.

2

Cách tiếp cận chung là có thuộc tính Bộ điều phối trên mô hình chế độ xem của bạn (có thể trong lớp cơ sở cho tất cả các kiểu xem) có thể được chèn bên ngoài. Bạn có thể xem nó trong mô hình xem vì mô hình xem NÊN phải biết về các khái niệm giao diện người dùng, nhưng không nên nhận thức được chế độ xem cụ thể (bố cục, điều khiển, v.v.) và chắc chắn không nên có tham chiếu đến chế độ xem.

Điều bạn có thể làm là bạn có thể dễ dàng gửi mã của mình đến chuỗi của Người gửi điều phối bằng cách tạo người trợ giúp hoặc dịch vụ sẽ trừu tượng điều phối viên. Ví dụ, bạn có thể tạo ra một helper như thế này:

public class AsyncHelper 
{ 
    public static void EnsureUIThread(Action action) 
    { 
     if (Application.Current != null && !Application.Current.Dispatcher.CheckAccess()) 
     { 
      Application.Current.Dispatcher.BeginInvoke(action, DispatcherPriority.Background); 
     } 
     else 
     { 
      action(); 
     } 
    } 
} 

Và bất cứ khi nào bạn cần phải cập nhật cho bạn bộ sưu tập quan sát, bạn quấn bạn mã trong đó phương pháp helper:

AsyncHelper.EnsureUIThread(() => 
{ 
    // Update you observable collections here 
}); 

OR, bạn có thể đi xa hơn và sử dụng AOP (ví dụ: PostSharp) để chỉ định khai báo (sử dụng các thuộc tính) rằng một phương thức sẽ được thực hiện trong chuỗi giao diện người dùng.

Và cuối cùng, xin lưu ý rằng bạn chỉ phải gửi các bản cập nhật bộ sưu tập đến chuỗi giao diện người dùng. Các thuộc tính thông thường có thể được cập nhật một cách an toàn từ một chuỗi nền. Các cập nhật sẽ tự động được gửi đến luồng giao diện người dùng bằng cơ chế ràng buộc. Có lẽ trong các phiên bản tương lai của bản cập nhật WPF cho một bộ sưu tập từ một chủ đề nền cũng sẽ được hỗ trợ.

+0

Anh ấy, tôi không thể nhớ lại nơi tôi đã thấy một cái gì đó tương tự ... +1 – Anvaka

0

Bạn có thể muốn sử dụng một cái gì đó như MTObservableCollection. Tôi đã sử dụng điều này trong một dự án và nó đã làm việc tuyệt vời. Về cơ bản, nó thực hiện tất cả các công việc gửi đi cho bạn khi sự kiện thay đổi bộ sưu tập được nâng lên, bằng cách phân tích luồng mà trình xử lý được gán và gửi đi phù hợp.

Bài viết này đáng để đọc, ngay cả khi bạn không định chọn tùy chọn này.

0

Tôi có một phần mở rộng cho việc này:

public static class DispatcherInvoker 
{  

    public static void AddOnUI<T>(this ICollection<T> collection, T item) 
    { 
     Action<T> addMethod = collection.Add; 
     Application.Current.Dispatcher.BeginInvoke(addMethod, item); 
    } 
} 

EDIT: Tôi đã đánh cắp nó từ một bài stackoverflow nhưng quên từ đó người ta

0

Tôi nghĩ rằng bạn phải nhiều khớp nối nếu bạn cần phải suy nghĩ về luồng trong lớp mô hình của bạn.

Điều bạn cần làm là không kết nối trực tiếp mô hình của mình với GUI. Như những người khác đã nói, sử dụng một lớp ở giữa (MVVM).

Điều này có nghĩa là bạn để lớp MVVM của bạn phản hồi thông báo thay đổi từ bộ sưu tập có thể quan sát của bạn. Đây là lớp MVVM quyết định xem các thông báo này có được chuyển đến GUI hay không. Hãy xem here để biết cách giảm tần suất cập nhật của GUI để giữ cho nó có thể sử dụng được.

Tóm tắt: Tiếp tục sử dụng ObeservableCollection trong lớp mô hình của bạn nếu bạn thích nhưng không sử dụng trực tiếp trong giao diện GUI. Để một lớp khác nhận được thông báo và kiểm soát cập nhật GUI.

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