Форум программистов, компьютерный форум, киберфорум
Наши страницы
bodynar
Войти
Регистрация
Восстановить пароль
Оценить эту запись

Открытие новых диалогов

Запись от bodynar размещена 20.05.2016 в 16:47
Обновил(-а) bodynar 20.05.2016 в 16:48
Метки c#, mvvm, wpf

Не так давно, в ходе изучения MVVM-паттерна, наткнулся на такой вопрос: "А как все-таки открывать новые диалоговые окна (View) из ViewModel?".
Небольшая справка
MVVM - Model-View-ViewModel - паттерн проектирования приложений. Очень хорошо реализуется в WPF благодаря привязкам (Bindings).
Модель (Model) - Модель данных, Например:
Кликните здесь для просмотра всего текста
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Student 
{
    string FirstName;
    string SecondName;
    int Age;
    double mark;
    
    //...
    
    func void SetMark(double mark) {
        self.mark = mark;
    }
    //...
}
.

Представление (View) — это графический интерфейс (окно, кнопки и т.п.). Представление является подписчиком на событие изменения значений свойств или команд, предоставляемых Моделью представления. В случае, если в Модели представления изменилось какое-либо свойство, то она оповещает всех подписчиков об этом, и Представление, в свою очередь, запрашивает обновленное значение свойства из Модели представления. В случае, если пользователь воздействует на какой-либо элемент интерфейса, Представление вызывает соответствующую команду, предоставленную Моделью представления.

Модель представления (ViewModel) является, с одной стороны, абстракцией Представления, а с другой, предоставляет обёртку данных из Модели, которые подлежат связыванию. То есть, она содержит Модель, которая преобразована к Представлению, а также содержит в себе команды, которыми может пользоваться Представление, чтобы влиять на Модель.
(с) Вики


По принципу MVVM ViewModel не знает о существовании View, как и View не знает о ViewModel. Этакая обособленность. По этому делать что-то вроде:
C#
1
new MainWindow1().ShowDialog();
внутри ViewModel будет нарушением использования паттерна.

Через некоторое время путешествия по интернету в поисках хорошего работоспособного и паттерно-чистого решения наткнулся на одного человека, который посоветовал использовать некий сервис, который будет выполнять всю грязную работу. Ранее уже выкладывал сей способ в одной из тем форума в разделе WPF.
C#
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
public interface IDialogService
    {
        bool? ShowDialog(ViewModelBase vm);
 
        MessageBoxResult MessageBox(string messageBoxText, string caption);
 
        MessageBoxResult MessageBox(string messageBoxText, string caption, MessageBoxButton button);
 
        MessageBoxResult MessageBox(string messageBoxText, string caption, MessageBoxButton button,
            MessageBoxImage iconImage);
    }
 
public class DialogService : IDialogService
    {
        private static DialogService _default;
        private readonly Stack<Window> _parents = new Stack<Window>();
 
        private readonly IDictionary<Type, Type> _windows = new Dictionary<Type, Type>();
 
        public DialogService(Window mainWindow)
        {
            _parents.Push(mainWindow);
        }
 
        public DialogService()
        {
        }
 
        public static DialogService Default => _default ?? (_default = new DialogService());
 
        public bool? ShowDialog(ViewModelBase vm)
        {
            var vmType = vm.GetType();
            if (!_windows.ContainsKey(vmType))
                throw new ArgumentException("no registerd window for type " + vmType.Name);
 
            var viewType = _windows[vmType];
 
            var view = (Window) Activator.CreateInstance(viewType);
            view.DataContext = vm;
            view.Owner = _parents.Peek();
 
            view.Closed += WindowClosed;
            _parents.Push(view);
 
            return view.ShowDialog();
        }
 
        public MessageBoxResult MessageBox(string messageBoxText, string caption)
        {
            return WPF.MessageBox.Show(_parents.Peek(), messageBoxText, caption);
        }
 
        public MessageBoxResult MessageBox(string messageBoxText, string caption, MessageBoxButton button)
        {
            return WPF.MessageBox.Show(_parents.Peek(), messageBoxText, caption, button);
        }
 
        public MessageBoxResult MessageBox(string messageBoxText, string caption, MessageBoxButton button,
            MessageBoxImage iconImage)
        {
            return WPF.MessageBox.Show(_parents.Peek(), messageBoxText, caption, button, iconImage);
        }
 
        public void SetMainParent(Window window)
        {
            _parents.Clear();
            _parents.Push(window);
        }
 
        public void Register<T, TK>()
            where T : ViewModelBase
            where TK : Window
        {
            _windows.Add(typeof (T), typeof (TK));
        }
 
        private void WindowClosed(object sender, EventArgs e)
        {
            var view = (Window) sender;
            view.Closed -= WindowClosed;
            _parents.Pop();
 
            var vm = view.DataContext as IDisposable;
            vm?.Dispose();
        }
    }
Насколько можно видеть - данный сервис позволяет произвести связывание ViewModel и View, который будет отображать данные из первого и отображение View по заданному ViewModel.

То есть - у нас имеется место в приложении, где нам необходимо отобразить следующее окно с какой-либо информацией. Для этого мы создаем в нужном месте ViewModel, в котором будут содержаться требуемые данные и "просим сервис отобразить данный ViewModel".

Таким образом, определив в App.xaml обработчик события Startup и в App.xaml.cs в обработчике этого пропишем что-то вроде:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
var _dialogService = DialogService.Default;
 
            _dialogService.Register<FirstViewModel, FirstView>();
 
            _dialogService.Register<SecondViewModel, SecondView>();
 
            _dialogService.Register<ThirdViewModel, ThirdView>();
 
            var v = new SecondView();
            var vm = new SecondViewModel(_dialogService);
            v.DataContext = vm;
            _dialogService.SetMainParent(v);
            v.ShowDialog();
И, допустим, в SecondViewModel у нас имеется метод, "вызывающий другое окно":
C#
1
2
var fvm = new FirstViewModel(editedperson);
_dialogService.ShowDialog(fvm));
Таким образом у нас имеется более-менее чистый способ решения вопроса об открытии других окон в рамках паттерна MVVM.

PS. Как можно было заметить - DialogService реализует паттерн синглтон. Как к нему относится - дело ваше. Человек, посоветовавший данный способ не слишком позитивно отзывался о данном паттерне, посему рекомендовал "протаскивать" ранее созданный _dialogService по всем ViewModel (с помощью передачи его в качестве аргумента в конструктор ViewModel)
Размещено в Без категории
Просмотров 391 Комментарии 0
Всего комментариев 0
Комментарии
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru