46
Разработка WPF приложений в стиле ViewModel First Денис Цветцих АстроСофт http://www.astrosoft.ru/ 11-я конференция .NET разработчиков 31 октября 2015 dotnetconf.ru

Разработка WPF приложений в стиле ViewModel First

Embed Size (px)

Citation preview

Page 1: Разработка WPF приложений в стиле ViewModel First

Разработка WPF приложений в стиле ViewModel First

Денис ЦветцихАстроСофт

http://www.astrosoft.ru/

11-я конференция .NET разработчиков31 октября 2015dotnetconf.ru

Page 2: Разработка WPF приложений в стиле ViewModel First

2

Почему это актуально• WPF все ещё жив • MVVM – тема множества докладов

и статей • Множество разных реализаций• от MVVM.Light• до Prism с мануалом на 250 страниц

Page 3: Разработка WPF приложений в стиле ViewModel First

3

В чем проблема?Много разных реализаций MVVM• Непонятно, чем они отличаются• Непонятно, какую из них

использовать и в каких случаях

Page 4: Разработка WPF приложений в стиле ViewModel First

4

Почему я здесь• Накоплен интересный опыт

участия в WPF проектах• Есть опыт, связанный с

фреймворками:• Использование сторонних• Доработка сторонних• Изобретение своего MVVM

фреймворка

Page 5: Разработка WPF приложений в стиле ViewModel First

5

Опрос • Кто собирается написать WPF

приложение?• Кто при этом использовал какие-

нибудь MVVM библиотеки/фреймворки?

• Кто их вас изобрел свой собственный MVVM велосипед фреймворк?

Page 6: Разработка WPF приложений в стиле ViewModel First

6

О чем мы поговорим?• Что такое MVVM?• Какими бывают подходы к его

реализации?• Как отличаются организация

CompositeUI и дочерних окон в разных подходах?

• Какой подход лучше использовать?

• Где найти реализацию этого подхода?

• Рекомендации на основе нашего опыта

Page 7: Разработка WPF приложений в стиле ViewModel First

7

Model-View-ViewModel

Page 8: Разработка WPF приложений в стиле ViewModel First

8

Что со всем этим делать?

Как решать типовые задачи?• CompositeUI (MasterDetail форма)• Навигация (дочерние окна)

Page 9: Разработка WPF приложений в стиле ViewModel First

9

Нам нужен MVVM фреймворк!

Бывает 2 типов:• ViewFirst • ViewModelFirst

Это не разные реализации паттерна MVVM Это разные подходы к решению типовых

задач с использованием MVVM

Page 10: Разработка WPF приложений в стиле ViewModel First

COMPOSITE UI

Page 11: Разработка WPF приложений в стиле ViewModel First

11

Master Detail

Page 12: Разработка WPF приложений в стиле ViewModel First

12

ViewFirst (Prism)Отображение нового региона:1) Создать View2) Создать ViewModel для View3) View.DataContext = ViewModel4) Инициализировать ViewModel

Page 13: Разработка WPF приложений в стиле ViewModel First

13

MainWinow

<Grid>

<Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions>

<view:UserListView Grid.Column="0" />

<view:UserDetailsView Grid.Column="1" />

</Grid>

Page 14: Разработка WPF приложений в стиле ViewModel First

14

UserListView<Grid>

<TextBlock Grid.Row="0" Text="User list" FontWeight="Bold" />

<ListBox Grid.Row="1" x:Name="UserListBox" SelectedItem="{Binding SelectedUser, Mode=TwoWay}" ItemsSource="{Binding Users}">

<ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding FirstName}" /> </DataTemplate> </ListBox.ItemTemplate>

</ListBox></Grid>

public UserListView(){ DataContext = new UserListViewModel();}

Page 15: Разработка WPF приложений в стиле ViewModel First

15

UserDetailsView<StackPanel Orientation="Vertical">

<TextBlock Text="User details" FontWeight="Bold" />

<TextBlock Text="{Binding User.FirstName}" /> <TextBlock Text="{Binding User.LastName}" /></StackPanel>

public UserDetailsView(){ DataContext = new UserDetailsViewModel();}

Page 16: Разработка WPF приложений в стиле ViewModel First

16

UserListViewModelpublic class UserListViewModel : ViewModel{ private User _selectedUser;

public IEnumerable<User> Users { get; } // инициализация

public User SelectedUser { get { return _selectedUser; } set { _selectedUser = value; OnPropertyChanged(); } }}

Page 17: Разработка WPF приложений в стиле ViewModel First

17

UserDetailsViewModelpublic class UserDetailsViewModel : ViewModel{

private User _user;

public User User { get { return _user; } set { _user = value; OnPropertyChanged(); } }}

Page 18: Разработка WPF приложений в стиле ViewModel First

18

ВопросКак сделать так, чтобы

UserListViewModel.SelectedUser синхронизировалось с UserDetailsViewModel.User?

Ответ в стиле ViewFirst – MessageBus

Page 19: Разработка WPF приложений в стиле ViewModel First

19

MessageBus

Page 20: Разработка WPF приложений в стиле ViewModel First

20

MessageBuspublic class MessageBus{ public static MessageBus Instance = new MessageBus();

public event EventHandler<UserChangedEventArgs> SelectedUserChanged;

public void OnSelectedUserChanged(User user) { SelectedUserChanged?.Invoke(this, new UserChangedEventArgs(user)); }}

public class UserChangedEventArgs : EventArgs{ public UserChangedEventArgs(User user) { User = user; } public User User { get; }}

Page 21: Разработка WPF приложений в стиле ViewModel First

21

UserListViewModel public class UserListViewModel : ViewModel{ public User SelectedUser { get { return _selectedUser; } set { _selectedUser = value; OnPropertyChanged();

MessageBus.Instance.OnSelectedUserChanged(value); } }}

Page 22: Разработка WPF приложений в стиле ViewModel First

22

UserDetailsViewModel public class UserDetailsViewModel : ViewModel{ public UserDetailsViewModel() { MessageBus.Instance.SelectedUserChanged +=

(s, e) => User = e.User; } }

Page 23: Разработка WPF приложений в стиле ViewModel First

23

Недостатки 1) Используется MessageBus,

предназначенный для интеграции систем

2) На широковещательное событие может подписаться любой объект

3) Поведение системы становится запутанным и неочевидным

Page 24: Разработка WPF приложений в стиле ViewModel First

24

ViewModelFirst (энтузиасты)

Отображение нового региона:1) Создать ViewModel2) Инициализировать ViewModel3) Создать View для ViewModel4) View.DataContext = ViewModel

Page 25: Разработка WPF приложений в стиле ViewModel First

25

Чистим CodeBehindpublic UserListView(){ DataContext = new UserListViewModel();}public UserDetailsView(){ DataContext = new UserDetailsViewModel();}

Page 26: Разработка WPF приложений в стиле ViewModel First

26

Убираем MessageBuspublic class UserListViewModel : ViewModel{ public User SelectedUser { get { return _selectedUser; } set { _selectedUser = value; OnPropertyChanged();

MessageBus.Instance.OnSelectedUserChanged(value); } }}

Page 27: Разработка WPF приложений в стиле ViewModel First

27

Убираем MessageBuspublic class UserDetailsViewModel : ViewModel{ public UserDetailsViewModel() { MessageBus.Instance.SelectedUserChanged +=

(s, e) => User = e.User; } }

Page 28: Разработка WPF приложений в стиле ViewModel First

28

ВопросКак сделать так, чтобы

UserListViewModel.SelectedUser синхронизировалось с UserDetailsViewModel.User?

Ответ в стиле ViewModelFirst – нам нужна родительская ViewModel

Page 29: Разработка WPF приложений в стиле ViewModel First

29

MainWindowViewModelpublic class MainWindowViewModel : ViewModel{ public UserDetailsViewModel UserDetailsViewModel { get; private

set; } public UserListViewModel UserListViewModel { get; private set; }

public void Initialize() { UserListViewModel.PropertyChanged += (s, e) => { if (e.PropertyName == "SelectedUser") UserDetailsViewModel.User =

UserListViewModel.SelectedUser; }; }}

Page 30: Разработка WPF приложений в стиле ViewModel First

30

MainWindow<Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions>

<views:UserListView DataContext="{Binding UserListViewModel}" Grid.Column="0" />

<views:UserDetailsView DataContext="{Binding UserDetailsViewModel}" Grid.Column="1" /></Grid>

Page 31: Разработка WPF приложений в стиле ViewModel First

31

ViewFirst vs ViewModelFirst

ViewFirst ViewModelFitstCompositeUI да даВзаимодействие ViewModel

MessageBus Родетельская ViewModel

Page 32: Разработка WPF приложений в стиле ViewModel First

НАВИГАЦИЯ

Page 33: Разработка WPF приложений в стиле ViewModel First

33

ViewFirst: показать новый элемент

Дочернее окно, новый таб:

Navigation.Show<ViewModel>(Value);илиNavigation.Show("View", Value);

Аналогично вебу: http://address.ru/?arg=value

ViewFirst предлагает в WPF организовать навигацию аналогично веб-приложению

Page 34: Разработка WPF приложений в стиле ViewModel First

34

ViewModelFirst: показать новый элемент

var vm = Navigation.Get<ViewModel>();

vm.Arg = Value;vm.Show();

Аналогично окну WPF:var wnd = new Window();wnd.Arg1 = Value1;wnd.Show();

Page 35: Разработка WPF приложений в стиле ViewModel First

35

ViewFirst vs ViewModelFirst

ViewFirst ViewModelFirstСоздать View Создать ViewModelСоздать ViewModel Инициализировать ViewModel

Инициализировать ViewModel Создать ViewView.DataContext = ViewModel View.DataContext = ViewModel

Page 36: Разработка WPF приложений в стиле ViewModel First

36

Дочернее окно

Page 37: Разработка WPF приложений в стиле ViewModel First

37

Отображение дочернего окна

private void OnShowDetails(User user){ var detailsVM =

Factory.Resolve<UserDetailsWindowViewModel>();

detailsVM.User = user; detailsVM.Closed += (s, e) => { /* обработка e.DialogResult */ }; detailsVM.Show();}

Page 38: Разработка WPF приложений в стиле ViewModel First

38

ChildViewModelpublic abstract class ChildViewModel : ViewModel, IChildViewModel{ [Dependency] public IChildViewModelManager ChildViewModelManager { private get; set; }

public bool IsClosed { get; private set; } // уже закрыли или нет? protected void Close() { if (IsClosed) throw new InvalidOperationException(“closed");

IsClosed = true;

ChildViewModelManager.Close(this);}

public void Show() { ChildViewModelManager.Show(this); }}

Page 39: Разработка WPF приложений в стиле ViewModel First

39

ChildViewModelManagerpublic class ChildViewModelManager : IChildViewModelManager{

// открытые окна private readonly Dictionary<Type, Window> _openedWindows

= new Dictionary<Type, Window>();

// по типу ViewModel возвращает View [Dependency] public IViewTypeResolver ViewTypeResolver

{ private get; set; }

}

Page 40: Разработка WPF приложений в стиле ViewModel First

40

ChildViewModelManager: Show

private void Show(IChildViewModel viewModel){ // получить тип окна, которое будем открывать var windowType = ViewTypeResolver.

ResolveViewType(viewModel.GetType());

// создать экземпляр окна var window =

(Window)Activator.CreateInstance(windowType);

// запомнить, какое окно открываем _openedWindows.Add(viewModel.GetType(), window);

window.DataContext = viewModel; // показать окно window.Show();

}

Page 41: Разработка WPF приложений в стиле ViewModel First

41

ChildViewModelManager: Close

public void Close(IChildViewModel viewModel){ // какое окно закрываем var window = _openedWindows[viewModel.GetType()];

// убираем из списка открытых _openedWindows.Remove(viewModel.GetType());

// закрываем

Application.Current.Dispatcher.BeginInvoke(new Action(() => window.Close()), null);

}

Page 42: Разработка WPF приложений в стиле ViewModel First

42

ChildWindowpublic abstract class ChildWindow : Window{ protected override void OnClosing(CancelEventArgs e) { base.OnClosing(e);

var viewModel = (ChildViewModel)DataContext;

// если ViewModel на находится в состоянии «Закрыто» if (!viewModel.IsClosed) { e.Cancel = true; // не закрываем окно

// запрашиваем изменение состояния ViewModel viewModel.Close();

} }}

Page 43: Разработка WPF приложений в стиле ViewModel First

43

Особенности ViewModelFirst

Достоинства• Позволяет реализовать CompositeUI• Не требует реализации MessageBus• Взаимодействие ViewModel более

очевидное• Нет MessageBus – нет его использования

не по назначению• Позволяет удобно реализовать

поддержку дочерних оконНедостатки• Не имеет вендорской поддержки

Page 44: Разработка WPF приложений в стиле ViewModel First

44

Наш рецепт• ViewModelFirst • Свой велосипед• Mugen MVVM Toolkit

• IoC контейнер• ReactiveUI (ограничено)• ReactiveCommand• ObservableForProperty

• Отдельная сборка для ViewModel

Page 45: Разработка WPF приложений в стиле ViewModel First

45

Материалы по ViewModelFirst

Материалы доклада на GitHub:https://

github.com/denis-tsv/ViewFirst-vs-ViewModelFirst

Курс «Методология синхронной разработки приложений в Microsoft Visual Studio 2010»

www.intuit.ru/studies/courses/2322/622/info

Mugen MVVM Toolkithttp://habrahabr.ru/post/236745/

Page 46: Разработка WPF приложений в стиле ViewModel First

46

Спасибо за внимание

Денис ЦветцихАстроСофт

[email protected]