我有以下设置:
public class Monitor
{
public ObservableCollection<People> listOfPeople = new ObservableCollection<People>();
public dosomework() {
Thread t = new ThreadStart(longwork);
t.Start();
}
public longwork() {
listOfPeople.Add(new People());
Thread.Sleep(10000);
}
}
public class People : INotifyPropertyChanged
{
int _age;
public int Age
{
get { return _age; }
set
{
_age = value;
Notify("Age");
}
}
}
public class UI : Window
{
Monitor md = new Monitor();
}
我想将数据网格绑定到observable集合(datagrid.ItemSource = md.listOfPeople),但是,我遇到了线程错误。另外,如果我不对新线程进行长时间的工作,那么UI只会在longwork函数完成后更新。我认为绑定会避免这个问题。
任何帮助表示感谢。
答案 0 :(得分:1)
绑定只是“其他代码监听更改”,因此线程仍然是一个问题。
通常的解决方法是在UI线程上执行添加到列表,因为这是Grid将要监听的位置,但如果针对单个元素运行,这将导致性能不佳。
最好将添加内容批量添加到后台线程上运行的临时列表中,并按间隔调用AddRange(每100项?)将该批次移动到UI集合上,以提供更好的响应和常规的DataGrid更新。
答案 1 :(得分:1)
正如其他人所说,如果你对UI以外的线程做任何工作,你必须调度到UI线程,以实际更新绑定到UI元素的任何元素。
在您的示例中,这将非常简单。只需将窗口的Dispatcher
(msdn)传递给Monitor
,然后使用该代码进行相应的发送:
...
Monitor md = new Monitor(this.Dispatcher);
...
public Monitor(Dispatcher dispatcher)
{
_dispatcher = dispatcher;
}
...
public longwork() {
_dispatcher.BeginInvoke((Action)(()=>listOfPeople.Add(new People()));
Thread.Sleep(10000);
}
您可以拨打Dispatcher.Invoke
而不是Dispatcher.BeginInvoke
,但如果您不关心结果并希望快速前进,BeginInvoke
就是您的朋友。
答案 2 :(得分:0)
数据绑定数据网格订阅了CollectionChanged事件。根据我的经验,CollectionChanged事件是在任何导致集合更改的线程上引发的。在这种情况下,它不是UI线程。由于UI元素只能从UI线程修改,并且事件是在非UI线程上引发和处理的,因此您将获得“线程错误”。
如上所述,一个简单的解决方案是仅从UI线程修改ObservableCollection。
如果您使用的是WPF或Silverlight,则所有FrameworkElements都有一个Dispatcher属性,您可以使用该属性在UI线程上执行工作。另见:http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherobject.aspx