Har*_*can 6 wpf multithreading dispatcher sta task-parallel-library
我知道在这个主题上有一些关于SO的答案,但是我无法让任何解决方案适合我.我试图从一个从datatemplate中发出的ICommand打开一个新窗口.当实例化新窗口时(在"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();
}
}
Run Code Online (Sandbox Code Playgroud)
使用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();
}
}
Run Code Online (Sandbox Code Playgroud)
谢谢
编辑.根据到目前为止的响应,我想指出我还在当前调度程序上尝试了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();
}
}
Run Code Online (Sandbox Code Playgroud)
在同一个线程中 更新:如果您已经在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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
BeginInvoke,使用对第一个/主窗口调度程序的引用 更新:工作
public void Execute(object sender)
{
if (sender is UserC)
{
var user = (UserC)sender;
GeneralManager.MainDispatcher.BeginInvoke(
DispatcherPriority.Normal,
new Action(() => this.CreateMessageWindow(user)));
}
}
Run Code Online (Sandbox Code Playgroud)
其中GeneralManager.MainDispatcher是对我创建的第一个窗口的Dispatcher的引用:
[somewhere far far away]
mainP = new MainP();
MainDispatcher = mainP.View.Dispatcher;
Run Code Online (Sandbox Code Playgroud)
我不知所措.
调用线程不仅必须是STA,还必须有消息循环.您的应用程序中只有一个线程已经有一个消息循环,这是您的主线程.因此,您应该使用Dispatcher.BeginInvoke从主线程打开窗口.
例如,如果您有对主应用程序窗口(MainWindow)的引用,则可以执行此操作
MainWindow.BeginInvoke(
DispatcherPriority.Normal,
new Action(() => this.CreateMessageWindow(user)));
Run Code Online (Sandbox Code Playgroud)
更新: 小心:你不能盲目打电话,Dispatcher.CurrentDispatcher因为它没有按照你的想法做到.该文件说CurrentDispatcher:
获取当前正在执行的线程的Dispatcher,并创建一个新的Dispatcher(如果尚未与该线程关联).
这就是为什么你必须使用Dispatcher与已经存在的UI控件相关联的原因(就像上面的例子中的主窗口一样).
您正在尝试从后台线程创建一个窗口。由于各种原因你无法做到这一点。通常,您需要在主应用程序线程中创建窗口。
对于您的情况,一个简单的想法是立即执行(只需CreateMessageWindow在内部调用Execute)而不是分配 a Task,因为您的命令肯定会从主(UI)线程触发。如果您不确定Execute运行的线程,您可以使用 来将其编组到 UI 线程Dispatcher.BeginInvoke()。
在极少数情况下,您希望新窗口在非主线程中运行。如果您的情况确实需要这样做,您应该Dispatcher.Run();在后面添加messageP.View.Show();(使用代码的第二个变体)。这将在新线程中启动消息循环。
您不应该尝试在 TPL 的线程中运行窗口,因为这些线程通常是线程池线程,因此不受您的控制。例如,您无法确保它们是 STA(通常是 MTA)。
编辑:
从更新代码中的错误来看,很明显它Execute在某些非 UI 线程中运行。尝试使用Application.Current.Dispatcher而不是Dispatcher.CurrentDispatcher. (CurrentDispatcher表示当前线程的调度程序,如果当前线程不是主线程,则可能会出错。)
| 归档时间: |
|
| 查看次数: |
17801 次 |
| 最近记录: |