在主题列表框中更新的ObservableCollection [英] Update ObservableCollection in list box in thread
问题描述
海兰,
我有被绑定列表框观察的集合。我想补充记录到观察的集合。我总是立即添加消息观测Collecten。但是,当循环结束列表中只得到更新,但我想更新,当我在加入一个项目的循环。这就是为什么我使用一个线程,但我有几个问题。
I have a Observable Collection which is bind with a list box. I add logs to the Observable Collection. I always add the message immediately to the Observable Collecten. But the list gets only updated when the loop is finished but I want to Update it when I add one item in the for loop. This is why I use a Thread but I have a few problems.
我有一个线程安全的ObservableCollection:
I have a thread safe ObservableCollection:
class ThreadSafeObservableCollection<T> : ObservableCollection<T>
{
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler collectionChanged = this.CollectionChanged;
if (collectionChanged != null)
foreach (NotifyCollectionChangedEventHandler handler in collectionChanged.GetInvocationList())
{
DispatcherObject dispatcherObject = handler.Target as DispatcherObject;
if (dispatcherObject != null)
{
Dispatcher dispatcher = dispatcherObject.Dispatcher;
if (dispatcher != null && !dispatcher.CheckAccess())
{
dispatcher.BeginInvoke(
(Action)(() => handler.Invoke(this,
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
DispatcherPriority.DataBind);
continue;
}
}
handler.Invoke(this, e);
}
}
}
这是我的测试类:
public partial class MainWindow : Window
{
ThreadSafeObservableCollection<Animal> list = new ThreadSafeObservableCollection<Animal>();
public MainWindow()
{
InitializeComponent();
list.Add(new Animal() { Name = "test1" });
list.Add(new Animal() { Name = "test2" });
this.DataContext = list;
}
private void dsofsdkfd(object sender, RoutedEventArgs e)
{
//Version 1
Task.Factory.StartNew(() => test());
//Version2
/*
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var token = Task.Factory.CancellationToken;
Task.Factory.StartNew(() => test(), token, TaskCreationOptions.None, uiScheduler);
*/
}
public void test()
{
for (int i = 0; i < 10000; i++)
{
list.Add(new Animal() { Name = "test" + i });
System.Threading.Thread.Sleep(1);
}
}
}
看到私人无效dsofsdkfd(对象发件人,RoutedEventArgs E)功能评论版本1 。 在它工作的起步阶段,所以名单更新,每次我添加一个项目。经过几个项目我得到一个异常:
See the private void dsofsdkfd(object sender, RoutedEventArgs e) function to the comment Version1. In the beginning it works so the list updates everytime I add a item. After a few entries I get an exception:
为开发信息(使用文字可视化阅读 此):\ r \ n此异常被抛出,因为发电机控制 System.Windows.Controls.ListBox Items.Count:1089名为记录仪 已收到序列CollectionChanged事件,不同意 Items集合的当前状态。下列 被检测到的差异:\ r \ N中所累加计数994是不同的 从实际计数1089. [累计计数(计数,最后复位+
"Information for developers (use Text Visualizer to read this):\r\nThis exception was thrown because the generator for control 'System.Windows.Controls.ListBox Items.Count:1089' with name 'Logger' has received sequence of CollectionChanged events that do not agree with the current state of the Items collection. The following differences were detected:\r\n Accumulated count 994 is different from actual count 1089. [Accumulated count is (Count at last Reset +
System.Windows.Controls.ItemContainerGenerator \ r \ñ
System.Windows.Controls.ItemCollection \ r \ñ
System.Windows.Data.ListCollectionView \ r \ N *
WpfApplication1.ThreadSafeObservableCollection`1 [WpfApplication1.Animal,
WpfApplication1,版本= 1.0.0.0,文化=中立,
公钥=空] \ r \ N(带星号的来源被认为是更
可能是问题的原因。)\ r \ñ\ r \ n此最常见的原因
是(一)改变了收集或计数不养
相应的事件,和(b)引发一个事件有一个不正确的索引
或项目参数。\ r \ñ\ r \ n此异常的堆栈跟踪介绍
检测到的不一致,他们也不怎么发生的。为了得到一个
更及时的异常,设置附加属性
对发电机'presentationTraceSources.TraceLevel'珍惜'高'
并重新运行方案。这样做的一种方式是运行一个命令
类似以下内容:\ñ
System.Diagnostics程序。presentationTraceSources.SetTraceLevel(myItemsControl.ItemContainerGenerator,
System.Diagnostics程序。presentationTraceLevel.High)\ r \的BackSlasHn发件人立即
窗口。这将导致检测逻辑后每运行
CollectionChanged事件,所以它会减慢应用程序。\ r \ N
System.Windows.Controls.ItemContainerGenerator\r\n
System.Windows.Controls.ItemCollection\r\n
System.Windows.Data.ListCollectionView\r\n *
WpfApplication1.ThreadSafeObservableCollection`1[[WpfApplication1.Animal,
WpfApplication1, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null]]\r\n(The starred sources are considered more
likely to be the cause of the problem.)\r\n\r\nThe most common causes
are (a) changing the collection or its Count without raising a
corresponding event, and (b) raising an event with an incorrect index
or item parameter.\r\n\r\nThe exception's stack trace describes how
the inconsistencies were detected, not how they occurred. To get a
more timely exception, set the attached property
'PresentationTraceSources.TraceLevel' on the generator to value 'High'
and rerun the scenario. One way to do this is to run a command
similar to the following:\n
System.Diagnostics.PresentationTraceSources.SetTraceLevel(myItemsControl.ItemContainerGenerator,
System.Diagnostics.PresentationTraceLevel.High)\r\nfrom the Immediate
window. This causes the detection logic to run after every
CollectionChanged event, so it will slow down the application.\r\n"
请参阅私人无效dsofsdkfd(对象发件人,RoutedEventArgs E)功能评论版本2 。 我也尝试过与使用FromCurrentSynchronizationContext的TaskScheduler。
See private void dsofsdkfd(object sender, RoutedEventArgs e) function to the comment Version2. I also tried it with the TaskScheduler using FromCurrentSynchronizationContext.
然后抛出也不例外,但我喜欢在一开始同样的问题,所以只有当每一个循环结束列表框刷新。
Then it throws no exception but I have the same problem like at the beginning, so the list box refreshes only if the for each loop is finished.
我怎样才能实现这个目标列表框更新时,我添加元素?
How I can accomplish that the list box updates when I add an element?
最好的问候
推荐答案
我不会推出自己的ObservableCollection这一点。我只是执行UI线程上。新增的呼叫。
I wouldn't roll my own ObservableCollection for this. I'd just perform the .Add call on the UI thread.
public void test()
{
for (var i = 0; i < 10000; i++)
{
// create object
var animal = new Animal {Name = "test" + i};
// invoke list.Add on the UI thread
this.Dispatcher.Invoke(new Action(() => list.Add(animal)));
// sleep
System.Threading.Thread.Sleep(1);
}
}
请注意,由于你在一个窗口的子类, this.Dispatcher
将对应调度员UI线程。如果将这样的逻辑,比方说,一个模型或视图模型类,你需要明确地捕捉 Dispatcher.Current
的UI线程上的价值,并传递人工调度到后台线程。
Note that since you're in a Window subclass, this.Dispatcher
will correspond to the dispatcher for the UI thread. If you move this logic to, say, a model or view model class, you'll need to explicitly capture the value of Dispatcher.Current
on the UI thread, and pass that dispatcher manually to the background thread.
编辑:OP要求对使用分派一个FrameworkElement的类以外的更多信息。下面是你将如何做到这一点。通过调用 Dispatcher.CurrentDispatcher
收购UI线程的调度员UI线程。该调度程序,然后直接传递到后台线程程序。
OP asked for more information on using the Dispatcher outside of a FrameworkElement class. Here's how you would do that. The dispatcher for the UI thread is acquired on the UI thread by calling Dispatcher.CurrentDispatcher
. That dispatcher is then passed directly into the background thread procedure.
public class MainWindowViewModel
{
// this should be called on the UI thread
public void Start()
{
// get the dispatcher for the UI thread
var uiDispatcher = Dispatcher.CurrentDispatcher;
// start the background thread and pass it the UI thread dispatcher
Task.Factory.StartNew(() => BackgroundThreadProc(uiDispatcher));
}
// this is called on the background thread
public void BackgroundThreadProc(Dispatcher uiDispatcher)
{
for (var i = 0; i < 10000; i++)
{
// create object
var animal = new Animal { Name = "test" + i };
// invoke list.Add on the UI thread
uiDispatcher.Invoke(new Action(() => list.Add(animal)));
// sleep
System.Threading.Thread.Sleep(1);
}
}
}
这篇关于在主题列表框中更新的ObservableCollection的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!