代码之家  ›  专栏  ›  技术社区  ›  Edward Tanguay

如何从backgroundworker线程更改XAML元素?

  •  2
  • Edward Tanguay  · 技术社区  · 16 年前

    在下面的代码示例中,我想 文本框的前景文本 从我的背景线索 错误

    此线程无法访问此对象 因为它在另一个线程中。

    我必须在下面的代码中更改什么,以便后台工作线程可以更改文本框中的前景色?

    答复:

    谢谢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();
            }
        }
    }
    
    2 回复  |  直到 16 年前
        1
  •  3
  •   dariom    16 年前

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

    WPF提供了一个 Dispatcher 类,使控件调用更容易路由到相应的UI线程。WPF中的控件公开 调度员 属性对象将调用分派到适当的UI线程。

    你可以这样使用它(我会在这行后面加上它 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);
        }
    }
    
        2
  •  2
  •   Andy    16 年前

    这个 ProgressChanged 委托在UI线程上运行。如果你设定 BackgroundColor 在那里而不是在里面 DoWork ,这将允许您设置颜色而不会出现错误。