简体   繁体   English

从单独的线程创建WPF控件

[英]Creating WPF control from a separate thread

I have an asynchronously running Task that fires an event when it's completed like this: 我有一个异步运行的Task ,它在完成时会触发一个事件,如下所示:

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

The event handler then should create an instance of a WPF control. 然后,事件处理程序应创建WPF控件的实例。 But when I try to do so, it causes an exception: The calling thread must be STA, because many UI components require this . 但是,当我尝试这样做时,它会导致一个异常: The calling thread must be STA, because many UI components require this Exception occurs in the class constructor, when calling method InitializeComponent() . 调用方法InitializeComponent()时,类构造函数中发生异常。

As far as I know, usually accessing WPF controls from separate threads is handled using the Dispatcher.Invoke , and it always worked for me, so I tried it: 据我所知,通常使用Dispatcher.Invoke处理从单独的线程访问WPF控件的过程,它始终对我有用,所以我尝试了一下:

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

But in that case exception keeps occurring. 但是在那种情况下,异常不断发生。 How do I create an instance of a WPF control from a separate thread? 如何从单独的线程创建WPF控件的实例?

Or maybe it will be a better approach to marshal the completion event to the main UI thread. 也许这是将完成事件封送至主UI线程的更好方法。 If yes, how can I do that? 如果是,我该怎么做?

You have to use a Dispatcher instance, which was associated with the UI thread. 您必须使用与UI线程关联的Dispatcher实例。 If you are writing something like this: 如果您正在编写如下内容:

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

In the task body, you're using dispatcher of the calling thread, which can be a background thread from a pool. 在任务主体中,您正在使用调用线程的调度程序,该线程可以是池中的后台线程。

Anyway, with tasks you shouldn't use Dispatcher directly. 无论如何,对于任务,您不应该直接使用Dispatcher。 Use an appropriate task scheduler: 使用适当的任务计划程序:

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

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

where tasks is a sequence of tasks being executed with the default scheduler. 其中tasks是使用默认调度程序执行的一系列任务。

I have done this in the past: 我过去曾这样做:

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

Calling InitializeComponent from the constructor on another thread seems like looking for trouble. 从构造函数在另一个线程上调用InitializeComponent似乎很麻烦。 The object isn't there yet (we're in the constructor) 该对象尚不存在(我们在构造函数中)

Marshaling it back to the UI thread would normally do the trick but during the constructor looks like a bad idea to me. 将其封送回UI线程通常可以解决问题,但是在构造函数期间,这对我来说似乎是个坏主意。

If you want to initialize the control asynchronously just subscribe to the loaded event, so you know the object is there, spawn a thread that does some calculations/data retrieval and marshals the data back to the UI thread to display it. 如果要异步初始化控件,只需订阅已加载的事件,就可以知道该对象已存在,请生成一个线程进行一些计算/数据检索,并将数据封送回UI线程以进行显示。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM