Опубликован: 13.12.2011 | Уровень: для всех | Доступ: платный
Лекция 12:

Особенности отображения диалоговых окон в WPF и Silverlight версиях приложения

Аннотация: В лекции рассматривается реализация логики отображения и управления окнами в соответствии с шаблоном MVVM для максимального повторного использования кода между Silverlight и WPF.

Цель лекции: показать читателям на примере фрагментов кода механизм отображения диалоговых окон в многослойном кроссплатформенном Silverlight/WPF MVVM приложении.

Понятие ICloseableViewModel и IChildViewModel

В предыдущей лекции было введено понятие базовой модели представления, и определяющего её интерфейса, IViewModel. Получившееся простейшее приложение состояло из главной модели представления, MainViewModel, и соответствующего ей представления, MainView.

Сложно представить себе программу, обходящуюся одним главным окном. В любом MVVM приложении необходимо не только взаимодействовать с сервисами на уровне главной модели представления, но и отображать дополнительную информацию в диалоговых окнах. В соответствии с шаблоном MVVM вся логика приложения должна находиться на уровне модели представления, а значит должна быть возможность из произвольной модели представления инициировать открытие дочерней модели представления.

Логично ввести понятие закрываемой модели представления, которая может быть отображена вызовом метода Show() и закрыта вызовом метода Close():

  1: /// <summary>
  2: /// Interface of a View Model that can be closed
  3: /// </summary>
  4: public interface ICloseableViewModel : IViewModel
  5: {
  6:     /// <summary>
  7:     /// Shows whether View Model has been closed
  8:     /// </summary>
  9:     bool IsClosed { get; }
 10:
 11:     /// <summary>
 12:     /// Event raised when the View Model is closed
 13:     /// </summary>
 14:     event EventHandler Closed;
 15:
 16:     /// <summary>
 17:     /// Closes View Model
 18:     /// </summary>
 19:     void Close();
 20:
 21:     /// <summary>
 22:     /// Registers View Model to be shown
 23:     /// </summary>
 24:     void Show();
 25: }

Флаг IsClosed сообщает о том, была ли модель представления уже закрыта, а событие Close инициируется как раз в момент закрытия.

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

  1: /// <summary>
  2: /// Interface of a View Model that has a title
  3: /// </summary>
  4: public interface IEntitledViewModel : IViewModel
  5: {
  6:     /// <summary>
  7:     /// View Model's title
  8:     /// </summary>
  9:     string Title { get; }
 10: }

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

  1: /// <summary>
  2: /// Interface of Child View's View Model
  3: /// </summary>
  4: public interface IChildViewModel
  5:    : ICloseableViewModel, IEntitledViewModel
  6: {
  7:     //
  8: }

Понятие IChildViewModelManager

Очевидно, что модели представления дочерних окон не могут работать сами по себе, и необходим механизм, отображающий их в настоящие дочерние окна целевой платформы (Silverlight или WPF).

Для этих целей необходимо ввести менеджер дочерних моделей представления: он ведет учет видимых в настоящий момент моделей представления и сообщает о них всем внешним слушателям, подписанным на изменения коллекции ViewModels:

  1: /// <summary>
  2: /// Interface of class managing closeable View Models of
  3: /// the specified type
  4: /// </summary>
  5: public interface IChildViewModelManager
  6:    : ICloseableViewModelPresenter<IChildViewModel>
  7: {
  8:     /// <summary>
  9:     /// Collection of managed View Models
 10:     /// </summary>
 11:     ReadOnlyObservableCollection<IChildViewModel>
 12:        ViewModels { get; }
 13: }

Здесь используется понятие презентера закрываемой модели представления, который принимает к регистрации видимые модели представления:

  1: /// <summary>
  2: /// Interface of closeable View Model presenter
  3: /// </summary>
  4: [InheritedExport]
  5: public interface ICloseableViewModelPresenter<in TViewModelBase>
  6:     where TViewModelBase : ICloseableViewModel
  7: {
  8:     /// <summary>
  9:     /// Shows <paramref name="viewModel" />
 10:     /// </summary>
 11:     void ShowViewModel(TViewModelBase viewModel);
 12: }

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

  1: /// <summary>
  2: /// Manages closeable View Models of the specified type
  3: /// </summary>
  4: internal class ChildViewModelManager
  5:    : IChildViewModelManager
  6: {
  7:     // Private fields
  8:     private readonly ObservableCollection<IChildViewModel>
  9:        _viewModelsInternal =
 10:            new DispatchObservableCollection<IChildViewModel>();
 11:     private ReadOnlyObservableCollection<IChildViewModel>
 12:        _viewModels;
 13:
 14:     /// <summary>
 15:     /// Collection of managed View Models
 16:     /// </summary>
 17:     public ReadOnlyObservableCollection<IChildViewModel>
 18:        ViewModels
 19:     {
 20:         get
 21:         {
 22:             return _viewModels ?? (_viewModels =
 23:                new ReadOnlyObservableCollection<IChildViewModel>(
 24:                   _viewModelsInternal));
 25:         }
 26:     }
 27:
 28:     #region ICloseableViewModelPresenter<TViewModelBase> Members
 29:
 30:     void ICloseableViewModelPresenter<IChildViewModel>
 31:        .ShowViewModel(IChildViewModel viewModel)
 32:     {
 33:         ShowViewModelCore(viewModel);
 34:     }
 35:
 36:     #endregion
 37:
 38:     /// <summary>
 39:     /// Closes <paramref name="viewModel" />
 40:     /// </summary>
 41:     protected virtual void CloseViewModelCore
 42:        (IChildViewModel viewModel)
 43:     {
 44:         viewModel.Closed -= OnViewModelClosed;
 45:
 46:         Debug.Assert(_viewModelsInternal.Contains(viewModel));
 47:         _viewModelsInternal.Remove(viewModel);
 48:     }
 49:
 50:     /// <summary>
 51:     /// Shows <paramref name="viewModel" />,
 52:     /// adding it to collection
 53:     /// </summary>
 54:     protected virtual void ShowViewModelCore
 55:        (IChildViewModel viewModel)
 56:     {
 57:         Debug.Assert(!viewModel.IsClosed);
 58:         viewModel.Closed += OnViewModelClosed;
 59:
 60:         Debug.Assert(!_viewModelsInternal.Contains(viewModel));
 61:         _viewModelsInternal.Add(viewModel);
 62:     }
 63:
 64:     private void OnViewModelClosed(object sender, EventArgs e)
 65:     {
 66:         Debug.Assert(sender is IChildViewModel);
 67:         CloseViewModelCore((IChildViewModel)sender);
 68:     }
 69: }
Анисимов Михаил
Анисимов Михаил
Украина
Наталия Шаститко
Наталия Шаститко
Украина, Днепропетровск, Днепропетровский Гуманитарный Университет, 2014