|
Теперь, когда 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: }
Дата добавления: 2015-08-13; просмотров: 52 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Понятие IChildViewModelManager | | | Реализация ChildViewModelBase |