Как создать общий / повторно используемый модальный диалог для WPF после MVVM - PullRequest
11 голосов
/ 02 июня 2010

Я хотел бы создать общий / повторно используемый модальный диалог, который я могу использовать в нашем приложении WPF (MVVM) - WCF LOB.

У меня есть View и связанные ViewModels, которые я хотел бы отображать с помощью диалогов. Привязки между представлениями и моделями представления выполняются с использованием шаблонов данных с таргетингом на тип.

Вот некоторые требования, которые мне удалось составить:

  • Я предпочитаю, чтобы это основывалось на Окне, а не на использовании украшателей и элементов управления, которые действуют как модальное диалоговое окно.
  • Он должен получить минимальный размер содержимого.
  • Он должен находиться в центре окна владельца.
  • В окне не должны отображаться кнопки «Свернуть» и «Развернуть».
  • Он должен получить свой заголовок из содержимого.

Каков наилучший способ сделать это?

Ответы [ 2 ]

11 голосов
/ 02 июня 2010

Я обычно имею дело с этим, вставляя этот интерфейс в соответствующие ViewModels:

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>

Вы можете узнать больше об этом (а также загрузить полный пример кода) в моей книге .

7 голосов
/ 02 июня 2010

Я отвечаю на свой вопрос, чтобы помочь другим найти все ответы, которые я изо всех сил пытался найти в одном месте. То, что выше, кажется прямой проблемой, на самом деле представляет множество проблем, на которые я надеюсь ответить достаточно ниже.

Здесь идет.

Ваше окно 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. Эта превосходная статья о проекте кода содержит подробности об этом. Обратите внимание на это сообщение на форуме статьи. Это решение также решает проблему обнаружения окна владельца через экземпляр ViewModel.

Используя этот интерфейс, вы можете вызвать IDialogService.ShowDialog (ownerViewModel, dialogViewModel). На данный момент я звоню от владельца ViewModel, что означает жесткие ссылки между моими ViewModel. Если вы используете агрегированные события, вы, вероятно, будете вызывать это из проводника.

Установка минимального размера в представлении, которое в конечном итоге будет отображаться в диалоговом окне, не устанавливает автоматически минимальный размер диалогового окна. Кроме того, поскольку логическое дерево в диалоговом окне содержит ViewModel, вы не можете просто привязать свойства элемента WindowContent. На этот вопрос есть ответ с моим решением.

Ответ, о котором я упоминал выше, также включает код, который центрирует окно на владельце.

Наконец, отключение кнопок минимизации и максимизации - это то, что WPF не может сделать изначально. Самое элегантное решение ИМХО использует это .

...