后台线程中的 WPF 更新绑定
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2505492/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF update binding in a background thread
提问by Mark
I have a control that has its data bound to a standard ObservableCollection
, and I have a background task that calls a service to get more data.
我有一个控件,它的数据绑定到一个标准ObservableCollection
,我有一个后台任务调用服务来获取更多数据。
I want to, then, update my backing data behind my control, while displaying a "please wait" dialog, but when I add the new items to the collection, the UI thread locks up while it re-binds and updates my controls.
然后,我想在我的控件后面更新我的支持数据,同时显示一个“请稍候”对话框,但是当我将新项目添加到集合中时,UI 线程在重新绑定和更新我的控件时锁定。
Can I get around this so that my animations and stuff keep running on my "please wait" dialog?
我可以解决这个问题,以便我的动画和内容继续在我的“请稍候”对话框中运行吗?
Or at least give the "appearance" to the user that its not locked up?
或者至少给用户一个没有被锁定的“外观”?
回答by Bubblewrap
If i understand correctly, you already use a BackgroundWorker to retrieve the data, and that simply assigning this data to the ObservableCollection is locking up the UI.
如果我理解正确的话,您已经使用 BackgroundWorker 来检索数据,并且简单地将此数据分配给 ObservableCollection 会锁定 UI。
One way to avoid locking up the UI is to assign the data to the ObservableCollection in smaller chunks by queuing multiple dispatcher methods. Between each method call, UI events can be handled.
避免锁定 UI 的一种方法是通过将多个调度程序方法排队,以较小的块将数据分配给 ObservableCollection。在每个方法调用之间,可以处理 UI 事件。
the following would add one item on at a time, that's a bit extreme, but it illustrates the concept.
下面将一次添加一个项目,这有点极端,但它说明了这个概念。
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);
}
回答by Sergey Galich
ObservableCollection will raise CollectionChanged events that will force UI to rebind data, measure, arrange and redraw. This might take a lot of time if you have many updates coming.
ObservableCollection 将引发 CollectionChanged 事件,这些事件将强制 UI 重新绑定数据、测量、排列和重绘。如果您有很多更新,这可能需要很长时间。
It is possible to make user think that UI is alive by splitting the job in small packages. Use Dispatcher from UI thread (any control has reference to it) to schedule collection update actions with 10-100 items (determine number by experiment, these just to support the idea).
通过将作业拆分成小包,可以让用户认为 UI 是活跃的。使用来自 UI 线程的 Dispatcher(任何控件都引用它)来安排具有 10-100 个项目的集合更新操作(通过实验确定数量,这些只是为了支持这个想法)。
Your background code might looks like this:
您的后台代码可能如下所示:
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);
}
}
回答by Becky York
I have a DLL which runs a worker thread and sends events back to the application - worked perfectly on windows forms, switched to WPF and everything stopped working. I've been smashing my head against a brick wall for 4 hours trying to get this to work. But the solution I ended up with, thanks to Microsoft's UI Thread Safe marshalling EnableCollectionSynchronization, gives a really clean implementation to solve this.
我有一个 DLL,它运行一个工作线程并将事件发送回应用程序 - 在 Windows 窗体上完美运行,切换到 WPF,一切都停止工作。我一直在用头撞砖墙 4 个小时试图让它发挥作用。但是我最终得到的解决方案,多亏了微软的 UI 线程安全编组 EnableCollectionSynchronization,提供了一个非常干净的实现来解决这个问题。
This Collection extends ObservableCollection and implements EnableCollectionSynchronization making these objects usable between WPF and also background workers.
该集合扩展了 ObservableCollection 并实现了 EnableCollectionSynchronization,使这些对象在 WPF 和后台工作程序之间可用。
Edit: Microsoft's docssay the following, so I'm going to assume that the object context sharing doesn't matter.
编辑:Microsoft 的文档说明如下,因此我假设对象上下文共享无关紧要。
The context parameter is an arbitrary object that you can use to information known when you enable collection synchronization. Context can be null.
上下文参数是一个任意对象,您可以在启用集合同步时使用它来获取已知信息。上下文可以为null。
ThreadSafeCollection.cs
线程安全集合.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);
}
}
}
Example WindowViewModel WindowViewModel.cs
示例 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>();
}
}
}
Usage in a listbox in a xaml window/control MainWindow.xaml
在 xaml 窗口/控件MainWindow.xaml中的列表框中使用
<ListBox x:Name="wpfStringCollection" ItemsSource="{Binding StringCollection,Mode=OneWay}">
</ListBox>
回答by Kishore Kumar
use BackgroundWorker to accomplish this task. update the obsrvablecollection in the DoWork method
使用 BackgroundWorker 来完成此任务。更新 DoWork 方法中的 obrvablecollection
回答by RockWorld
Use this:
用这个:
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Render, new Action(UpdateData), value);
private void UpdateData(int value)
{
BindingSourceProperty = value;
}