Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
course_(Windows&Web).docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
4.68 Mб
Скачать

Определение моделей представления

Для возможности разрешения моделей представления через IoC контейнер, безусловно, в первую очередь необходимо зарегистрировать их в качестве экспорта в MEF:

1: [Export]

2: [PartCreationPolicy(CreationPolicy.NonShared)]

3: public class MainViewModel

4: {

5: //

6: }

Обратите внимание на атрибут PartCreationPolicy: он указывает на то, что данный экспорт необходимо создавать заново при удовлетворении каждой новое зависимости, т.е. он не является синглетоном.

Для разрешения модели в контексте текущего IoC контейнера представления достаточно воспользоваться определенным выше интерфейсом ServiceLocator: так, для получения модели представления MainViewModel необходимо выполнить код:

1: var mainViewModel =

2: ServiceLocator.Current.GetInstance<MainViewModel>();

В данном случае можно возразить, что с точки зрения языка корректно создать экземпляр модели представления посредством оператора new, не привлекая к работе IoC контейнер. Однако, хоть такая операция и не произведет ошибок компиляции, код будет не совсем верен: у созданной через конструктор по умолчанию модели представления не будут удовлетворены зависимости (импорты), так как IoC контейнер о ней будет попросту не знать. Также использование IoC контейнера для создания моделей представления открывает возможность использования элементов аспектно-ориентированного программирования, о чем будет сказано несколько позже.

Определение представлений

Следующим этапом является регистрация представлений и декларативная привязка их к соответствующим моделям представления.

Для абстрагирования от конкретной реализации представления в соответствии с принципами инверсии управления необходимо определить общий интерфейс:

1: public interface IView

2: {

3: object DataContext { set; }

4: }

Представления будут реализовывать данный интерфейс, в качестве ключа экспорта указывая свое имя:

1: [Export("MainView", typeof(IView))]

2: [PartCreationPolicy(CreationPolicy.NonShared)]

3: public partial class MainView : IView

4: {

5: public MainView()

6: {

7: InitializeComponent();

8: }

9: }

Сопоставление модели представления и представления

Теперь, когда MEF имеет информацию как о представлении, так и о модели представления, следует реализовать логику сопоставления, которая на основании соглашении об именовании определяет представление, соответствующее переданной модели представления.

Обычной практикой является добавление суффикса ViewModel моделям представления и суффикса View представлениям. Таким образом, имея имя типа модели представления, достаточно заменить в нем ViewModel на View, чтобы получить имя представления.

1: var view = ServiceLocator.Current.GetInstance<IView>(

2: viewModel.GetType().Name.Replace("ViewModel", "View"));

На практике часто возникает ситуация, в которой целое семейство моделей представления, объединенное в одну иерархию, соответствует единственному представлению. Например, модели представления по редактированию доменных объектов UserEditViewModel, RoleEditViewModel будут иметь общего предка EditViewModel, и обеим моделям представления должно быть сопоставлено одно представление EditView, содержащее элемент управления DataForm, предоставляющий обобщенный интерфейс редактирования объекта на основе отражения типов.

Простым решением будет создание 2 наследников для представления EditView, UserEditView и RoleEditView, не содержащих никакой логики и служащих для соблюдения соглашения о наименовании. Однако более разумно включить в логику поиска соответствия обход по всему дереву наследования, до тех пор, пока не будет найдено соответствие:

1: IView view;

2: Type viewModelType = viewModel.GetType();

3:

4: do

5: {

6: view = ServiceLocator.Current.GetInstance<IView>(

7: viewModelType.Name.Replace("ViewModel", "View"));

8:

9: if (view != null)

10: {

11: break;

12: }

13:

14: viewModelType = viewModelType.BaseType;

15: } while (viewModelType != null);

Для возможности повторного использования данная логика добавляется в наследника класса ContentControl, который по переданной через зависимое свойство ViewModel модели представления находит соответствующее представление и устанавливает её своим содержимым:

1: /// <summary>

2: /// Presents a View corresponding to the View Model

3: /// </summary>

4: public class ViewModelPresenter : ContentControl

5: {

6: /// <summary>

7: /// Initializes a new instance

8: /// </summary>

9: public ViewModelPresenter()

10: {

11: HorizontalContentAlignment = HorizontalAlignment.Stretch;

12: VerticalContentAlignment = VerticalAlignment.Stretch;

13: }

14:

15: // Dependency properties

16: public static readonly DependencyProperty ViewModelProperty

17: = DependencyProperty.Register(

18: "ViewModel", typeof(object), typeof(ViewModelPresenter),

19: new PropertyMetadata(null, OnViewModelPropertyChanged));

20:

21: // Private fields

22: private Type _oldViewType;

23:

24: /// <summary>

25: /// View Model to be presented

26: /// </summary>

27: public object ViewModel

28: {

29: get { return GetValue(ViewModelProperty); }

30: set { SetValue(ViewModelProperty, value); }

31: }

32:

33: private static void OnViewModelPropertyChanged(

34: DependencyObject changedObject,

35: DependencyPropertyChangedEventArgs args)

36: {

37: var contentControl = (ViewModelPresenter)changedObject;

38: contentControl.RefreshContentPresenter();

39: }

40:

41: private void RefreshContentPresenter()

42: {

43: if (ViewModel == null)

44: {

45: Content = null;

46: _oldViewType = null;

47:

48: return;

49: }

50:

51: IView view;

52: Type viewModelType = ViewModel.GetType();

53:

54: do

55: {

56: view = ServiceLocator.Current.GetInstance<IView>(

57: viewModelType.Name.Replace("ViewModel", "View"));

58:

59: if (view != null)

60: {

61: break;

62: }

63:

64: viewModelType = viewModelType.BaseType;

65: } while (viewModelType != null);

66:

67: if (view != null)

68: {

69: Type viewType = view.GetType();

70:

71: if (viewType == _oldViewType && Content is IView)

72: {

73: ((IView)Content).DataContext = ViewModel;

73: }

74: else

75: {

76: view.DataContext = ViewModel;

77: Content = view;

78: _oldViewType = viewType;

79: }

80: }

81: else

82: {

83: Content = "View hasn't been found";

84: _oldViewType = null;

85: }

86: }

87: }

В данном элементе управления также реализовано простейшее кеширование: если представление не изменилось, а изменилась только модель представления, то создания нового объекта не произойдет, и новая модель представления будет назначена контекстом представления взамен старой.

Далее, в если в XAML разметке необходимо отобразить представление, соответствующее определенной модели представления, достаточно вставить элемент управления ViewModelPresenter и передать ему в зависимое свойство данную модель представления. Внутри ViewModelPresenter будет отображено представление, найденное по описанному выше алгоритму.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]