| Украина |
Особенности отображения диалоговых окон в WPF и Silverlight версиях приложения
Реализация ChildViewModelBase
После реализации всех необходимых менеджеров можно перейти непосредственно к ChildViewModelBase. Этот класс будет предоставлять своим наследникам базовый функционал, позволяющий им работать как моделям представления дочернего окна.
В класс также добавлены некоторые члены, которые пригодятся при его использовании:
- Команда CloseCommand позволяет быстро привязывать элементы управления, закрывающие окно (такие, как кнопка "Отмена" или "Закрыть");
- Метод Show сделан виртуальным, чтобы спецификации дочерних окон могли при необходимости переопределить его и произвести необходимую инициализацию при показе модели представления.
- Защищенный виртуальный метод OnClosing позволяет наследнику при помощи аргумента CancelEventArgs отменить закрытие окна. Это полезно, например, при необходимости отобразить диалог подтверждения закрытия, и не закрывать модель представления, если пользователь передумал.
- При помощи защищенного виртуального метода OnClosed классы, находящиеся ниже в иерархии наследования, могут обработать событие закрытия модели представления, например, очистив выделенные при создании или в методе Show ресурсы.
1: /// <summary>
2: /// Base class for View Model supporting closing
3: /// </summary>
4: public abstract class ChildViewModelBase : IChildViewModel
5: {
6: /// <summary>
7: /// Shows whether a View Model has been closed
8: /// </summary>
9: public virtual bool IsClosed { get; private set; }
10:
11: /// <summary>
12: /// View Model's title
13: /// </summary>
14: public virtual string Title
15: {
16: get { return "Безымянный"; }
17: }
18:
19: #region Commands
20:
21: private ICommand _closeCommand;
22:
23: /// <summary>
24: /// Command closing the tab
25: /// </summary>
26: public ICommand CloseCommand
27: {
28: get
29: {
30: return _closeCommand ??
31: (_closeCommand = new ActionCommand(Close));
32: }
33: }
34:
35: #endregion
36:
37: #region Injected properties
38:
39: [Import]
40: public virtual ICloseableViewModelPresenter<IChildViewModel>
41: ViewModelPresenter { get; set; }
42:
43: #endregion
44:
45: /// <summary>
46: /// Event raised when the View Model is closed
47: /// </summary>
48: public event EventHandler Closed;
49:
50: /// <summary>
51: /// Closes View Model
52: /// </summary>
53: public void Close()
54: {
55: if (IsClosed)
56: {
57: return;
58: }
59:
60: var cancelEventArgs = new CancelEventArgs();
61: OnClosing(cancelEventArgs);
62:
63: if (cancelEventArgs.Cancel)
64: {
65: return;
66: }
67:
68: OnClosed();
69: }
70:
71: /// <summary>
72: /// Registers View Model to be shown
73: /// </summary>
74: public virtual void Show()
75: {
76: ViewModelPresenter.ShowViewModel(this);
77: }
78:
79: /// <summary>
80: /// Processes View Model closing attempt
81: /// </summary>
82: protected virtual void OnClosing(CancelEventArgs e)
83: {
84: //
85: }
86:
87: /// <summary>
88: /// Processes View Model close
89: /// </summary>
90: protected virtual void OnClosed()
91: {
92: IsClosed = true;
93:
94: if (Closed != null)
95: {
96: Closed(this, EventArgs.Empty);
97: }
98: }
99: }Реализация ModalChildViewModelBase
Полезным расширением реализованной модели представления ChildViewModelBase является модель представления модального диалога ModalChildViewModelBase. Модальный диалог позволяет в унифицированной форме сообщить результат выполнения вызвавшему его объекту при помощи свойства ModalResult.
Как и во всех стандартных оконных средах выполнения, установка значения ModalResult автоматически закрывает модель представления.
1: /// <summary>
2: /// Base class of Child View's View Model providing its modal
3: /// result
4: /// </summary>
5: public abstract class ModalChildViewModelBase :
6: ChildViewModelBase, IModalChildViewModel
7: {
8: // Private fields
9: private bool? _modalResult;
10:
11: /// <summary>
12: /// Gets or sets modal result
13: /// </summary>
14: /// <remarks>
15: /// Setting <see cref="ModalResult" /> causes View Model
16: /// to close
17: /// </remarks>
18: public virtual bool? ModalResult
19: {
20: get { return _modalResult; }
21: protected set
22: {
23: if (value == null)
24: {
25: throw new InvalidOperationException(
26: "Unable to set ModalResult to null");
27: }
28:
29: _modalResult = value;
30: Close();
31: }
32: }
33: }Реализация MessageViewModel
Развивая идею ModalChildViewModelBase, целесообразно реализовать одну из наиболее часто используемых моделей представления – модель представления сообщения MessageViewModel.
Данная модель представления отображает окно с сообщением, заданное набором параметров: тип сообщения (информационное, предупреждение, ошибка), доступные для нажатия кнопки (ОK, ОК/Отмена, Повторить/Отмена, или любой заданный пользователем набор), заголовок окна, и непосредственно само сообщение.
Команда ButtonCommand является здесь универсальной для любой заданной пользователем кнопки: в качестве аргумента ей передается индекс кнопки, который затем записывается в MessageResult; также при этом устанавливается свойство ModalResult, что приводит к закрытию окна.
1: /// <summary>
2: /// View model of modal message
3: /// </summary>
4: [Export(typeof(MessageViewModel))]
5: [PartCreationPolicy(CreationPolicy.NonShared)]
6: [ExportMetadata(AopExtensions.AspectMetadata,
7: Aspects.NotifyPropertyChanged)]
8: public class MessageViewModel : ModalChildViewModelBase
9: {
10: /// <summary>
11: /// Initializes a new instance
12: /// </summary>
13: protected MessageViewModel()
14: {
15: //
16: }
17:
18: // Predefined button sets
19: public static readonly IList<string> OkButtons
20: = new[] { "OК" };
21: public static readonly IList<string> OkCancelButtons
22: = new[] { "OК", "Отмена" };
23: public static readonly IList<string> RetryCancelButtons
24: = new[] { "Повторить", "Отмена" };
25:
26: // Private fields
27: private IList<string> _buttons = OkButtons;
28:
29: /// <summary>
30: /// List of available buttons
31: /// </summary>
32: public virtual IList<string> Buttons
33: {
34: get { return _buttons; }
35: set { _buttons = value; }
36: }
37:
38: /// <summary>
39: /// Message execution result
40: /// </summary>
41: public virtual int MessageResult { get; protected set; }
42:
43: /// <summary>
44: /// Body of the message
45: /// </summary>
46:
47: public virtual string Message { get; set; }
48:
49: /// <summary>
50: /// Type of the message
51: /// </summary>
52: public virtual MessageType MessageType { get; set; }
53:
54: /// <summary>
55: /// Message's caption
56: /// </summary>
57: [Localizable(true)]
58: public virtual string Caption { get; set; }
59:
60: /// <summary>
61: /// View model's title
62: /// </summary>
63: public override string Title
64: {
65: get { return Caption; }
66: }
67:
68: #region Commands
69:
70: private ICommand _buttonCommand;
71:
72: public ICommand ButtonCommand
73: {
74: get
75: {
76: return _buttonCommand ??
77: (_buttonCommand = new ActionCommand(
78: param => SetMessageResult(param.ToString())));
79: }
80: }
81:
82: #endregion
83:
84: /// <summary>
85: /// Processes View Model closing attempt
86: /// </summary>
87: protected override void OnClosing(CancelEventArgs e)
88: {
89: base.OnClosing(e);
90: e.Cancel = ModalResult == null;
91: }
92:
93: /// <summary>
94: /// Sets specified button's index as message result
95: /// </summary>
96: private void SetMessageResult(string selectedButton)
97: {
98: if (IsClosed)
99: {
100: return;
101: }
102:
103: Debug.Assert(Buttons.Contains(selectedButton));
104: MessageResult = Buttons.IndexOf(selectedButton);
105: ModalResult = true;
106: }
107: }Как видно из кода, особенностью представленной модели представления является то, что переопределяемый метод OnClosing не позволяет закрыть окно, не установив MessageResult – таким образом вызывающему коду гарантируется детерминированный результат отображения сообщения.
Соответствующее представление в соответствии с шаблоном MVVM не содержит сложной логики и лишь визуализирует состояние, представленное в модели представления:
1: <UserControl x:Class="TestProject.MessageView"
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:controlsHelpers
5: ="clr-namespace:PassportOffice.View.Controls">
6:
7: <Grid>
8: <Grid.Resources>
9: <controlsHelpers:CommandReference
10: x:Key="ButtonCommandReference"
11: Command="{Binding ButtonCommand}" />
12: <controlsHelpers:MessageTypeConverter
13: x:Key="MessageTypeConverter" />
14: </Grid.Resources>
15:
16: <Grid.ColumnDefinitions>
17: <ColumnDefinition Width="Auto" />
18: <ColumnDefinition Width="Auto" />
19: </Grid.ColumnDefinitions>
20: <Grid.RowDefinitions>
21: <RowDefinition Height="*" />
22: <RowDefinition Height="Auto" />
23: </Grid.RowDefinitions>
24:
25: <Image x:Name="ImageMessage" Height="48"
26: UriSource="{Binding MessageType,
27: Converter={StaticResource MessageTypeConverter}}"
28: Grid.Column="0" Grid.Row="0" />
29:
30: <TextBlock Margin="3" MinWidth="300" MaxWidth="600"
31: Text="{Binding Message}" VerticalAlignment="Center"
32: TextWrapping="Wrap" Grid.Column="1" Grid.Row="0" />
33:
34: <ItemsControl x:Name="ButtonsControl"
35: HorizontalAlignment="Center"
36: ItemsSource="{Binding Buttons}" Margin="0,5,0,0"
37: Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1">
38: <ItemsControl.ItemsPanel>
39: <ItemsPanelTemplate>
40: <StackPanel Orientation="Horizontal" />
41: </ItemsPanelTemplate>
42: </ItemsControl.ItemsPanel>
43: <ItemsControl.ItemTemplate>
44: <DataTemplate>
45: <Button Content="{Binding}"
46: Command="{StaticResource ButtonCommandReference}"
47: CommandParameter="{Binding}" />
48: </DataTemplate>
49: </ItemsControl.ItemTemplate>
50: </ItemsControl>
51: </Grid>
52: </UserControl>Краткие итоги
В рамках данной лекции была реализована логика отображения диалоговых окон на уровне моделей представления. Для этого добавлены набор слушателей слоя представления, которые, не нарушая принципы MVVM, обеспечивают отображение регистрируемых моделей представления диалоговых окон. Также разработанные базовые классы в качестве примера были расширены наследниками, специализирующимися в отображении сообщений различного характера.