1

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

编辑!所以我的实际目标(我想从我提供的示例中并不清楚)是能够在更新 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>
4

7 回答 7

2

尝试这个

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

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

于 2012-09-07T18:47:58.797 回答
2

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

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

于 2012-09-07T18:24:54.657 回答
1

您不能从后台线程进行 UI 更新;使用RunWorkerCompletedProgressChanged事件来更新 UI。

worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    test.Background = Brushes.Orange;
}
于 2012-09-07T18:24:17.517 回答
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();

    }
于 2012-09-07T19:42:07.013 回答
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>

此代码多次更新视图(在本例中为按钮)。我认为这解决了您最初的问题。

于 2012-10-02T15:37:01.313 回答
0

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

于 2012-09-07T18:28:35.663 回答
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() ));
}
于 2012-09-07T19:30:44.737 回答