是由于不使用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,谁能提供一个例子,我如何在我的情况下使用它来启动第二个模态视图的调用?

谢谢

是由于不使用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());
}

这是(或多或少)第一个代码片段被编译器转换成的。