是由于不使用Dispatcher而导致的错误/问题
本文关键字:错误 问题 于不使 Dispatcher | 更新日期: 2023-09-27 18:17:12
我正在使用这个很棒的框架在WPF中做模态对话框。
使用这个框架,我试图得到一个模态对话框覆盖另一个模态对话框,当用户从第一个对话框内单击一个按钮。在这个框架的DemoApp
中有一个这样的例子,它只是使用_dialogmanager
先弹出一个MessageDialog
,然后是另一个。
从DemoApp
中执行此操作的代码如下所示:
private void ShowLayeredDialog()
{
_dialogManager
.CreateMessageDialog("Wait 2 secs...", "I'm the 1st dialog", DialogMode.Ok)
.Show();
ThreadPool.QueueUserWorkItem(o =>
{
Thread.Sleep(2000);
_dialogManager
.CreateMessageDialog("Hello again...", "I'm the 2nd dialog", DialogMode.Ok)
.Show();
});
}
我试图做一些类似的事情,但不是使用CreateMessageDialog的方法调用,我想使用他们的CreateCustomContentDialog()
,它接受一个对象,并显示其内容(提供其UIElement
)在一个模态视图。
因此,已经调用了_dialogManager
来让我进入第一个模态视图,我在该视图上创建了一个按钮,该按钮将生成一个新的CustomContentDialog,就像这样使用类似于DemoApp代码的技术:
ThreadPool.QueueUserWorkItem(o => _dialogManager.CreateCustomContentDialog(new SpikePhaseView(), DialogMode.Ok).Show());
不幸的是,我得到了异常'调用线程必须是STA,因为许多UI组件需要这个'对SpikePhaseView()
的构造函数,这是一个香草UserControl。
因此,在这里和这里研究了这个错误之后,我从设置ApartmentState(ApartmentState. sta)的第二个链接中实现了不被接受但高度支持的解决方案,如下所示:
var test = new Thread(() => _dialogManager.CreateCustomContentDialog(new SpikePhaseView(), DialogMode.Ok).Show());
test.SetApartmentState(ApartmentState.STA);
test.Start();
但是在wpfdialogmanagement框架代码的某个地方,我得到这个错误'调用线程不能访问这个对象,因为不同的线程拥有它。'在这段代码上:
public void SetCustomContent(object content)
{
CustomContent.Content = content;
}
上面,CustomContent (A System.Windows.Controls.ContentControl)被设置为我的SpikePhaseView对象。
编辑
在DemoApp中,他们能够成功地启动两个模态对话框(没有错误)。为什么我不能有一个UserControl(视图)衍生另一个没有这个冲突线程设置这个CustomContext对象的内容?
似乎设置ApartmentState帮助我克服了第一个错误,但如果这一切归结为使用Dispatcher,谁能提供一个例子,我如何在我的情况下使用它来启动第二个模态视图的调用?
谢谢
你不希望在你的应用程序中有多个UI线程,除非你真的,真的确定你需要它。如果您总是需要考虑哪个线程拥有哪个控件,这将使问题变得非常复杂。如果只有一个UI线程,并且它拥有一切,那就容易多了。
而不是试图让你创建一个STA线程的新线程,只是确保你的第二个弹出窗口是从主UI线程创建/访问/显示。
如果你使用的是c# 5.0,这是非常简单:
//Consider changing the return type to Task
//if this method is not used as an event handler
private async void ShowLayeredDialog()
{
_dialogManager
.CreateMessageDialog("Wait 2 secs...", "I'm the 1st dialog", DialogMode.Ok)
.Show();
await Task.Delay(2000);
_dialogManager
.CreateMessageDialog("Hello again...", "I'm the 2nd dialog", DialogMode.Ok)
.Show();
}
await
调用将确保方法的其余部分(第二个对话框的创建)作为第一个任务的延续而运行,并且它将确保它在捕获的SynchronizationContext(在本例中表示UI线程)中运行。最终的结果是代码不会阻塞后台线程,让你只在一个声明范围内,将执行得更好,更好地处理错误,更少的输入,等等。
c# 4.0实现这一点的方法是更多的代码:
private void ShowLayeredDialog()
{
_dialogManager
.CreateMessageDialog("Wait 2 secs...", "I'm the 1st dialog", DialogMode.Ok)
.Show();
Task.Delay(2000).ContinueWith(t =>
{
_dialogManager
.CreateMessageDialog("Hello again...", "I'm the 2nd dialog", DialogMode.Ok)
.Show();
}, TaskScheduler.FromCurrentSynchronizationContext());
}
这是(或多或少)第一个代码片段被编译器转换成的。