Пример будет максимально упрощен, но основные идеи постараюсь показать.
1. Создаем пустой WPF проект. В него добавляем класс окно вот с такой разметкой:
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 Title}"SizeToContent="WidthAndHeight">
<Grid>
<ContentPresenter Content="{Binding }" />
</Grid>
</Window>
В коде у него ничего не добавляем. Именно в этом окне будут показываться все дочерние ViewModel. Его состояние можно привязать к модели, например, здесь показано как привязать заголовок, но точно так же можно Visability или другие свойства (для свойств типа Visability можно через конвертор, а в можели хранить bool).
2. Добавляем класс базового ViewModel:
///
///Окно в котором показывается текущий ViewModel
///
///
/// Заголовок окна
///
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:
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:
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:
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>
Все, можно запускать наше приложение. Клики по кнопке на главной форме показывают дочерние окна, ну а клик на кнопке в дочернем окне закрывает дочернее окно:
Полный код примера можно скачать здесь.