[英]Update the ItemSource of a Datagrid from another thread
我在这里看到了许多这类问题的提问和回答,但似乎没有一个能解决我的问题。
我有一个从数据库中检索并显示数据列表的页面。 我的初始代码看起来像这样
private void HistoryPage_OnLoaded(object sender, RoutedEventArgs e)
{
//''''''
_invoices = Invoice.GetAll(); // returns a list of invoices
InvoiceList = new PagingCollection<Invoice>(_invoices, _itemsPerPage);
DgInvoices.ItemsSource = InvoiceList.CurrentItems;
//'''''''''
}
这工作正常,直到数据列表变大。 现在这个操作大约需要 6-8 秒。 然后我尝试从另一个线程获取数据并从那里更新 Datagrid ( DGInvoices )。
private void HistoryPage_OnLoaded(object sender, RoutedEventArgs e)
{
//''''''''
new Thread(() =>
{
_invoices = Invoice.GetAll();
InvoiceList = new PagingCollection<Invoice>(_invoices, _itemsPerPage);
DgInvoices.ItemsSource = InvoiceList.CurrentItems;
}).Start();
}
引发此异常
调用线程无法访问此 object,因为不同的线程拥有它
搜索了一圈,发现Dispatcher是关于这个的go的方式。 但我无法让它工作。
private void HistoryPage_OnLoaded(object sender, RoutedEventArgs e)
{
//''''''''
new Thread(() =>
{
_invoices = Invoice.GetAll();
InvoiceList = new PagingCollection<Invoice>(_invoices, _itemsPerPage);
Dispatcher.Invoke(() =>
{
DgInvoices.ItemsSource = InvoiceList.CurrentItems;
});
}).Start();
}
这仍然会引发上述异常。
你能推荐一种让它工作的方法吗?
我个人认为BackgroundWorker
将是最好的选择。 Dispatcher
可能会工作,但它在 WPF 中是一个更“强制”的操作,它有时会出现一连串其他问题。 使用BackgroundWorker
,您可以在后台处理数据,然后在完成后在主线程上处理 UI。
举个例子:
BackgroundWorker bw = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
//Subscribe to the events
bw.DoWork += Bw_DoWork;
bw.RunWorkerCompleted += Bw_RunWorkerCompleted;
}
private void HistoryPage_OnLoaded(object sender, RoutedEventArgs e)
{
//Start background worker on page load
bw.RunWorkerAsync(); //This is the DoWork function
}
//Background worker executes on separate thread
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
//Do long running operations
_invoices = Invoice.GetAll();
}
//Fires when the DoWork operation finishes. Executes on the main UI thread
private void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Update UI when the worker completes on the main thread
InvoiceList = new PagingCollection<Invoice>(_invoices, _itemsPerPage);
DgInvoices.ItemsSource = InvoiceList.CurrentItems;
}
如果您的操作变得很长,您甚至可以利用BackgrounWorker.ReportProgess
操作并向 UI 提供状态更新。 它是加载操作的绝佳工具,可用于避免锁定 UI。
为什么在新线程中使用 Dispatcher?
您可以简单地在新线程之外使用 Dipatcher。
像这样:
Dispatcher.Invoke(() =>
{
DgInvoices.ItemsSource = InvoiceList.CurrentItems;
});
所以你可以在主线程而不是新线程上调用
不要直接在线程内更新 DgInvoices.ItemsSource。 而是将 ItemSource 绑定到属性并更新线程中的属性。
在您最后一次编辑时,要使其正常工作,您必须将InvoiceList = new PagingCollection<Invoice>(_invoices, _itemsPerPage)
也移动到Dispatcher
,因为它的子CurrentItems
已分配给 DitaGrid 的项目源。 因此,您不能从主 UI 线程以外的地方修改InvoiceList
。
此外,我建议使用Task
而不是Thread
,因为创建线程的操作过于昂贵,并且Task
可能会重用已创建的线程并节省您的时间和 PC 资源。 Task
是Thread
的智能包装器。
private void HistoryPage_OnLoaded(object sender, RoutedEventArgs e)
{
//''''''''
Task.Run(() =>
{
_invoices = Invoice.GetAll();
Dispatcher.Invoke(() =>
{
InvoiceList = new PagingCollection<Invoice>(_invoices, _itemsPerPage);
DgInvoices.ItemsSource = InvoiceList.CurrentItems;
});
});
}
或者,如果您的 API 有异步方法来获取数据,您可以使用异步方法。 但我不知道是否存在这种等待的方法。
private async void HistoryPage_OnLoaded(object sender, RoutedEventArgs e)
{
//''''''''
await _invoices = Invoice.GetAllAsync();
InvoiceList = new PagingCollection<Invoice>(_invoices, _itemsPerPage);
DgInvoices.ItemsSource = InvoiceList.CurrentItems;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.