如何在WPF应用程序中使后台工作器(BackgroundWorker)正常工作

3

我对编程和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);
}

1
除了赞同demoncodemonkey的答案之外,你还应该研究一下“Task”类。 - Dbl
几乎没有理由让你考虑使用“BackgroundWorker”。如果你使用MVVM / Binding,你会有自动UI线程调度。此外,对于任何CPU绑定计算,你应该使用“Task.Run”,对于任何I / O绑定操作,你应该使用异步任务I / O。 - Aron
2个回答

2

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

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

要在WPF应用程序上设置公寓状态,请向应用程序添加[STAThread]属性,如此示例

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()]。那么为什么我的程序仍然会抛出错误呢? - savi
抱歉,我错过了改变应用程序的构建操作从“应用程序定义”到“页面”的步骤。即使我这样做了,当我运行应用程序时,它仍然会抛出“调用线程必须是sta,因为许多UI组件需要此线程”。我在我的问题中添加了AddTabItem代码,以显示它出错的确切位置。我在这里做错了什么? - savi
2
使用 this.Dispatcher.Invoke((Action)(() => { ...// 在这里编写你的代码。 })); 解决了我的问题。 - savi

1

编辑:

我删除了关于STAThread的内容,因为这不是你的问题。

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

1)不要直接更新那个RichTextBox,而是更新一个你已经绑定到该属性的变量。由于你表示你是WPF的新手,请参阅此文章以获取数据绑定的概述: http://msdn.microsoft.com/en-us/library/ms752347(v=vs.110).aspx

2)使用Invoke而不是直接设置属性。无论如何,你可能需要这样做来完成选项1。这可能是你最直接的快速解决方法。这里有一个使用WPF Dispatcher的好理由,这是你需要做这个调用业务的东西:http://tech.pro/tutorial/800/working-with-the-wpf-dispatcher

它将像这样工作:

mcRTB.Dispatcher.Invoke( System.Windows.Threading.DispatcherPriority.Normal, new Action( delegate() { mcRTB.Text = "hello"; } ));

以上内容是用于更改属性。您也可以将其调整为调用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


它会报错,属性 'STAThread' 在此声明类型上无效。它只在 '方法' 声明上有效。 - savi
你说得对,它应该进入主入口点方法。请参考demoncodemonkey的答案以获得正确的放置位置。不过,你不应该需要一个STAThread来让后台工作程序正常运行。你能把所有与后台工作程序相关的代码都贴出来吗? - Bill Sambrone
添加了剩余的代码,其中详细显示了后台工作器。 - savi
1
你说得对,从BackgroundWorker线程更新UI是问题所在,使用Dispatch.invoke解决了我的问题。非常感谢。 - savi
很高兴能够帮到您!如果这个解决方案解决了您的问题,您可以接受这个答案吗?谢谢! - Bill Sambrone

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接