I've seen many of these types of questions asked and answered here but none of those seems to solve my problem.
I have a Page that retrieves and shows a list of data from a database. my initial code looked like this
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;
//'''''''''
}
this worked ok until the data list got bigger. now it takes about 6-8 seconds for this operation. then I tried to fetch data from a different thread and update the Datagrid ( DGInvoices ) from there.
private void HistoryPage_OnLoaded(object sender, RoutedEventArgs e)
{
//''''''''
new Thread(() =>
{
_invoices = Invoice.GetAll();
InvoiceList = new PagingCollection<Invoice>(_invoices, _itemsPerPage);
DgInvoices.ItemsSource = InvoiceList.CurrentItems;
}).Start();
}
which throws this exception
The Calling thread cannot access this object because a different thread owns it
After searching around, I found that the Dispatcher is the way to go about this. but I cannot get it to work.
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();
}
this still throws the above exception.
can you recommend a way to get this working?
I personally think a BackgroundWorker
would be the best option. Dispatcher
may work, but it's a more "forced" operation in WPF and it can sometimes present a litany of other problems. With a BackgroundWorker
you can do your data work in the background, and then do your UI work on the main thread upon its completion.
As an example:
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;
}
If your operation gets really long you can even tap into the BackgrounWorker.ReportProgess
operation and give status updates to the UI. It's a great tool for loading operations that you can use to avoid locking the UI.