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