“调用线程必须是STA".解决方法 [英] "The calling thread must be STA" workaround

查看:1236
本文介绍了“调用线程必须是STA".解决方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道关于SO的这个主题有一些答案,但是我找不到任何适合我的解决方案.我正在尝试从数据模板内触发的ICommand打开一个新窗口.当实例化新窗口时,以下两项都会给出上述错误 (在"new MessageWindowP"中):

I know there are a few answers on this topic on SO, but I can not get any of the solutions working for me. I am trying to open a new window, from an ICommand fired from within a datatemplate. Both of the following give the aforementioned error when the new window is instantiated (within "new MessageWindowP"):

使用TPL/FromCurrentSynchronizationContext 更新:有效

public class ChatUserCommand : ICommand
{
    public void Execute(object sender)
    {
        if (sender is UserC)
        {
            var user = (UserC)sender;
            var scheduler = TaskScheduler.FromCurrentSynchronizationContext();                  
            Task.Factory.StartNew(new Action<object>(CreateMessageWindow), user,CancellationToken.None, TaskCreationOptions.None,scheduler);         
        }
    }

    private void CreateMessageWindow(object o)
    {
        var user = (UserC)o;
        var messageP = new MessageWindowP();
        messageP.ViewModel.Participants.Add(user);
        messageP.View.Show();
    }
}

使用ThreadStart: 更新:不推荐,请参见乔恩的答案

public class ChatUserCommand : ICommand
{
    public void Execute(object sender)
    {
        if (sender is UserC)
        {
            var user = (UserC)sender;

            var t = new ParameterizedThreadStart(CreateMessageWindow);
            var thread = new Thread(t);
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start(sender);           
        }
    }

    private void CreateMessageWindow(object o)
    {
        var user = (UserC)o;
        var messageP = new MessageWindowP();
        messageP.ViewModel.Participants.Add(user);
        messageP.View.Show();
    }
}

谢谢

编辑.根据到目前为止的响应,我想指出的是,我还在当前的分派器上尝试了BeginInvoke,并在原始方法中执行了代码(这就是代码的开始方式).见下文:

EDIT. Based on the responses so far, I'd like to point out that I have also tried BeginInvoke on the current dispatcher, as well as executing the code in the original method (that's how the code started). See below:

开始调用 更新:不建议参阅乔恩的回答

public class ChatUserCommand : ICommand
{
    public void Execute(object sender)
    {
        if (sender is UserC)
        {
            var user = (UserC)sender;
            Dispatcher.CurrentDispatcher.BeginInvoke(new Action<object>(CreateMessageWindow), sender);       
        }
    }

    private void CreateMessageWindow(object o)
    {
        var user = (UserC)o;
        var messageP = new MessageWindowP();
        messageP.ViewModel.Participants.Add(user);
        messageP.View.Show();
    }
}

在同一线程中 更新:如果您已经在UI线程上,则可以使用

public class ChatUserCommand : ICommand
{
    public void Execute(object sender)
    {
        if (sender is UserC)
        {
            var user = (UserC)sender;
            var messageP = new MessageWindowP();
            messageP.ViewModel.Participants.Add(user);
            messageP.View.Show();    
        }
    }

}

开始调用,使用对第一个/主窗口的调度程序的引用 更新:有效

 public void Execute(object sender)
   {
       if (sender is UserC)
       {
            var user = (UserC)sender;
                    GeneralManager.MainDispatcher.BeginInvoke(
                               DispatcherPriority.Normal,
                               new Action(() => this.CreateMessageWindow(user)));      
        }
    }

其中GeneralManager.MainDispatcher是对我创建的第一个窗口的Dispatcher的引用:

where GeneralManager.MainDispatcher is a reference to the Dispatcher of the first window I create:

     [somewhere far far away]
        mainP = new MainP();
        MainDispatcher = mainP.View.Dispatcher;

我很茫然.

推荐答案

调用线程不得仅 是STA,但还必须具有消息循环.您的应用程序中只有一个线程已经具有消息循环,这就是您的主线程.因此,您应该使用Dispatcher.BeginInvoke从主线程打开窗口.

The calling thread must not only be STA, but it must also have a message loop. There's only one thread in your application that already has a message loop, and that's your main thread. So you should use Dispatcher.BeginInvoke to open your window from your main thread.

例如如果您有对主应用程序窗口(MainWindow)的引用,则可以

E.g. if you have a reference to your main application window (MainWindow), you can do

MainWindow.BeginInvoke(
    DispatcherPriority.Normal, 
    new Action(() => this.CreateMessageWindow(user)));

更新: 请注意:不能盲目地致电Dispatcher.CurrentDispatcher,因为它没有按照您的想法去做. 文档表示CurrentDispatcher:

Update: Be careful: you cannot blindly call Dispatcher.CurrentDispatcher because it doesn't do what you think it does. The documentation says that CurrentDispatcher:

获取当前正在执行的线程的Dispatcher并创建一个 新的Dispatcher(如果尚未与该线程关联).

Gets the Dispatcher for the thread currently executing and creates a new Dispatcher if one is not already associated with the thread.

这就是为什么必须使用与已经存在的UI控件相关联的Dispatcher的原因(例如上例中的主窗口).

That's why you must use the Dispatcher associated with an already-existing UI control (like your main window as in the example above).

这篇关于“调用线程必须是STA".解决方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆