2010-03-24 56 views
15

我有一個控件將其數據綁定到標準ObservableCollection,我有一個後臺任務調用服務以獲取更多數據。後臺線程中的WPF更新綁定

然後,我希望在顯示「請稍候」對話框的同時更新我的​​控制背後的數據,但是當我將新項目添加到集合中時,UI線程在重新綁定和更新時鎖定我的控制。

我可以解決這個問題嗎?這樣我的動畫和內容就可以在我的「請稍等」對話框上繼續運行了嗎?

或者至少給用戶「外觀」,它沒有鎖定?

回答

17

如果我沒有理解錯,你已經在使用一個BackgroundWorker來檢索數據,以及簡單地分配這個數據到的ObservableCollection被鎖定UI 。

避免鎖定UI的一種方法是通過排隊多個分派器方法將數據分配到較小的塊中的ObservableCollection。在每個方法調用之間,可以處理UI事件。

以下會一次添加一個項目,這有點極端,但它說明了這個概念。

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

?這是如何運作的? – Mark 2010-03-24 09:25:46

+3

不,你將它們添加在調度程序線程,但具有較低的優先級,並在較小的chucnks所以UI保持響應 – Bubblewrap 2010-03-24 09:29:46

+0

我知道了,謝謝我給一個嘗試 – Mark 2010-03-24 09:33:26

0

使用BackgroundWorker來完成此任務。更新obsrvablecollection在DoWork的方法

+0

這是如何更新UI線程?也許我不明白這一點,但不是所有的控件和UI線程背後的渲染,所以更新後臺線程中的集合只會導致UI線程進行正常的更新? – Mark 2010-03-24 09:27:00

-1

使用此:

 

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

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

 
+1

它是做什麼的?爲什麼它好?它將如何幫助我? – Mark 2010-03-24 09:24:47

+0

可以說你有一個WPF UI表單,其ProgressBar控件爲其中PercentValue是你的DataContext中定義的屬性。假設你正在獲取某個其他線程的回調數據,表示操作進度,並且你想在UI上顯示它。如果直接將該值設置爲您的依賴項源屬性'PercentValue',則僅當後臺線程操作完成時纔會更新ui。所以,爲了得到的百分比值的實時進展情況,以便將它們添加到項目集合在後臺線程將更新UI線程上的控件設置這個值作爲上述 – RockWorld 2010-03-24 09:55:24

10

的ObservableCollection將引發CollectionChanged事件,這將迫使UI重新綁定數據,測量,安排和重繪。如果你有很多更新,這可能需要很長時間。

通過將作業分成小包裝,可以使用戶認爲UI是活着的。使用UI線程中的分派器(任何控件都有引用它)來計劃包含10-100個項目的收集更新操作(通過實驗確定數量,這些僅僅是爲了支持這個想法)。

你的後臺代碼可能看起來是這樣的:

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

我已經運行在工作線程和發送事件返回給應用程序一個DLL - 完美地工作在Windows形式,切換到WPF,一切都停止工作。我一直在磚牆上砸我的頭4個小時試圖讓這個工作。但是我最終得到的解決方案,感謝Microsoft的UI Thread Safe編組EnableCollectionSynchronization,提供了一個非常乾淨的實現來解決這個問題。

此集合擴展了ObservableCollection並實現了EnableCollectionSynchronization,使得這些對象可以在WPF和後臺工作人員之間使用。

編輯Microsoft's docs下面再說,所以我會假設對象上下文共享無所謂。

context參數是一個任意對象,您可以使用它來啓用集合同步時已知的信息。上下文可以是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); 
     } 
    } 
} 

例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>(); 
     } 
    } 
} 

在列表框用法在XAML窗口/控制 MainWindow.xaml

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

    </ListBox> 
+0

嗯想着它;這個靜態__threadsafelock將被所有的集合共享;這可能會導致問題;)我將不得不回到這個... =] – 2017-08-21 23:41:09