如何从后台工作线程更改 XAML 元素?

发布于 2024-07-30 06:16:20 字数 3231 浏览 4 评论 0原文

在下面的代码示例中,我想从我的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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一生独一 2024-08-06 06:16:20

正如 Andy 所说,更改控件的属性必须发生在 UI 线程上。

WPF 提供了一个 Dispatcher 类,可以更轻松地将控件调用路由到适当的 UI 线程。 WPF 中的控件公开一个 Dispatcher 属性对象,以将调用分派到适当的 UI 线程。

您可以按如下方式使用它(我会在 Button_Start 方法中的 worker.ReportProgress((int)percentageDone); 行之后添加它):

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

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 a Dispatcher 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 the Button_Start method):

// 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);
    }
}
心碎无痕… 2024-08-06 06:16:20

ProgressChanged 委托在 UI 线程上运行。 如果您在那里设置 BackgroundColor 而不是在 DoWork 中,那么您应该可以设置颜色而不会出现错误。

The ProgressChanged delegate is run on the UI thread. If you set the BackgroundColor there instead of in DoWork, that should allow you to set the color without the error.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文