2010-03-24 48 views
15

Tôi có một điều khiển có dữ liệu được liên kết với một tiêu chuẩn ObservableCollection và tôi có nhiệm vụ nền gọi một dịch vụ để nhận thêm dữ liệu.Cập nhật WPF ràng buộc trong một chủ đề nền

Tôi muốn cập nhật dữ liệu sao lưu sau khi điều khiển, trong khi hiển thị hộp thoại "vui lòng chờ", nhưng khi tôi thêm các mục mới vào bộ sưu tập, chuỗi giao diện người dùng sẽ bị khóa trong khi nó liên kết lại và cập nhật điều khiển của tôi.

Tôi có thể giải quyết vấn đề này để hoạt ảnh và nội dung của tôi tiếp tục chạy trên hộp thoại "vui lòng đợi" không?

Hoặc ít nhất cung cấp cho "diện mạo" cho người dùng rằng nó không bị khóa?

Trả lời

17

Nếu tôi hiểu đúng, bạn đã sử dụng một BackgroundWorker để lấy dữ liệu, và điều đó chỉ đơn giản là gán dữ liệu này để ObservableCollection được khóa lên giao diện người dùng .

Một cách để tránh khóa giao diện người dùng là gán dữ liệu cho ObservableCollection theo khối nhỏ hơn bằng cách xếp hàng nhiều phương thức điều phối. Giữa mỗi cuộc gọi phương thức, các sự kiện giao diện người dùng có thể được xử lý.

sau đây sẽ thêm một mục vào một thời điểm, điều đó hơi khắc nghiệt, nhưng nó minh họa khái niệm.

void UpdateItems() 
{ 
    //retrievedItems is the data you received from the service 
    foreach(object item in retrievedItems) 
     Dispatcher.BeginInvoke(DispatcherPriority.Background, new ParameterizedThreadStart(AddItem), item);  
} 

void AddItem(object item) 
{ 
    observableCollection.Add(item); 
} 
+0

Vì vậy, bằng cách thêm chúng vào bộ sưu tập các mục trong chuỗi nền sẽ cập nhật các điều khiển trên chuỗi giao diện người dùng? Nó hoạt động như thế nào? – Mark

+3

Không, bạn thêm chúng vào chủ đề bộ điều phối, nhưng với ưu tiên thấp hơn và trong chucnks nhỏ hơn để giao diện người dùng vẫn đáp ứng – Bubblewrap

+0

Tôi thấy, cảm ơn tôi sẽ thử một lần – Mark

0

sử dụng BackgroundWorker để thực hiện tác vụ này. cập nhật obsrvablecollection trong phương pháp DoWork

+0

Làm thế nào để cập nhật mà thread UI? Có lẽ im không hiểu điều này, nhưng arent tất cả các điều khiển của tôi và rendering đằng sau chúng trong thread UI, vì vậy việc cập nhật bộ sưu tập trong một chủ đề nền sẽ chỉ gây ra thread UI để làm việc cập nhật bình thường? – Mark

-1

Sử dụng này:

 

Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Render, new Action(UpdateData), value); 

private void UpdateData(int value) 
{ 
    BindingSourceProperty = value; 
} 

 
+1

nó sẽ làm gì? Tại sao nó lại tốt? nó sẽ giúp tôi làm thế nào? – Mark

+0

cho phép bạn có dạng giao diện người dùng WPF có điều khiển ProgressBar dưới dạng trong đó PercentValue là thuộc tính được định nghĩa trong DataContext của bạn được đính kèm với biểu mẫu. Giả sử một kịch bản mà bạn đang nhận dữ liệu gọi lại trên một số luồng khác cho biết tiến trình hoạt động và bạn muốn hiển thị nó trên giao diện người dùng. Nếu bạn đặt giá trị đó trực tiếp vào thuộc tính nguồn phụ thuộc của bạn 'PercentValue', bạn sẽ nhận được cập nhật ui chỉ khi hoạt động của luồng nền kết thúc. Vì vậy, để có được tiến độ thời gian thực của giá trị phần trăm, hãy đặt giá trị này như trên – RockWorld

10

Quan sátCollection sẽ tăng Thu thập sự kiệnChuyển đổi sẽ buộc giao diện người dùng khôi phục dữ liệu, đo, sắp xếp và vẽ lại. Điều này có thể mất rất nhiều thời gian nếu bạn có nhiều cập nhật sắp tới.

Có thể khiến người dùng nghĩ rằng giao diện người dùng còn sống bằng cách chia công việc thành các gói nhỏ. Sử dụng Dispatcher từ giao diện người dùng (bất kỳ điều khiển nào có tham chiếu đến nó) để lên lịch các hành động cập nhật bộ sưu tập với 10-100 mục (xác định số bằng thử nghiệm, chỉ để hỗ trợ ý tưởng).

nền bạn đang sức trông như thế này:

void WorkInBackground() 
{ 
    var results = new List<object>(); 

    //get results... 

    // feed UI in packages no more than 100 items 
    while (results.Count > 0) 
    { 
     Application.Current.MainWindow.Dispatcher.BeginInvoke(
      new Action<List<object>>(FeedUI), 
      DispatcherPriority.Background, 
      results.GetRange(0, Math.Min(results.Count, 100))); 
     results.RemoveRange(0, Math.Min(results.Count, 100)); 
    } 
} 
void FeedUI(List<object> items) 
{ 
    // items.Count must be small enough to keep UI looks alive 
    foreach (var item in items) 
    { 
     MyCollection.Add(item); 
    } 
} 
0

Tôi đã một DLL mà chạy một sợi nhân viên và gửi các sự kiện trở lại ứng dụng - làm việc một cách hoàn hảo trên cửa sổ hình thức, chuyển sang WPF và tất cả mọi thứ ngừng làm việc. Tôi đã đập đầu của tôi vào một bức tường gạch trong 4 giờ cố gắng để có được điều này để làm việc. Nhưng giải pháp mà tôi đã kết thúc, nhờ vào UI Thread Safe của SafeCarshectionSynchronization của Microsoft, cung cấp một triển khai thực sự rõ ràng để giải quyết vấn đề này.

Bộ sưu tập này mở rộng ObservableCollection và triển khai EnableCollectionSynchronization làm cho các đối tượng này có thể sử dụng được giữa WPF và nhân viên nền.

Sửa: Microsoft's docs nói sau đây, vì vậy tôi sẽ cho rằng việc chia sẻ nội dung đối tượng không quan trọng.

Tham số ngữ cảnh là một đối tượng tùy ý mà bạn có thể sử dụng để biết thông tin khi bạn bật đồng bộ hóa bộ sưu tập. Bối cảnh có thể là null.

ThreadSafeCollection.cs

using System.Collections.ObjectModel; 
using System.Windows.Data; 

namespace NSYourApplication 
{ 
    /// <summary> 
    /// This ObservableCollection is thread safe 
    /// You can update it from any thread and the changes will be safely 
    /// marshalled to the UI Thread WPF bindings 
    /// Thanks Microsoft! 
    /// </summary> 
    /// <typeparam name="T">Whatever type of collection you want!</typeparam> 
    public class ThreadSafeCollection<T> : ObservableCollection<T> 
    { 
     private static object __threadsafelock = new object(); 

     public ThreadSafeCollection() 
     { 
      BindingOperations.EnableCollectionSynchronization(this, __threadsafelock); 
     } 
    } 
} 

Ví dụ WindowViewModel WindowViewModel.cs

namespace NSYourApplication 
{ 
    /// <summary> 
    /// Example View 
    /// BaseModelView implements "PropertyChanged" to update WPF automagically 
    /// </summary> 
    class TestViewModel : BaseModelView 
    { 
     public ThreadSafeCollection<string> StringCollection { get; set; } 

     /// <summary> 
     /// background thread implemented elsewhere... 
     /// but it calls this method eventually ;) 
     /// Depending on the complexity you might want to implement 
     /// [MethodImpl(MethodImplOptions.Synchronized)] 
     /// to Synchronize multiple threads to prevent chase-conditions,deadlocks etc 
     /// </summary> 
     public void NonUIThreadMethod() 
     { 
      // No dispatchers or invokes required here! 
      StringCollection.Add("Some Text from a background worker"); 
     } 

     /// <summary> 
     /// Somewhere in the UIThread code it'll call this method 
     /// </summary> 
     public void UIThreadMethod() 
     { 
      StringCollection.Add("This text come from UI Thread"); 
     } 

     /// <summary> 
     /// Constructor, creates a thread-safe collection 
     /// </summary> 
     public TestViewModel() 
     { 
      StringCollection = new ThreadSafeCollection<string>(); 
     } 
    } 
} 

Cách sử dụng trong một ListBox trong một XAML cửa sổ/kiểm soát MainWindow.xaml

<ListBox x:Name="wpfStringCollection" ItemsSource="{Binding StringCollection,Mode=OneWay}"> 

    </ListBox> 
+0

Hmm nghĩ về nó; __threadsafelock tĩnh đơn này sẽ được chia sẻ bởi tất cả các bộ sưu tập; điều này có thể gây ra vấn đề;) Tôi sẽ phải quay lại điều này ... =] –

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