[英]How to properly update the ObservableCollection from another thread?
如果我使用ActionBlock
进行数据库调用,并且需要更新GUI(也许是ObservableCollection
)。 是遍历结果集并使用Dispatcher.BeingInvoke
是一个好的解决方案,还是有更好的方法?
我想一次加载到GUI中,因为即使启用了虚拟化,好像我立即更新了整个可观察的集合,GUI也将挂起,直到可以呈现整个数据网格为止。
一些模拟情况的示例代码:
ActionBlock<Func<Task>> _block = new ActionBlock<Func<Task>>(action => action());
_block.Post(async () =>
{
await Task.Delay(1000); // Perhaps Long database read
for (int i = 0; i < 1000000; i++) // Perhaps looping over database result set
{
await Dispatcher.BeginInvoke( // Need to update GUI
new Action(
() =>
{
// Add new object to collection (GUI will update DataGrid one row at a time).
MyModel.MyCollection.Add(new MyClass() { MyInt = i });
}
), DispatcherPriority.Background
);
}
});
如果在循环内添加调用Dispather.BeginInvoke,则将UI更新100k次。 理想情况下,我会这样做:
//do as much work as possible in background thread
var items = new MyClass[100000];
for (int i = 0; i < 1000000; i++) {
items[i] = new MyClass{ MyInt = i;}
}
Dispatcher.BeginInvoke(new Action(() => //update UI just once
MyModel.MyCollection = new ObservableCollection(items);
));
如果您的虚拟化确实有效,那应该没问题。
为了避免在UI线程中添加大量数字,可以将其拆分为较小的数据部分:
for (int i = 0; i < 100; i++){
await Dispatcher.BeginInvokenew Action(() =>
{
for (int j = 0; j < 1000; j++) { //add thousand items at once
MyModel.MyCollection.Add(items[i * 1000 + j])
});
}
是遍历结果集并使用Dispatcher.BeingInvoke是一个好的解决方案,还是有更好的方法?
在现代应用程序中从来没有充分的理由使用Dispatcher.BeginInvoke
。
在您的情况下,由于您已经在使用TPL Dataflow,您可以将ActionBlock
更改为TransformManyBlock
,并将其链接到在UI线程上执行的单独的ActionBlock
。 就像是:
var _getRowsBlock = new TransformManyBlock<Func<Task<IEnumerable<TRow>>>, TRow>(
action => action());
var _updateUiBlock = new ActionBlock<TRow>(row =>
{
MyModel.MyCollection.Add(new MyClass() { MyInt = i });
}, new ExecutionDataflowBlockOptions
{
TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(),
});
_getRowsBlock.LinkTo(_updateUiBlock, new DataflowLinkOptions { PropagateCompletion = true });
_block.Post(async () =>
{
await Task.Delay(1000); // Perhaps Long database read
return result.Rows; // return the database result set
});
我想一次加载到GUI中,因为即使启用了虚拟化,好像我立即更新了整个可观察的集合,GUI也将挂起,直到可以呈现整个数据网格为止。
好吧,那么您可能正在寻找错误的解决方案。 我不知道一次添加一行数据会有什么帮助。 如果UI一次添加了1000000行,那么一次添加1000000行将使它更多……
您可能需要考虑不向UI加载1000000行的解决方案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.