2

我有一个异步运行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 线程可能是一种更好的方法。如果是,我该怎么做?

4

3 回答 3

3

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

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

在任务主体中,您正在使用调用线程的调度程序,它可以是池中的后台线程。

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

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

    }, CancellationToken.None, TaskContinuationOptions.None, ui);

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

于 2012-05-25T05:36:51.743 回答
1

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

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

如果你想异步初始化控件,只需订阅加载的事件,这样你就知道对象在那里,生成一个线程来执行一些计算/数据检索并将数据编组回 UI 线程以显示它。

于 2012-05-25T05:35:22.183 回答
1

我过去曾这样做过:

 Dispatcher.Invoke(DispatcherPriority.Normal,
                   new Action(
                        delegate()
                        {
                            // Access control created by main thread
                            textBlock.Text = msg;
                        }
                   ));
于 2012-05-25T05:30:25.983 回答