如何使用MVVM构建WPF的通用/可重复使用模态对话框

11
我希望构建一个通用/可重复使用的模态对话框,以便在我们的WPF(MVVM)- WCF LOB应用程序中使用。我有一些视图和关联的视图模型,想要使用对话框来显示。视图和视图模型之间的绑定是使用面向类型的数据模板完成的。
以下是我所能起草的一些要求:
  • 我希望它基于窗口而不是使用行为像模态对话框的装饰器和控件。
  • 它应该从内容获取其最小大小。
  • 它应该在所有者窗口上居中。
  • 窗口不能显示最小化和最大化按钮。
  • 它应该从内容获取其标题。
什么是实现此目标的最佳方法?
2个回答

11

我通常通过将此接口注入到相应的ViewModel中来处理这个问题:

public interface IWindow
{
    void Close();

    IWindow CreateChild(object viewModel);

    void Show();

    bool? ShowDialog();
}

这允许ViewModels生成子窗口并以模式显示它们,而不是以非模式显示。

一个可重用的IWindow实现如下:

public class WindowAdapter : IWindow
{
    private readonly Window wpfWindow;

    public WindowAdapter(Window wpfWindow)
    {
        if (wpfWindow == null)
        {
            throw new ArgumentNullException("window");
        }

        this.wpfWindow = wpfWindow;
    }

    #region IWindow Members

    public virtual void Close()
    {
        this.wpfWindow.Close();
    }

    public virtual IWindow CreateChild(object viewModel)
    {
        var cw = new ContentWindow();
        cw.Owner = this.wpfWindow;
        cw.DataContext = viewModel;
        WindowAdapter.ConfigureBehavior(cw);

        return new WindowAdapter(cw);
    }

    public virtual void Show()
    {
        this.wpfWindow.Show();
    }

    public virtual bool? ShowDialog()
    {
        return this.wpfWindow.ShowDialog();
    }

    #endregion

    protected Window WpfWindow
    {
        get { return this.wpfWindow; }
    }

    private static void ConfigureBehavior(ContentWindow cw)
    {
        cw.WindowStartupLocation = WindowStartupLocation.CenterOwner;
        cw.CommandBindings.Add(new CommandBinding(PresentationCommands.Accept, (sender, e) => cw.DialogResult = true));
    }
}

你可以将此窗口用作可重复使用的宿主窗口。没有代码后台:

<Window x:Class="Ploeh.Samples.ProductManagement.WpfClient.ContentWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:self="clr-namespace:Ploeh.Samples.ProductManagement.WpfClient"
        xmlns:pm="clr-namespace:Ploeh.Samples.ProductManagement.PresentationLogic.Wpf;assembly=Ploeh.Samples.ProductManagement.PresentationLogic.Wpf"
        Title="{Binding Path=Title}"
        Height="300"
        Width="300"
        MinHeight="300"
        MinWidth="300" >
    <Window.Resources>
        <DataTemplate DataType="{x:Type pm:ProductEditorViewModel}">
            <self:ProductEditorControl />
        </DataTemplate>
    </Window.Resources>
    <ContentControl Content="{Binding}" />
</Window>

你可以在我的书中了解更多信息(以及下载完整的代码示例)。


6
我将回答自己的问题,以帮助其他人在一个地方找到我苦苦寻找的所有答案。看起来很简单的问题实际上涉及到多个问题,我希望以下内容能够足够解决。

以下是详细内容。

作为通用对话框的WPF窗口可以像这样:

<Window x:Class="Example.ModalDialogView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ex="clr-namespace:Example"
        Title="{Binding Path=mDialogWindowTitle}" 
        ShowInTaskbar="False" 
        WindowStartupLocation="CenterOwner"
        WindowStyle="SingleBorderWindow"
        SizeToContent="WidthAndHeight"
        ex:WindowCustomizer.CanMaximize="False"
        ex:WindowCustomizer.CanMinimize="False"
        >
    <DockPanel Margin="3">
        <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" FlowDirection="RightToLeft">
            <Button Content="Cancel" IsCancel="True" Margin="3"/>
            <Button Content="OK" IsDefault="True" Margin="3" Click="Button_Click" />
        </StackPanel>
        <ContentPresenter Name="WindowContent" Content="{Binding}"/>
    </DockPanel>
</Window>

遵循MVVM,显示对话框的正确方式是通过中介者。要使用中介者,通常还需要一些服务定位器。有关中介者的具体细节,请参见此处
我采用的解决方案涉及实现IDialogService接口,该接口通过简单的静态ServiceLocator进行解析。这篇优秀的codeproject文章详细介绍了这一点。请注意文章论坛中消息。此解决方案还解决了通过ViewModel实例发现所有者窗口的问题。
使用此接口,您可以调用IDialogService.ShowDialog(ownerViewModel,dialogViewModel)。目前,我从所有者ViewModel中调用它,这意味着我的ViewModel之间存在硬引用。如果您使用聚合事件,则可能会从指挥员中调用此操作。
在最终显示在对话框中的视图上设置最小大小并不会自动设置对话框的最小大小。此外,由于对话框中的逻辑树包含ViewModel,因此无法仅绑定到WindowContent元素的属性。这个问题有一个包含我的解决方案的答案。
我上面提到的答案还包括将窗口居中于所有者的代码。
最后,禁用最小化和最大化按钮是WPF无法本地执行的操作。在我看来,最优雅的解决方案是使用这个

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接