C# 在单独的线程中更新 ObservableCollection
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2104614/
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
Updating an ObservableCollection in a separate thread
提问by rem
In a WPF application an ObservableCollection is filled and updated by LINQ to SQL queries. Then UI objects are updated using values from this ObservableCollection.
在 WPF 应用程序中,一个 ObservableCollection 由 LINQ to SQL 查询填充和更新。然后使用此 ObservableCollection 中的值更新 UI 对象。
Is it possible and reasonable that operations of updating this ObservableCollection by LINQ to SQL queries were executed in a separate thread?
通过 LINQ to SQL 查询更新此 ObservableCollection 的操作在单独的线程中执行是否可能且合理?
If yes, will, in this case, it be one and the same instance of this ObservableCollection? (I mean, if it is not the same one for taking values from LINQ datacontext and the one for giving values to update UI, then I will not be able to update UI)
如果是,在这种情况下,它会是这个 ObservableCollection 的同一个实例吗?(我的意思是,如果从 LINQ 数据上下文中获取值和提供值以更新 UI 的值不同,那么我将无法更新 UI)
采纳答案by Thomas Levesque
With the built-in ObservableCollection<T>
class, you can't change the content from a separate thread if the UI is bound to the collection, it throws a NotSupportedException
(but change notification for properties of collection items works fine). I wrote an AsyncObservableCollection<T>
classto handle this case. It works by invoking the event handlers on the UI synchronization context
使用内置ObservableCollection<T>
类,如果 UI 绑定到集合,则无法从单独的线程更改内容,它会抛出一个NotSupportedException
(但集合项属性的更改通知工作正常)。我写了一个AsyncObservableCollection<T>
类来处理这种情况。它通过调用 UI 同步上下文上的事件处理程序来工作
回答by Max Galkin
In our app, we have a TreeView bound to an ObservableCollection, which we regularly update in a background thread, requesting data from our storage. It works perfectly!
在我们的应用程序中,我们有一个绑定到 ObservableCollection 的 TreeView,我们定期在后台线程中更新它,从我们的存储中请求数据。它完美地工作!
Whoops. I was mis-informed =))
哎呀。我被误导了 =))
Right, we're actually subclassing the ObservableCollection<T>
and override the OnCollectionChanged
method to avoid the UI crossthreading exception. We're using this solution:
是的,我们实际上是子类化ObservableCollection<T>
并覆盖该OnCollectionChanged
方法以避免 UI 跨线程异常。我们正在使用这个解决方案:
public class MTObservableCollection<T> : ObservableCollection<T>
{
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
var eh = CollectionChanged;
if (eh != null)
{
Dispatcher dispatcher = (from NotifyCollectionChangedEventHandler nh in eh.GetInvocationList()
let dpo = nh.Target as DispatcherObject
where dpo != null
select dpo.Dispatcher).FirstOrDefault();
if (dispatcher != null && dispatcher.CheckAccess() == false)
{
dispatcher.Invoke(DispatcherPriority.DataBind, (Action)(() => OnCollectionChanged(e)));
}
else
{
foreach (NotifyCollectionChangedEventHandler nh in eh.GetInvocationList())
nh.Invoke(this, e);
}
}
}
}
Without that override you'd get an exception like that
没有那个覆盖,你会得到这样的异常
System.NotSupportedException : This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
System.NotSupportedException :这种类型的 CollectionView 不支持从不同于 Dispatcher 线程的线程更改其 SourceCollection。
Now the only problem we have is the selected item position, in some cases if the currently selected item is deleted from the collection the TreeView moves the selection to the next item (which causes some other unnecessary UI actions in our app). But that's a small issue.
现在我们唯一的问题是所选项目的位置,在某些情况下,如果当前所选项目从集合中删除,TreeView 会将选择移动到下一个项目(这会导致我们的应用程序中出现一些其他不必要的 UI 操作)。但这是一个小问题。
回答by apandit
Trying to understand your question here:
试图在这里理解你的问题:
Scenario 1 1. LINQ to SQL retrieves data set from database and adds to ObservableCollection A. 2. Periodically, more data is retrieved from database and added to A. Old data is removed from A. Scenario 2 1. LINQ to SQL retrieves data set from database and adds to ObservableCollection A. 2. Periodically, data in A is updated with new data from database (no add/remove).
With Scenario 1, you're going to have to use the UI thread. The UI thread owns the ObservableCollection and you'll get an exception if you try to use it in another thread.
对于场景 1,您将不得不使用 UI 线程。UI 线程拥有 ObservableCollection,如果您尝试在另一个线程中使用它,则会出现异常。
With Scenario 2, thumbs up. As long as you don't try to add or remove items from the collection itself, you can update the item as much as you want in a background thread.
对于场景 2,竖起大拇指。只要您不尝试在集合本身中添加或删除项目,您就可以在后台线程中根据需要更新项目。
回答by Brian Hinchey
.Net 4.5 provides a solution within the BindingOperations class.
.Net 4.5 在 BindingOperations 类中提供了一个解决方案。
You can now use the BindingOperations.EnableCollectionSynchronization method as follows:
您现在可以使用 BindingOperations.EnableCollectionSynchronization 方法,如下所示:
private readonly object _personCollectionLock;
private ObservableCollection<Person> _personCollection;
public ObservableCollection<Person> PersonCollection
{
get { return _personCollection; }
set
{
_personCollection = value;
BindingOperations.EnableCollectionSynchronization(_personCollection, _personCollectionLock);
}
I have only just tried this in my development environment but everything seems to be working correctly now when I update the collection from a background thread.
我刚刚在我的开发环境中尝试过这个,但是当我从后台线程更新集合时,现在一切似乎都正常工作。
There is a more in-depth discussion of this solution at: http://10rem.net/blog/2012/01/16/wpf-45-observable-collection-cross-thread-change-notification
对此解决方案有更深入的讨论:http: //10rem.net/blog/2012/01/16/wpf-45-observable-collection-cross-thread-change-notification
The MSDN entry for this method is at: https://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.enablecollectionsynchronization(v=vs.110).aspx
此方法的 MSDN 条目位于:https: //msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.enablecollectionsynchronization(v=vs.110).aspx
回答by MaxWolodin
I did not receive the same System.NotSupportedException
, but unfortunately my UI did not Update properlyif I set the my ObservableCollectiony<MyType>
from another thread.
我没有收到相同的System.NotSupportedException
,但不幸的是,如果我从另一个线程设置 my ,我的UI 没有正确更新ObservableCollectiony<MyType>
。
The only thing that worked for me is presented by https://www.codeproject.com/Tips/1111432/Update-data-to-WPF-control-from-another-thread:
唯一对我有用的是https://www.codeproject.com/Tips/1111432/Update-data-to-WPF-control-from-another-thread:
In ViewModel
在视图模型中
private SynchronizationContext _syncContext = SynchronizationContext.Current;
private ObservableCollection<PackageModel> _packageModelList;
public ObservableCollection<PackageModel> PackageModelList
{
get => _packageModelList;
set
{
if (_packageModelList == value)
return;
packageModelList = value;
RaisePropertyChanged("PackageModelList");
}
}
In other thread
在其他线程
_syncContext.Send(x => { PackageModelList = OtherThreadPackageModels; },null);
Hope this helps others.
希望这对其他人有帮助。