Пару лет назад уже была статья «Показ дочерних View в рамках патерна MVVM«, т.к. сейчас это делаем по другому, да и вопрос тут возник на тостере… Еще раз, в рамках паттерна предполагается что ViewModel (бизнес-логика) работает только с классами ViewModel и Model, а нам необходимо показать окно, т.е. кроме создания ViewModel для него, нужно создать еще и View. Как это сделать? Четвертый вариант под катом.

Пример будет максимально упрощен, но основные идеи постараюсь показать.
1. Создаем пустой WPF проект. В него добавляем класс окно вот с такой разметкой:

<Window x:Class=»ChildWindowsDemo.ChildWindow»

        xmlns=»http://schemas.microsoft.com/winfx/2006/xaml/presentation»
        xmlns:x=»http://schemas.microsoft.com/winfx/2006/xaml»
        xmlns:d=»http://schemas.microsoft.com/expression/blend/2008″
        xmlns:mc=»http://schemas.openxmlformats.org/markup-compatibility/2006″
        xmlns:local=»clr-namespace:ChildWindowsDemo»
        mc:Ignorable=»d»
        Title=»{Binding TitleSizeToContent=»WidthAndHeight»>
    <Grid>
        <ContentPresenter Content=»{Binding }» />
    </Grid>
</Window>

В коде у него ничего не добавляем. Именно в этом окне будут показываться все дочерние ViewModel. Его состояние можно привязать к модели, например, здесь показано как привязать заголовок, но точно так же можно Visability или другие свойства (для свойств типа Visability можно через конвертор, а в можели хранить bool).
2. Добавляем класс базового ViewModel:

public class ViewModelBase : DependencyObject

{
    ///
    ///Окно в котором показывается текущий ViewModel
    ///

    private ChildWindow _wnd = null;

    ///
    /// Заголовок окна
    ///
    public string Title
    {
        get { return (string)GetValue(TitleProperty); }
        set { SetValue(TitleProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Title.  This enables animation, styling, binding, etc…
    public static readonly DependencyPropertyTitleProperty =
        DependencyProperty.Register(«Title», typeof(string), typeof(ViewModelBase), new PropertyMetadata(«»));       

    ///
    /// Методы вызываемый окном при закрытии
    ///
    protected virtual void Closed()
    {

    }

    ///
    /// Методы вызываемый для закрытия окна связанного с ViewModel
    ///
    public bool Close()
    {
        var result = false;
        if (_wnd != null)
        {
            _wnd.Close();
            _wnd = null;
            result = true;               
        }
        return result;
    }

    ///
    /// Метод показа ViewModel в окне
    ///
    /// viewModel«>
    protected void Show(ViewModelBase viewModel)
    {
        viewModel._wnd = new ChildWindow();
        viewModel._wnd.DataContext = viewModel;
        viewModel._wnd.Closed += (sender, e) => Closed();
        viewModel._wnd.Show();
    }
}
Потомки этого класса могут передавать произвольного потомка этого класса в метод Show, чтобы показать его в отдельном окне.
3. Создаем View для демо, т.к. у нас всегда показывается в окне из пункта 1, то View делаем на основе UserControl:

<UserControl x:Class=»ChildWindowsDemo.View.DemoView»

             xmlns=»http://schemas.microsoft.com/winfx/2006/xaml/presentation»
             xmlns:x=»http://schemas.microsoft.com/winfx/2006/xaml»
             xmlns:mc=»http://schemas.openxmlformats.org/markup-compatibility/2006″
             xmlns:d=»http://schemas.microsoft.com/expression/blend/2008″
             xmlns:local=»clr-namespace:ChildWindowsDemo.View»
             mc:Ignorable=»d»
             d:DesignHeight=»300″ d:DesignWidth=»300″>
    <StackPanelWidth=»200″>
        <DatePicker SelectedDate=»{Binding Date}» />
        <Button Command=»{BindingCloseCommand}»>Закрыть</Button>
    </StackPanel>
</UserControl>
Наш дочерний ViewModel позволяет вводить дату и содержит кнопку для закрытия окна.
4. Демонстрационный ViewModel, потомок нашего ViewModelBase:

class DemoViewModel : ViewModelBase

{
    public DateTime Date
    {
        get { return (DateTime)GetValue(DateProperty); }
        set { SetValue(DateProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Date.  This enables animation, styling, binding, etc…
    public static readonly DependencyPropertyDateProperty =
        DependencyProperty.Register(«Date», typeof(DateTime), typeof(DemoViewModel), new PropertyMetadata(null));

    public ICommandCloseCommand
    {
        get { return (ICommand)GetValue(CloseCommandProperty); }
        set { SetValue(CloseCommandProperty, value); }
    }

    // Using a DependencyProperty as the backing store for CloseCommand.  This enables animation, styling, binding, etc…
    public static readonly DependencyPropertyCloseCommandProperty =
        DependencyProperty.Register(«CloseCommand», typeof(ICommand), typeof(DemoViewModel), new PropertyMetadata(null));

    public DemoViewModel()
    {
        CloseCommand = new SimpleCommand(() => Close());
    }
}

5. Главный ViewModel тоже является потомком BaseViewModel, в нем реализуем показ дочернего окна вызовом метода Show:

class MainViewModel : ViewModelBase

{
    public ICommandCreateChildCommand { get; set; }

    public MainViewModel()
    {
        CreateChildCommand = new SimpleCommand(CreateChild);
    }

    private void CreateChild()
    {
        var child = new DemoViewModel()
        {
            Title = «Дочернее окно«,
            Date = DateTime.Now
        };
        Show(child);
    }
}

6. Ну и магия, в ресурсах приложения создаем связку между View и ViewModel:
<Application.Resources>
    <DataTemplateDataType=»{x:Type viewmodel:DemoViewModel}»>
        <view:DemoViewHorizontalAlignment=»Stretch» />
    </DataTemplate>
</Application.Resources>

Все, можно запускать наше приложение. Клики по кнопке на главной форме показывают дочерние окна, ну а клик на кнопке в дочернем окне закрывает дочернее окно:

Полный код примера можно скачать здесь.