“调用线程必须是STA".解决方法 [英] "The calling thread must be STA" workaround
问题描述
我知道关于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屋!