如何从后台工作线程更改 XAML 元素?
在下面的代码示例中,我想从我的BackgroundThread更改TextBox的前景文本的颜色,但它收到错误:
该线程无法访问该对象 因为它在另一个线程中。
我必须在下面的代码中更改什么,以便后台工作线程可以更改 TextBox 中的前景色?
答案:
谢谢 Andy,这只是一个小疏忽,这里是更正后的代码为了后代:
using System.Windows;
using System.ComponentModel;
using System.Threading;
using System.Windows.Media;
namespace TestBackgroundWorker7338
{
public partial class Window1 : Window
{
private BackgroundWorker _worker;
int _percentageFinished = 0;
public Window1()
{
InitializeComponent();
ButtonCancel.IsEnabled = false;
}
private void Button_Start(object sender, RoutedEventArgs e)
{
_worker = new BackgroundWorker();
_worker.WorkerReportsProgress = true;
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += (s, args) =>
{
BackgroundWorker worker = s as BackgroundWorker;
int numberOfTasks = 300;
for (int i = 1; i <= numberOfTasks; i++)
{
if (worker.CancellationPending)
{
args.Cancel = true;
return;
}
Thread.Sleep(10);
float percentageDone = (i / (float)numberOfTasks) * 100f;
worker.ReportProgress((int)percentageDone);
}
};
_worker.ProgressChanged += (s,args) =>
{
_percentageFinished = args.ProgressPercentage;
ProgressBar.Value = _percentageFinished;
Message.Text = _percentageFinished + "% finished";
if (_percentageFinished < 500)
{
Message.Text = "stopped at " + _percentageFinished + "%";
}
else
{
Message.Text = "finished";
}
if (_percentageFinished >= 70)
{
InputBox.Foreground = new SolidColorBrush(Colors.Red);
}
else if (_percentageFinished >= 40)
{
InputBox.Foreground = new SolidColorBrush(Colors.Orange);
}
else if (_percentageFinished >= 10)
{
InputBox.Foreground = new SolidColorBrush(Colors.Brown);
}
else
{
InputBox.Foreground = new SolidColorBrush(Colors.Black);
}
};
_worker.RunWorkerCompleted += (s,args) =>
{
ButtonStart.IsEnabled = true;
ButtonCancel.IsEnabled = false;
ProgressBar.Value = 0;
};
_worker.RunWorkerAsync();
ButtonStart.IsEnabled = false;
ButtonCancel.IsEnabled = true;
}
private void Button_Cancel(object sender, RoutedEventArgs e)
{
_worker.CancelAsync();
}
}
}
In the following code example, I want to change the color of the Foreground text of a TextBox from my BackgroundThread, but it gets the error:
This thread cannot access this object
since it is in another thread.
What do I have to change in the code below so that the background worker thread can change the foreground color in the TextBox?
Answer:
Thanks Andy, it was just that small oversight, here is the corrected code for posterity's sake:
using System.Windows;
using System.ComponentModel;
using System.Threading;
using System.Windows.Media;
namespace TestBackgroundWorker7338
{
public partial class Window1 : Window
{
private BackgroundWorker _worker;
int _percentageFinished = 0;
public Window1()
{
InitializeComponent();
ButtonCancel.IsEnabled = false;
}
private void Button_Start(object sender, RoutedEventArgs e)
{
_worker = new BackgroundWorker();
_worker.WorkerReportsProgress = true;
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += (s, args) =>
{
BackgroundWorker worker = s as BackgroundWorker;
int numberOfTasks = 300;
for (int i = 1; i <= numberOfTasks; i++)
{
if (worker.CancellationPending)
{
args.Cancel = true;
return;
}
Thread.Sleep(10);
float percentageDone = (i / (float)numberOfTasks) * 100f;
worker.ReportProgress((int)percentageDone);
}
};
_worker.ProgressChanged += (s,args) =>
{
_percentageFinished = args.ProgressPercentage;
ProgressBar.Value = _percentageFinished;
Message.Text = _percentageFinished + "% finished";
if (_percentageFinished < 500)
{
Message.Text = "stopped at " + _percentageFinished + "%";
}
else
{
Message.Text = "finished";
}
if (_percentageFinished >= 70)
{
InputBox.Foreground = new SolidColorBrush(Colors.Red);
}
else if (_percentageFinished >= 40)
{
InputBox.Foreground = new SolidColorBrush(Colors.Orange);
}
else if (_percentageFinished >= 10)
{
InputBox.Foreground = new SolidColorBrush(Colors.Brown);
}
else
{
InputBox.Foreground = new SolidColorBrush(Colors.Black);
}
};
_worker.RunWorkerCompleted += (s,args) =>
{
ButtonStart.IsEnabled = true;
ButtonCancel.IsEnabled = false;
ProgressBar.Value = 0;
};
_worker.RunWorkerAsync();
ButtonStart.IsEnabled = false;
ButtonCancel.IsEnabled = true;
}
private void Button_Cancel(object sender, RoutedEventArgs e)
{
_worker.CancelAsync();
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
正如 Andy 所说,更改控件的属性必须发生在 UI 线程上。
WPF 提供了一个 Dispatcher 类,可以更轻松地将控件调用路由到适当的 UI 线程。 WPF 中的控件公开一个
Dispatcher
属性对象,以将调用分派到适当的 UI 线程。您可以按如下方式使用它(我会在
Button_Start
方法中的worker.ReportProgress((int)percentageDone);
行之后添加它):As Andy said changing properties of controls must happen on the UI thread.
WPF provides a
Dispatcher
class that makes it easier to route calls for controls to the appropriate UI thread. Controls in WPF exposes aDispatcher
property object to dispatch the call to an appropriate UI thread.You can use this as follows (I would add this after the line
worker.ReportProgress((int)percentageDone);
in theButton_Start
method):ProgressChanged
委托在 UI 线程上运行。 如果您在那里设置BackgroundColor
而不是在DoWork
中,那么您应该可以设置颜色而不会出现错误。The
ProgressChanged
delegate is run on the UI thread. If you set theBackgroundColor
there instead of inDoWork
, that should allow you to set the color without the error.