Reputation: 395
I'm trying to get a BackgroundWorker to update the UI during program logic execution. But I get the error:
EDIT! So my actual goal, which I guess isn't clear from the sample I provided, is to be able to perform calculations while updating the UI. See the updated code sample below.
InvalidOperationException: The calling thread cannot access this object because a different thread owns it.
My C# and xaml follow:
public partial class MainWindow : Window
{
private readonly BackgroundWorker worker = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
worker.DoWork += worker_DoWork;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
test.Background = Brushes.Orange;
for (double x = 0; x < 10000000000; )
{
x++;
}
test.Background = Brushes.Red;
}
private void test_Click(object sender, RoutedEventArgs e)
{
worker.RunWorkerAsync();
}
}
And then, my XAML:
<Button Name="test" Click="test_Click">This is a button!</Button>
Upvotes: 1
Views: 2256
Reputation: 4760
i have tried some other code, and do some researching, and yes, there exist ways for to modify view from another thread, using the SynchronizationContext.
Please see this example:
private void Button_Click(object sender, RoutedEventArgs e)
{
var sync = SynchronizationContext.Current;
BackgroundWorker w = new BackgroundWorker();
w.DoWork+=(_, __)=>
{
//sync.Post(p => { button.Content = "Working"; }, null);
int j = 0;
for (int i = 0; i < 10; i++)
{
j++;
sync.Post(p => { button.Content = j.ToString(); }, null);
Thread.Sleep(1000);
}
sync.Post(p => { button.Background = Brushes.Aqua; button.Content = "Some Content"; }, null);
};
w.RunWorkerAsync();
}
And this is the view:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button x:Name="button" Content="Some Content" Click="Button_Click"/>
</Grid>
</Window>
This code, updates several times the view (a button in this case). I think this solve your initial question.
Upvotes: 0
Reputation: 4760
Ok the previous lock your app too, try this:
void worker_DoWork(object sender, DoWorkEventArgs e)
{
for (double x = 0; x < 10000000000; )
{
x++;
}
}
private void test_Click(object sender, RoutedEventArgs e)
{
test.Background = Brushes.Orange;
worker.RunWorkerCompleted += (_,__) => test.Background = Brushes.Red;
worker.RunWorkerAsync();
}
Upvotes: 1
Reputation: 4760
This works for any code:
void CodeToExcecute()
{
test.Background = Brushes.Orange;
for (double x = 0; x < 10000000000; )
{
x++;
}
test.Background = Brushes.Red;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
Dispatcher.BeginInvoke(new Action(() => CodeToExcecute() ));
}
Upvotes: 0
Reputation: 4760
Try this
void worker_DoWork(object sender, DoWorkEventArgs e)
{
Dispatcher.BeginInvoke(new Action(() => { test.Background = Brushes.Orange; }));
}
you need to execute it from the UI dispatcher.
Upvotes: 2
Reputation: 5224
You need to do it in the BackgroundWorker's RunWorkerCompleted event. Within this event, you'll need to use the Dispatcher and one of it's 2 main methods: Invoke and BeginInvoke.
Upvotes: 0
Reputation: 564333
You can't directly access user interface objects from background threads. You need to either marshal the call back onto the UI thread (via Dispatcher.Invoke
) or do the update within a progress or completion event of the BackgroundWorker
, as these are already run on the UI thread.
That being said, in this case, the BW is only updating the UI, so you should just do it directly in your Button's event handler, as there is no other "work" to perform.
Upvotes: 2
Reputation: 102723
You can't do a UI update from the background thread; use either the RunWorkerCompleted
or the ProgressChanged
events to update the UI.
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
test.Background = Brushes.Orange;
}
Upvotes: 1