从另一个线程更新 Datagrid 的 ItemSource
Update the ItemSource of a Datagrid from another thread
我在这里看到过许多此类问题的问答,但其中 none 似乎解决了我的问题。
我有一个页面可以检索并显示来自数据库的数据列表。我的初始代码如下所示
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();
}
抛出这个异常
The Calling thread cannot access this object because a different thread owns it
四处搜索后,我发现 Dispatcher 是解决此问题的方法。但我无法让它工作。
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();
}
这仍然会抛出上述异常。
你能推荐一种方法来实现它吗?
为什么要在新线程中使用 Dispatcher?
您可以在新线程之外简单地使用 Dipatcher。
像这样:
Dispatcher.Invoke(() =>
{
DgInvoices.ItemsSource = InvoiceList.CurrentItems;
});
所以你可以在主线程上调用而不是在新线程上调用
我个人认为 BackgroundWorker
是最好的选择。 Dispatcher
可能有效,但它在 WPF 中更 "forced" 操作,有时会出现一连串其他问题。使用 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.
不要直接在线程内更新 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;
}
我在这里看到过许多此类问题的问答,但其中 none 似乎解决了我的问题。
我有一个页面可以检索并显示来自数据库的数据列表。我的初始代码如下所示
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();
}
抛出这个异常
The Calling thread cannot access this object because a different thread owns it
四处搜索后,我发现 Dispatcher 是解决此问题的方法。但我无法让它工作。
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();
}
这仍然会抛出上述异常。
你能推荐一种方法来实现它吗?
为什么要在新线程中使用 Dispatcher?
您可以在新线程之外简单地使用 Dipatcher。
像这样:
Dispatcher.Invoke(() =>
{
DgInvoices.ItemsSource = InvoiceList.CurrentItems;
});
所以你可以在主线程上调用而不是在新线程上调用
我个人认为 BackgroundWorker
是最好的选择。 Dispatcher
可能有效,但它在 WPF 中更 "forced" 操作,有时会出现一连串其他问题。使用 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.
不要直接在线程内更新 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;
}