【问题标题】:How to make backgroundworker work in WPF application如何让后台工作人员在 WPF 应用程序中工作
【发布时间】:2014-07-17 16:00:19
【问题描述】:

我对编程和 WPF 架构非常陌生。我有一个使用 backgroundworker 类的 WPF 应用程序。但是,它总是抛出错误“调用线程必须是 sta,因为许多 ui 组件需要这个”。我需要在我的主要方法中添加 STAThread 属性。但我不知道该怎么做。

public partial class MainWindow : Window
    {
public MainWindow()
        {
            InitializeComponent();

            this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();           

            InitializeBackgroundWorker();
            Thread.CurrentThread.SetApartmentState(ApartmentState.STA);
            tabItemList.CollectionChanged += this.TabCollectionChanged;
        }
}


 private void InitializeBackgroundWorker()
        {
            backgroundWorker1.DoWork +=
                new DoWorkEventHandler(backgroundWorker1_DoWork);
            backgroundWorker1.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(
            backgroundWorker1_RunWorkerCompleted);
            backgroundWorker1.ProgressChanged +=
                new ProgressChangedEventHandler(
            backgroundWorker1_ProgressChanged);
        }

        // This event handler is where the actual, 
        // potentially time-consuming work is done. 
        private void backgroundWorker1_DoWork(object sender,
            DoWorkEventArgs e)
        {
            // Get the BackgroundWorker that raised this event.
            BackgroundWorker worker = sender as BackgroundWorker;

            // Assign the result of the computation 
            // to the Result property of the DoWorkEventArgs 
            // object. This is will be available to the  
            // RunWorkerCompleted eventhandler.
            //e.Result = AddTabitem((int)e.Argument, worker, e);
            AddTabitem((string)e.Argument, worker, e);
        }

 void AddTabitem(string filePath, BackgroundWorker worker, DoWorkEventArgs e)
        {
            if (File.Exists(filePath))
            {
//This line which throws error "the calling thread must be sta because many ui components require this"
                RichTextBox mcRTB = new RichTextBox();
                rtbList.Add(mcRTB);
}

【问题讨论】:

  • 除了同意 democodemonkey 的回答之外,您还应该查看“任务”类。
  • 您几乎没有理由考虑使用BackgroundWorker。一方面,如果您使用 MVVM/Binding,您将拥有自动 UI 线程封送处理。另一方面,您应该将Task.Run 用于任何 CPU 密集型计算和异步任务 I/O 用于任何 I/O 密集型操作。

标签: c# wpf backgroundworker


【解决方案1】:

您必须在线程启动之前设置 ApartmentState。

http://blogs.msdn.com/b/jfoscoding/archive/2005/04/07/406341.aspx

要在 WPF 应用上设置公寓状态,
[STAThread] 属性添加到您的应用程序中,例如this

public partial class App : Application
{
    App()
    {
        InitializeComponent();
    }

    [STAThread]
    static void Main()
    {
        Window1 window = new Window1();
        App app = new App();
        app.Run(window);
    }
}

【讨论】:

  • 当我添加这部分代码时,它会抛出一个错误,提示“Editor.App”已经定义了一个名为“Main”的具有相同参数类型的成员。而且我确实看到 App.g.cs 中存在 Main(),并且我还看到在 Main() 之前添加了 [System.STAThreadAttribute()]。那为什么我的程序还是会报错呢?
  • 抱歉,我错过了将应用程序的构建操作从“应用程序定义”更改为“页面”的步骤。即使这样做之后,当我运行应用程序时,它仍然会抛出“调用线程必须是 sta,因为许多 ui 组件都需要这个”。我在我的问题中添加了 AddTabItem 代码以显示错误的确切位置。我在这里做错了什么?
  • 使用 this.Dispatcher.Invoke((Action)(() => { ...// 你的代码在这里。}));解决了我的问题
【解决方案2】:

编辑:

我对 STAThread 不屑一顾,因为这不是你的问题。

查看更新后的后台工作线程代码后,您正尝试从后台工作线程更新 UI。这是行不通的,正如你所经历的那样。你有几个选择:

1) 不是直接更新 RichTextBox,而是更新您已数据绑定到您正在更新的属性的变量。有关数据绑定的概述,请参阅本文,因为您已声明您是 WPF 新手: http://msdn.microsoft.com/en-us/library/ms752347(v=vs.110).aspx

2) 使用 Invoke 而不是直接设置属性。无论如何,您可能需要对选项 1 执行此操作。这可能是您最直接的快速修复。这是执行此调用业务所需的 WPF 调度程序的充分理由:http://tech.pro/tutorial/800/working-with-the-wpf-dispatcher

它会像这样工作: mcRTB.Dispatcher.Invoke( System.Windows.Threading.DispatcherPriority.Normal, 新动作( 代表() { mcRTB.Text = "你好"; } ));

以上内容用于更改属性。您也可以调整它以调用 rtbList 的 .Add 方法。

3) 想要避免调用?使用您的后台工作人员的报告进度!当您在 BW 中告知该事件时会触发此事件,然后您可以在主 UI 线程上做一些工作,而不必担心调用内容。

当你初始化你的 BW 时,把它加进去:

backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.ProgressChanged += new ProgressChanged EventHandler(backgroundWorker1_ProgressChanged);

然后,在您进行的回调(或使用 += 为您自动创建的回调)中,您可以在 UI 线程上进行工作。您可以通过执行以下操作告诉您的 BW 报告一些进度:

// the int in the first parameter is arbitrary
// the object can be any object. This is how you pass actual data to your call back
backgroundWorker1.ReportProgress(1,new object()) 

以下是有关报告进度的好读物: http://msdn.microsoft.com/en-us/library/a3zbdb1t(v=vs.110).aspx

【讨论】:

  • 它给出了一个错误,Arrtribute 'STAThread' 在这个声明类型上是无效的。它仅对“方法”声明有效。
  • 你说得对,它应该走主入口点方法。有关正确放置的信息,请参阅 democodemonkey 的答案。尽管如此,你不应该需要一个 STAThread 来让后台工作人员做它的事情。您可以发布所有与后台工作人员相关的代码吗?
  • 添加了详细显示后台工作人员的剩余代码。
  • 你是对的,从后台工作线程更新 UI 是问题,使用 Dispatch.invoke 确实解决了我的问题。非常感谢。
  • 很高兴能帮上忙!如果这解决了你的问题,你能接受这个答案吗?谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-23
  • 1970-01-01
相关资源
最近更新 更多