我知道有关SO的这个主题有一些答案,但我无法让任何解决方案适合我。我试图从一个从datatemplate中发出的ICommand打开一个新窗口。当实例化新窗口时(
使用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: 更新:不推荐,请参阅Jon的回答
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();
}
}
由于
EDIT。根据到目前为止的响应,我想指出我还在当前调度程序上尝试了BeginInvoke,以及在原始方法中执行代码(这就是代码的启动方式)。见下文:
BeginInvoke 更新:不推荐看Jon的回答
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();
}
}
}
BeginInvoke,使用对第一个/主窗口调度程序的引用 更新:工作
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的引用:
[somewhere far far away]
mainP = new MainP();
MainDispatcher = mainP.View.Dispatcher;
我很茫然。
答案 0 :(得分:7)
调用线程不能仅是STA,但它必须还有一个消息循环。您的应用程序中只有一个线程已经有一个消息循环,这是您的主线程。因此,您应该使用Dispatcher.BeginInvoke
从主线程打开窗口。
E.g。如果您有对主应用程序窗口(MainWindow
)的引用,则可以执行
MainWindow.BeginInvoke(
DispatcherPriority.Normal,
new Action(() => this.CreateMessageWindow(user)));
更新: 小心:无法盲目地呼叫Dispatcher.CurrentDispatcher
,因为它没有按照您的想法执行。 documentation表示CurrentDispatcher
:
获取当前正在执行的线程的Dispatcher并创建一个 new Dispatcher如果尚未与该线程关联。
这就是必须使用与已存在的UI控件相关联的Dispatcher
的原因(就像上面示例中的主窗口一样)。
答案 1 :(得分:5)
答案 2 :(得分:3)
您正在尝试从后台线程创建一个窗口。由于各种原因你不能这样做。通常,您需要在主应用程序线程中创建窗口。
对于您的情况,一个简单的想法就是立即执行(只需在CreateMessageWindow
内调用Execute
)而不是分配Task
,因为您的命令肯定会从主要命令发出(UI)线程。如果您不确定运行Execute
的线程,可以使用Dispatcher.BeginInvoke()
将其封送到UI线程。
在您希望新窗口在非主线程中运行的情况非常少。如果在您的情况下确实需要这样做,则应在Dispatcher.Run();
之后添加messageP.View.Show();
(使用代码的第二个变体)。这将在新线程中启动消息循环。
您不应该尝试在TPL的线程中运行窗口,因为这些线程通常是线程池线程,因此无法控制。例如,您无法确保它们是STA(通常是MTA)。
编辑:
从您更新的代码中的错误,很明显Execute
在一些非UI线程中运行。尝试使用Application.Current.Dispatcher
代替Dispatcher.CurrentDispatcher
。 (CurrentDispatcher
表示当前线程的调度程序,如果当前线程不是主线程,则可能是错误的。)