В следующем примере кода я хочу изменить цвет текста Foreground TextBox из моего BackgroundThread, но он получает ошибку:
Этот поток не может получить доступ к этому объекту поскольку он находится в другом потоке.
Что мне нужно изменить в приведенном ниже коде, чтобы поток рабочего фона мог изменить цвет переднего плана в TextBox?
Спасибо Энди, это был просто маленький недосмотр, вот исправленный код для потомков:
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();
}
}
}
Делитель ProgressChanged
запускается в потоке пользовательского интерфейса. Если вы установите BackgroundColor
вместо DoWork
, это должно позволить вам установить цвет без ошибки.
Как сказал Энди, изменение изменений элементов управления должно происходить в потоке пользовательского интерфейса.
WPF предоставляет класс Dispatcher
, который упрощает маршрутизацию вызовов для элементов управления в соответствующий поток пользовательского интерфейса. Элементы управления в WPF предоставляют объект свойства Dispatcher
для отправки вызова в соответствующий поток пользовательского интерфейса.
Вы можете использовать это следующим образом (я бы добавил это после строки worker.ReportProgress((int)percentageDone);
в методе Button_Start
):
// the Window class exposes a Dispatcher property, alternatively you could use
// InputBox.Dispatcher to the same effect
if (!Dispatcher.CheckAccess())
{
Dispatcher.Invoke(new Action(() => ChangeForegroundColor(percentageDone));
}
else
{
ChangeForegroundColor(percentageDone);
}
...
private void ChangeForegroundColor(int percentageDone)
{
if (percentageDone >= 90)
{
InputBox.Foreground = new SolidColorBrush(Colors.Red);
}
else if(percentageDone >=10)
{
InputBox.Foreground = new SolidColorBrush(Colors.Orange);
}
}