Reputation: 11
I'm using .NET C# and trying to load a UserControl that contains three tabs. What I want to do is, to load the first Tab make it visible and responsive and then load the other two tabs in a background thread (or worker or task).
I've tried to do so using a thread, a task, a backgroundworker but always have the same exception "The calling thread cannot access this object because a different thread owns it."
here some code samples I used:
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerAsync();
in the bw_DoWork I put all the work I need to do that involves UI updates
Task t1 = new Task(DoWork);
t1.Start();
in the DoWork I do the same( some code with UI update)
Thanks for the help.
Oussema
Upvotes: 1
Views: 811
Reputation: 63732
This is not impossible, but it is a bad idea - it's very tricky to get right.
Instead, you want to separate the business logic from the UI, so that you can do the logic in the background, while the UI is still on the UI thread. The key is that you must not modify the UI controls from the background thread - instead, you first load whatever data you need, and then either use Invoke
or ReportProgress
to marshal it back on the UI thread, where you can update the UI.
If creating the controls themselves is taking too long, you might want to look at some options at increasing the performance there. For example, using BeginUpdate
and friends to avoid unnecessary redraws and layouting while you're still filling in the data, or creating the controls on demand (as they are about to be visible), rather than all upfront.
A simple example using await
(using Windows Forms, but the basic idea works everywhere):
tabSlow.Enabled = false;
try
{
var data = await Database.LoadDataAsync();
dataGrid.DataSource = data;
}
finally
{
tabSlow.Enabled = true;
}
The database operation is done in the background, and when it's done, the UI update is performed back on the UI thread. If you need to do some expensive calculations, rather than I/O, you'd simply use await Task.Run(...)
instead - just make sure that the task itself doesn't update any UI; it should just return the data you need to update the UI.
Upvotes: 2
Reputation: 545
WPF does not allow you to change UI from the background thread. Only ONE SINGLE thread can handle UI thread. Instead, you should calculate your data in the background thread, and then call Application.Current.Dispatcher.Invoke(...)
method to update the UI. For example:
Application.Current.Dispatcher.Invoke(() => textBoxTab2.Text = "changing UI from the background thread");
Upvotes: 0