Благодаря шаблонам данных (data templates) в WPF очень удобно реализовывать привязку моделей представления к самим представлениям (паттерн Model-View-ViewModel).
Достаточно добавить в ресурс приложения или окна строку наподобие следующей
<DataTemplate DataType="{x:Type vm:MyViewModel}">
<views:MyView />
DataTemplate>
и тогда для представления всех экземпляров MyViewModel будет использоваться MyView, если явно не будет задан другой шаблон.
Пример 1
MyViewModelObject – экземпляр класса MyViewModel или производного от него.
В качестве содержимого элемента ContentControl будет подставлен экземпляр класса MyView, в качестве данных которому будет привязан сам MyViewModelObject.
Пример 2
MyViewModelCollection – коллекция объектов MyViewModel. Для отображения каждого элемента в списке ListBox будет использоваться представление MyView.
Всё замечательно, если представления ограничиваются элементами пользовательского интерфейса, размещаемыми внутри одного окна или страницы. Но если нужно сделать представление в виде отдельного окна, например, в виде модального диалога, то тут начинаются проблемы.
Хотя WPF разрешает использовать внутри шаблонов любые типы, но при попытке обратиться к шаблону, содержащему в себе окно из того же ContentControl возникнет исключение, связанное с тем, что запрещено внедрение одного окна внутрь другого.
Для того, что бы всё же отобразить представление в виде модального диалогового окна, я написал вспомогательный класс DialogViewManager.
public static class DialogViewManager
{
public static bool? ShowDialog(object viewModel)
{
if (viewModel == null)
{
throw new ArgumentNullException("viewModel");
}
var template = Application.Current.FindResource(new DataTemplateKey(viewModel.GetType())) as DataTemplate;
if (template != null)
{
if (Application.Current.MainWindow == null)
{
return ShowDialog(viewModel, template);
}
return
(bool?)
Application.Current.MainWindow.Dispatcher.Invoke(DispatcherPriority.Normal,
new ShowDialogDelegate(ShowDialog), viewModel,
template);
}
return null;
}
private static bool? ShowDialog(object viewModel, DataTemplate template)
{
DependencyObject content = template.LoadContent();
var window = content as Window;
if (window == null)
{
window = new Window
{
ShowInTaskbar = false,
WindowStartupLocation = WindowStartupLocation.CenterScreen
};
if (content == null)
{
var textBlock = new TextBlock
{
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Foreground = SystemColors.HighlightTextBrush,
Text =
string.Format("Отсутствует шаблон данных для {0}",
viewModel.GetType().FullName)
};
window.Content = textBlock;
}
else
{
window.Content = content;
}
}
//window.Parent = Application.Current.MainWindow;
window.DataContext = viewModel;
return window.ShowDialog();
}
#region Nested type: ShowDialogDelegate
private delegate bool? ShowDialogDelegate(object viewModel, DataTemplate template);
#endregion
}
Пример использования
public class MySecondViewModel
{
...
MyViewModel MyData {get; set;}
...
public void EditMyData()
{
//для MyData будет найдено представление по шаблону данных, находящемуся в ресурсах приложения,
// и это представление будет отображено в виде модального диалогового окна
DialogViewManager.ShowDialog(MyData);
}
}
Таким образом, благодаря созданному классу DialogViewManager возможно:
- отображение представлений в виде диалоговых окон по модели представления без использования явных ссылок на тип представления;
- использование всё тех же шаблонов данных для привязки моделей преставления к представлениям, представляющих собой окна.
Комментариев нет:
Отправить комментарий