Использование шаблонов данных для модальных диалоговых окон

Благодаря шаблонам данных (data templates) в WPF очень удобно реализовывать привязку моделей представления к самим представлениям (паттерн Model-View-ViewModel).

Достаточно добавить в ресурс приложения или окна строку наподобие следующей

<DataTemplate DataType="{x:Type vm:MyViewModel}">

<views:MyView />

DataTemplate>

и тогда для представления всех экземпляров MyViewModel будет использоваться MyView, если явно не будет задан другой шаблон.

Пример 1

Content="{Binding MyViewModelObject}"/>

MyViewModelObject – экземпляр класса MyViewModel или производного от него.

В качестве содержимого элемента ContentControl будет подставлен экземпляр класса MyView, в качестве данных которому будет привязан сам MyViewModelObject.

Пример 2

ItemsSource="{Binding MyViewModelCollection}"/>

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 возможно:

  • отображение представлений в виде диалоговых окон по модели представления без использования явных ссылок на тип представления;
  • использование всё тех же шаблонов данных для привязки моделей преставления к представлениям, представляющих собой окна.

Комментариев нет:

Отправить комментарий