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

Понятие 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: }

ChildViewManager

Теперь, когда IChildViewModelManager ведет учет моделей представления дочерних окон, можно перейти к следующему понятию – менеджеру дочерних представлений. Он прослушивает коллекцию ViewModel на предмет изменения по событию CollectionChanged интерфейса INotifyCollectionChanged: если в коллекцию добавлена новая модель представления, то необходимо создать для неё дочернее окно и сохранить соответствие в словаре; затем, при удалении модели представления из коллекции достаточно найти дочернее окно по словарю, закрыть его и удалить запись о соответствии.

Следует заметить, что так как объекты пользовательского интерфейса как правило не позволяют взаимодействовать с собой из потока, отличного от GUI потока, а вызовы слоя моделей представления могут происходить в любом контексте, в том числе в потоке из пула потоков (наиболее часто в нем выполняются обратные методы вызова асинхронных операций Windows Communication Foundation сервисов), то необходимо явно отправлять события изменений коллекции ViewModels на Dispatcher GUI потока посредством DispatcherSynchronizationContext.

1: #if SILVERLIGHT

2: using ChildViewType = System.Windows.Controls.ChildWindow;

3: #else

4: using ChildViewType = System.Windows.Window;

5: #endif

6:

7: public class ChildViewManager

8: {

9: [ImportingConstructor]

10: public ChildViewManager

11: (IEnumerable<IChildViewModel> viewModelCollection)

12: {

13: OnViewModelCollectionChanged(viewModelCollection,

14: new NotifyCollectionChangedEventArgs

15: (NotifyCollectionChangedAction.Reset));

16:

17: var notifiable = viewModelCollection

18: as INotifyCollectionChanged;

19:

20: if (notifiable != null)

21: {

22: notifiable.CollectionChanged += (sender, e)

23: => DispatcherSynchronizationContext.Post(arg =>

24: OnViewModelCollectionChanged(sender, e), null);

25: }

26: }

27:

28: // Private readonly fields

29: protected static readonly DispatcherSynchronizationContext

30: DispatcherSynchronizationContext =

31: new DispatcherSynchronizationContext(

32: #if SILVERLIGHT

33: Deployment.Current.Dispatcher

34: #else

35: Application.Current.Dispatcher

36: #endif

37: );

38:

39: // Private fields

40: private readonly IDictionary<IChildViewModel, ChildViewType>

41: _childViews =

42: new Dictionary<IChildViewModel, ChildViewType>();

43:

44: /// <summary>

45: /// Closes all managed <see cref="ChildViewPresenter" />

46: /// </summary>

47: protected virtual void CloseAllViews()

48: {

49: foreach (KeyValuePair<IChildViewModel, ChildViewType>

50: pair in _childViews)

51: {

52: CloseView(pair.Key);

53: }

54: }

55:

56: /// <summary>

57: /// Closes specified <see cref="ChildViewPresenter" />

58: /// </summary>

59: protected virtual void CloseView

60: (IChildViewModel childViewModel)

61: {

62: Debug.Assert(_childViews.ContainsKey(childViewModel));

63:

64: ChildViewType childView = _childViews[childViewModel];

65: _childViews.Remove(childViewModel);

66: childView.Close();

67: }

68:

69: /// <summary>

70: /// Shows specified <see cref="ChildViewPresenter" />

71: /// </summary>

72: protected virtual void ShowView

73: (IChildViewModel childViewModel)

74: {

75: ChildViewType childWindow = new ChildViewPresenter

76: { DataContext = childViewModel };

77: _childViews.Add(childViewModel, childWindow);

78: childWindow.Show();

79: }

80:

81: private void OnViewModelCollectionChanged(object sender,

82: NotifyCollectionChangedEventArgs e)

83: {

84: switch (e.Action)

85: {

86: case NotifyCollectionChangedAction.Add:

87: foreach (IChildViewModel viewModel in e.NewItems)

88: {

89: ShowView(viewModel);

90: }

91: break;

92:

93: case NotifyCollectionChangedAction.Remove:

94: foreach (IChildViewModel viewModel in e.OldItems)

95: {

96: CloseView(viewModel);

97: }

98: break;

99:

100: case NotifyCollectionChangedAction.Reset:

101: CloseAllViews();

102:

103: foreach (IChildViewModel viewModel

104: in (IEnumerable)sender)

105: {

106: ShowView(viewModel);

107: }

108: break;

109:

110: default:

111: throw new ArgumentOutOfRangeException

112: ("e.Action is out of range", (Exception)null);

113: }

114: }

115: }

ChildViewPresenter – это окно Window в случае WPF и ChildWindow в случае Silverlight. Данные элементы управления содержат в себе ViewModelPresenter, который уже в свою очередь определяет, какое представление необходимо отобразить в дочернем окне.

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

Для правильной работы приложения при попытке пользователя закрыть дочернее окно системными средствами необходимо отменить это закрытие в событии OnClosing и инициировать корректное закрытие модели представления, которое в свою очередь затем приведет к закрытию окна. С точки зрения пользователя поведение приложения будет корректным, и при этом архитектура приложения не будет загрязнена дополнительной логикой обработки специального случая.

Реализация ChildViewPresenter для WPF:

1: <Window x:Class="TestProject.ChildViewPresenter"

2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

4: xmlns:viewModelMapping="clr-namespace:TestProject"

5: SizeToContent="WidthAndHeight"

6: Title="{Binding Title}"

7: WindowStartupLocation="CenterScreen">

8:

9: <viewModelMapping:ViewModelPresenter

10: x:Name="ViewModelPresenter" ViewModel="{Binding}" />

11: </Window>

Для Silverlight:

1: <controls:ChildWindow x:Class="TestProject.ChildViewPresenter"

2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

4: xmlns:controls=

5: "http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"

6: xmlns:ViewModelMapping="clr-namespace:TestProject"

7: Title="{Binding Title}">

8:

9: <ViewModelMapping:ViewModelPresenter ViewModel="{Binding}" />

10: </controls:ChildWindow>

Код данных элементов управления одинаковый как в Silverlight, так и в WPF:

1: public partial class ChildViewPresenter

2: {

3: /// <summary>

4: /// Initializes a new instance

5: /// </summary>

6: public ChildViewPresenter()

7: {

8: InitializeComponent();

9: }

10:

11: /// <summary>

12: /// Underlying View Model

13: /// </summary>

14: private ICloseableViewModel ViewModel

15: {

16: get

17: {

18: Debug.Assert(DataContext == null

19: || DataContext is ICloseableViewModel);

20: return (ICloseableViewModel)DataContext;

21: }

22: }

23:

24: /// <summary>

25: /// Processes window closing

26: /// </summary>

27: protected override void OnClosing(CancelEventArgs e)

28: {

29: base.OnClosing(e);

30:

31: Debug.Assert(ViewModel != null);

32:

33: if (!ViewModel.IsClosed)

34: {

35: e.Cancel = true;

36: ViewModel.Close();

37: }

38: }

39: }

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