从WinForms BackgroundWorker创建WPF窗口

3
我有一个包含多个窗口类并公开显示这些窗口的方法的WPF dll。
我还有一个单独的WinForms项目,该项目在BackgroundWorker组件的DoWork方法中调用WPF项目中的其中一种方法。
在实例化WPF Window的代码行上,我收到以下运行时错误:
“因为许多UI组件需要它,所以调用线程必须是STA。”
谷歌搜索引导我进入this discussion。 (原来Jon Skeet除了Stack Overflow之外还在其他网站回答问题!)他链接到this article,其中提到:
“BackgroundWorker组件与WPF很好地配合使用......”
该文章还提到使用DispatcherObject类,但我不知道如何使其工作,而且我更愿意继续使用我的BackgroundWorker组件。
作为一个测试案例,我想出了以下代码来重现错误。在WPF类库中,以下是Window1.xaml.vb的代码。
Partial Public Class Window1

    Public Shared Function ShowMe() As Boolean?
        Dim w = New Window1 'Error appears on this line.
        Return w.ShowDialog()
    End Function

End Class

在WinForms应用程序中,这是Form1.vb中的代码。
Imports System.ComponentModel

Public Class Form1

    Private WithEvents worker As BackgroundWorker
    Private Sub doWord(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles worker.DoWork
        WpfLibrary.Window1.ShowMe()
    End Sub
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        worker = New BackgroundWorker
        worker.RunWorkerAsync()
    End Sub
End Class

即使将BackgroundWorker组件放置在Window1.xaml.vb中,仍会出现相同的错误。那么,那篇文章是错误的,我真的不能在WPF中使用BackgroundWorker吗?还是我需要做其他事情才能让它正常工作?
如果BackgroundWorker无法工作,那么我该如何替换Form1.vb中的代码以使用Dispatcher呢?
2个回答

4

在WPF中,您可以使用后台工作程序,这不是您的问题。

您的问题是,在winform或WPF应用程序中,您无法从主UI线程以外的任何位置执行更新UI的任务,而BackgroundWorker的DoWork方法正在运行不同的线程中。

因此,您必须在BackgroundWorker的后台线程之外打开新窗口,要么在启动BackgroundWorker之前,要么在其RunWorkerCompleted事件中。

如果没有了解调用打开窗口的代码,我很难提供更多建议,但是希望这可以指引您朝着正确的方向前进。


谢谢!我刚刚把代码从DoWork事件复制到了RunWorkerCompleted事件中,现在可以运行了。 - CoderDennis
提供信息,我认为 RunWorkerCompleted 在启动工作程序的线程上运行(而非工作程序的线程)。如果是这样的话,您可能根本不需要使用 BackgroundWorker。 - Eddie Deyo
不是这样的。在WPF中,您可以拥有多个UI线程(我已经做过了)。但是,您不会想要从BackgroundWorker线程显示UI,因为它不完全在您的控制之下 - 线程池决定该线程将执行何种操作以及何时执行。您需要一个常规线程,将其设置为STA,并从ThreadStart方法调用Application.Run。 - Joe White
@OAB,我不在乎哪个线程正在运行代码。 我只需要方法立即返回,而不是等待窗口显示,完成任务然后关闭。 - CoderDennis

3

你的工作线程也可以创建自己的线程,并将该线程标记为STA,然后调用 Thread.Join() 等待其终止。然后你就能在新线程上创建并显示窗口(请注意,没有使用 Dispatcher,其他线程将无法与它交互)。

C# 示例:

Thread workerThread = new Thread(ShowMyWindow);
workerThread.SetApartmentState(ApartmentState.STA);
workerThread.Start();
workerThread.Join();

接着:

private void ShowMyWindow()
{
    WpfLibrary.Window1.ShowMe()
}

那在代码中看起来是什么样子?线程如何标记STA? - CoderDennis
我已经添加了一个C#的例子。很抱歉,我不太熟悉VB,无法提供VB的示例,但是你需要使用SetApartmentState调用。 - Eddie Deyo
很酷,谢谢!我也有使用过C#,但我认为大多数VB开发人员都知道足够的C#来阅读代码示例。我们必须这样做,因为大多数代码示例都是用C#编写的! - CoderDennis

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