Reputation: 6908
I'm fairly new to the idea of threading, and I'm working with a BackgroundWorker
, trying to get a progress bar to display while some work is being done. Here's the DoWork
event for the BackgroundWorker
:
private void BackgroundWorker1DoWork(object sender, DoWorkEventArgs e)
{
SetDateStartEndTimeForReport();
try
{
PopulateGrid();
}
catch (Exception ex)
{
Logger.Error(ex.Message);
MessageBox.Show(ex.Message);
return;
}
}
Now, inside my SetDateStartEndTimeForReport()
, I get an InvalidOperationException
:
private void SetDateStartEndTimeForReport()
{
Logger.Info("Setting the chosen date and/or start/end times for report...");
// Quite possibly the best yo-yo code I've ever written
// "Look, I'm a string. Nope, I'm a DateTime. Tricked you again, I'm a string."
var reportDate = Convert.ToDateTime(cmbDateSelecter.SelectedValue.ToString()).ToString("yyyy-MM-dd"); // Exception occurs here
Report.Date = reportDate;
if (chkboxTimeFrame.IsChecked == true)
{
Report.StartTime = timeStart.Value.Value.ToString(reportDate + " HH:mm");
Report.EndTime = timeEnd.Value.Value.ToString(reportDate + " HH:mm");
}
Logger.Info("Date and/or start/end times set");
}
The exception states:
The calling thread cannot access this object because a different thread owns it.
So, I did some research, and found out about this.Dispatcher.Invoke((Action)(()
. Now, when I wrap the SetDateStartEndTimeForReport();
in that:
this.Dispatcher.Invoke((Action)(() =>
{
SetDateStartEndTimeForReport();
}));
My PopulateGrid()
throws the same exception. Now, I could wrap that method in the same Dispatcher.Invoke()
, and that makes the issue go away. However, I feel as if I'm missing something a little more... elegant. At the time of the function calls, neither UI element SHOULD be in use by anything else. My question is, can I wrap two methods in the same Dispatcher.Invoke()
? Though, I honestly feel I shouldn't be using two of those calls in the same function. Is there a better way to go about what I'm attempting to do?
Upvotes: 1
Views: 280
Reputation: 67075
You can pass data back via the background worker's ProgressChanged event. This will marshal back onto the UI thread. That documentation lets you know that you invoke this event by calling ReportProgress
Please note that BackgroundWorker's
should only be doing business logic in their DoWork
. If all you are doing is updating the UI, then you cannot do this on a background thread. If you need to retrieve data, then you can load that into memory using the background worker, and when completed, it will call the OnRunWorkerCompleted event if you have it hooked up (which will be on the UI thread)
If you want some sort of approach that does it in parts, then Invoke
is the only other way.
Upvotes: 3