我正在尝试让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>
答案 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更新;使用RunWorkerCompleted
或ProgressChanged
事件来更新用户界面。
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>
此代码会多次更新视图(在本例中为按钮)。我认为这可以解决你的初步问题。