我有一個控件將其數據綁定到標準ObservableCollection
,我有一個後臺任務調用服務以獲取更多數據。後臺線程中的WPF更新綁定
然後,我希望在顯示「請稍候」對話框的同時更新我的控制背後的數據,但是當我將新項目添加到集合中時,UI線程在重新綁定和更新時鎖定我的控制。
我可以解決這個問題嗎?這樣我的動畫和內容就可以在我的「請稍等」對話框上繼續運行了嗎?
或者至少給用戶「外觀」,它沒有鎖定?
我有一個控件將其數據綁定到標準ObservableCollection
,我有一個後臺任務調用服務以獲取更多數據。後臺線程中的WPF更新綁定
然後,我希望在顯示「請稍候」對話框的同時更新我的控制背後的數據,但是當我將新項目添加到集合中時,UI線程在重新綁定和更新時鎖定我的控制。
我可以解決這個問題嗎?這樣我的動畫和內容就可以在我的「請稍等」對話框上繼續運行了嗎?
或者至少給用戶「外觀」,它沒有鎖定?
如果我沒有理解錯,你已經在使用一個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);
}
使用BackgroundWorker來完成此任務。更新obsrvablecollection在DoWork的方法
這是如何更新UI線程?也許我不明白這一點,但不是所有的控件和UI線程背後的渲染,所以更新後臺線程中的集合只會導致UI線程進行正常的更新? – Mark 2010-03-24 09:27:00
使用此:
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Render, new Action(UpdateData), value);
private void UpdateData(int value)
{
BindingSourceProperty = value;
}
它是做什麼的?爲什麼它好?它將如何幫助我? – Mark 2010-03-24 09:24:47
可以說你有一個WPF UI表單,其ProgressBar控件爲
的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);
}
}
我已經運行在工作線程和發送事件返回給應用程序一個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>
嗯想着它;這個靜態__threadsafelock將被所有的集合共享;這可能會導致問題;)我將不得不回到這個... =] – 2017-08-21 23:41:09
?這是如何運作的? – Mark 2010-03-24 09:25:46
不,你將它們添加在調度程序線程,但具有較低的優先級,並在較小的chucnks所以UI保持響應 – Bubblewrap 2010-03-24 09:29:46
我知道了,謝謝我給一個嘗試 – Mark 2010-03-24 09:33:26