WPF中使用BackgroundWorker的InvalidOperationException

时间:2012-09-07 18:20:15

标签: c# wpf

我正在尝试让BackgroundWorker在程序逻辑执行期间更新UI。但是我得到了错误:

EDIT!所以我的实际目标,我想从我提供的示例中不清楚,是能够在更新UI时执行计算。请参阅下面的更新代码示例。

  

InvalidOperationException:调用线程无法访问此对象,因为另一个线程拥有它。

我的C#和xaml跟随:

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();
    }
}

然后,我的XAML:

<Button Name="test" Click="test_Click">This is a button!</Button>

7 个答案:

答案 0 :(得分:2)

您无法直接从后台线程访问用户界面对象。您需要将回调编组回UI线程(通过Dispatcher.Invoke)或在BackgroundWorker的进度或完成事件中进行更新,因为这些已经在UI线程上运行。

话虽如此,在这种情况下,BW仅 更新UI,因此您应该直接在Button的事件处理程序中执行,因为没有其他“工作”可以执行。

答案 1 :(得分:2)

试试这个

void worker_DoWork(object sender, DoWorkEventArgs e)
{
   Dispatcher.BeginInvoke(new Action(() => { test.Background = Brushes.Orange; }));
}

您需要从UI调度程序执行它。

答案 2 :(得分:1)

您无法从后台线程进行UI更新;使用RunWorkerCompletedProgressChanged事件来更新用户界面。

worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    test.Background = Brushes.Orange;
}

答案 3 :(得分:1)

在上一次锁定你的应用程序时,试试这个:

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();

    }

答案 4 :(得分:0)

您需要在BackgroundWorker的RunWorkerCompleted事件中执行此操作。在这个事件中,你需要使用Dispatcher和它的两个主要方法之一:Invoke和BeginInvoke。

答案 5 :(得分:0)

这适用于任何代码:

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() ));
}

答案 6 :(得分:0)

我已经尝试了一些其他代码,并进行了一些研究,是的,存在使用SynchronizationContext从另一个线程修改视图的方法。

请看这个例子:

        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();
        }

这就是观点:

<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>

此代码会多次更新视图(在本例中为按钮)。我认为这可以解决你的初步问题。