1

我正在使用这个奇妙的框架在 WPF 中进行模式对话框。

使用这个框架,当用户从第一个对话框中单击一个按钮时,我试图让一个模态对话框覆盖另一个模态对话框。DemoApp在for this framework 中有一个这样的例子,它只是使用_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 的方法调用,而是使用 their CreateCustomContentDialog(),它接受一个对象并在模式视图中显示其内容(提供它的 a UIElement)。

因此,已经调用_dialogManager让我进入第一个模态视图,我在该视图上创建了一个按钮,该按钮将使用类似于他们的 DemoApp 代码的技术生成一个新的 CustomContentDialog :

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();

但是后来在 WpfDialogManagment 框架代码的某个地方,我收到了这个错误'调用线程无法访问这个对象,因为另一个线程拥有它。' 在这段代码中:

public void SetCustomContent(object content)
{
    CustomContent.Content = content;
}

上面,CustomContent(A System.Windows.Controls.ContentControl)被设置为我的 SpikePhaseView 对象。

编辑

在 DemoApp 中,他们能够成功启动两个模式对话框(没有错误)。为什么我不能让一个 UserControl(视图)生成另一个,而不会在哪个线程设置此 CustomContext 对象的内容时发生冲突?

似乎设置 ApartmentState 帮助我克服了第一个错误,但如果这一切都归结为使用 Dispatcher,有人可以提供一个示例,说明我如何在我的情况下使用它来启动调用以启动第二个模式视图?

谢谢

4

1 回答 1

2

您不希望在您的应用程序中有多个 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());
}

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

于 2013-06-06T17:08:59.743 回答