从单独的线程创建 WPF 控件

本文关键字:WPF 控件 创建 线程 单独 | 更新日期: 2023-09-27 18:35:28

我有一个异步运行的Task,当它像这样完成时会触发一个事件:

task.ContinueWith(() => {
    if (MyEvent != null)
        MyEvent(this, EventArgs.Empty);
}

然后,事件处理程序应创建 WPF 控件的实例。但是当我尝试这样做时,它会导致异常:The calling thread must be STA, because many UI components require this .调用方法 InitializeComponent() 时,类构造函数中发生异常。

据我所知,通常从单独的线程访问 WPF 控件是使用 Dispatcher.Invoke 来处理的,它总是对我有用,所以我尝试了一下:

Dispatcher.Invoke(new Action(() =>
{
    InitializeComponent();
}));

但在这种情况下,异常不断发生。如何从单独的线程创建 WPF 控件的实例?

或者,也许将完成事件封送到主 UI 线程会是更好的方法。如果是,我该怎么做?

从单独的线程创建 WPF 控件

您必须使用与 UI 线程关联的调度程序实例。如果你正在写这样的东西:

Dispatcher.Invoke(new Action(() =>
{
    InitializeComponent();
}));

在任务正文中,你使用的是调用线程的调度程序,该调度程序可以是池中的后台线程。

无论如何,对于任务,您不应该直接使用调度程序。使用适当的任务计划程序:

var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.ContinueWhenAll(tasks.ToArray(),
    result =>
    {
        // Put you UI calls here
    }, CancellationToken.None, TaskContinuationOptions.None, ui);

其中tasks是使用默认调度程序执行的一系列任务。

从另一个线程上的构造函数调用 InitializeComponent似乎在找麻烦。对象还不存在(我们在构造函数中)

将其封送回 UI 线程通常会解决问题,但在构造函数期间对我来说似乎是一个坏主意。

如果要异步初始化控件,只需订阅加载的事件,以便知道对象在那里,请生成一个线程,该线程执行一些计算/数据检索,并将数据封送回 UI 线程以显示它。

我过去做过这个:

 Dispatcher.Invoke(DispatcherPriority.Normal,
                   new Action(
                        delegate()
                        {
                            // Access control created by main thread
                            textBlock.Text = msg;
                        }
                   ));