economistdolly
economistdolly

Reputation: 395

InvalidOperationException with BackgroundWorker in WPF

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

Answers (7)

Ra&#250;l Ota&#241;o
Ra&#250;l Ota&#241;o

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

Ra&#250;l Ota&#241;o
Ra&#250;l Ota&#241;o

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

Ra&#250;l Ota&#241;o
Ra&#250;l Ota&#241;o

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

Ra&#250;l Ota&#241;o
Ra&#250;l Ota&#241;o

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

Big Daddy
Big Daddy

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

Reed Copsey
Reed Copsey

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

McGarnagle
McGarnagle

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

Related Questions