Как написать XAML-приложение без Message Bus

170
Как разрабатывать UI XAML приложений (Prism) и веб-приложений (Angular2, React) без Message Bus Денис Цветцих One Systems DotNext Msk 9 декабря 2016 http://dotnext-mos cow.ru /

Transcript of Как написать XAML-приложение без Message Bus

Page 1: Как написать XAML-приложение без Message Bus

Как разрабатывать UI XAML приложений (Prism) и веб-приложений (Angular2, React)

без Message Bus

Денис ЦветцихOne Systems

DotNext Msk9 декабря 2016http://dotnext-moscow.ru/

Page 2: Как написать XAML-приложение без Message Bus

2

Message Bus – паттерн для интеграции

MessageBus

Application 1 Application 2

Application 3 Application 4

Page 3: Как написать XAML-приложение без Message Bus

3

Чем плох MessageBus на UI• Взаимодействие объектов неявное• Сложно дебажить• В больших проектах MessageHell

• Простор для костылей• Messages вместо Events • Messages вместо Bindings

Page 4: Как написать XAML-приложение без Message Bus

4

Кто виноват?

Page 5: Как написать XAML-приложение без Message Bus

5

Виноват Composite Application Blocks

public class MasterController : Controller{ [EventPublication("GlobalEvent")] public event EventHandler GlobalEvent;}

public class DetailController : Controller{ [EventSubscription("GlobalEvent")] public void OnGlobalEvent(object sender, EventArgs e) { }}

Page 6: Как написать XAML-приложение без Message Bus

6

Виноват Composite Application Blocks

public class MasterController : Controller{ [EventPublication("GlobalEvent")] public event EventHandler GlobalEvent;}

public class DetailController : Controller{ [EventSubscription("GlobalEvent")] public void OnGlobalEvent(object sender, EventArgs e) { }}

Page 7: Как написать XAML-приложение без Message Bus

7

Виноват Composite Application Blocks

public class MasterController : Controller{ [EventPublication("GlobalEvent")] public event EventHandler GlobalEvent;}

public class DetailController : Controller{ [EventSubscription("GlobalEvent")] public void OnGlobalEvent(object sender, EventArgs e) { }}

Page 8: Как написать XAML-приложение без Message Bus

8

Виноват Composite Application Blocks

public class MasterController : Controller{ [EventPublication("GlobalEvent")] public event EventHandler GlobalEvent;}

public class DetailController : Controller{ [EventSubscription("GlobalEvent")] public void OnGlobalEvent(object sender, EventArgs e) { }}

Page 9: Как написать XAML-приложение без Message Bus

9

Виноват Composite Application Blocks

public class MasterController : Controller{ [EventPublication("GlobalEvent")] public event EventHandler GlobalEvent;}

public class DetailController : Controller{ [EventSubscription("GlobalEvent")] public void OnGlobalEvent(object sender, EventArgs e) { }}

Page 10: Как написать XAML-приложение без Message Bus

10

Виноват Composite Application Blocks

public class MasterController : Controller{ [EventPublication("GlobalEvent")] public event EventHandler GlobalEvent;}

public class DetailController : Controller{ [EventSubscription("GlobalEvent")] public void OnGlobalEvent(object sender, EventArgs e) { }}

Page 11: Как написать XAML-приложение без Message Bus

11

Виноват Composite Application Blocks

public class MasterController : Controller{ [EventPublication("GlobalEvent")] public event EventHandler GlobalEvent;}

public class DetailController : Controller{ [EventSubscription("GlobalEvent")] public void OnGlobalEvent(object sender, EventArgs e) { }}

Page 12: Как написать XAML-приложение без Message Bus

12

CAB UI: скрытая угроза• Нет класса EventAggregator• Но• Любой контроллер может подписаться на• Любое событие• Любого другого контроллера• Неявно (при помощи атрибута)

Page 13: Как написать XAML-приложение без Message Bus

13

Message Bus есть во всех MVVM• Prism – по инерции от Composite App Blocks• Caliburn.Micro – для поддержки View-First и

ViewModel-First• MVVM от энтузиастов• Не знают как по-другому• Делают как у всех

Page 14: Как написать XAML-приложение без Message Bus

14

О чем мы поговорим?• Типовые UI-задачи для MessageBus• UI Composition• Смена контекста • Костыли

• Как без него обойтись• MVVM-фреймворки для WPF и UWP без

MessageBus• Что c MessageBus на JS (Angular2, React)

Page 15: Как написать XAML-приложение без Message Bus

15

Опрос• Кто использовал MessageBus на UI?• И считает что без него обойтись нельзя• И считает, что без него использовать можно

• Кто не использовал MessageBus на UI?

Page 16: Как написать XAML-приложение без Message Bus

16

Примеры с INPC кодомpublic class DetailViewModel : BindableBase{ public User User { get { return _user; } set { SetProperty(ref _user, value); } }

}

Page 17: Как написать XAML-приложение без Message Bus

17

Реализацию INPC держим в умеpublic class DetailViewModel{ public User User { get; set; }}

Page 18: Как написать XAML-приложение без Message Bus

18

Задача 1: UI Composition

Page 19: Как написать XAML-приложение без Message Bus

19

View-Switching Navigation (Prism)

Page 20: Как написать XAML-приложение без Message Bus

20

ShellView<ShellView>

<ContentControl regions:RegionManager.RegionName="LeftRegion“ />

<ContentControl regions:RegionManager.RegionName="MainRegion“ />

</ShellView>

Page 21: Как написать XAML-приложение без Message Bus

21

View-Switching Navigation (View-First)

RegionManager.RequestNavigate("MainRegion", EmailUri);

Заменяем содержимое региона во время выполнения

Page 22: Как написать XAML-приложение без Message Bus

22

UI Composition: MasterDetail формаShellView

MasterRegion DetailRegion

Page 23: Как написать XAML-приложение без Message Bus

23

ShellView (ViewFirst)<ShellView>

<ContentControl regions:RegionManager.RegionName=“MasterRegion“ />

<ContentControl regions:RegionManager.RegionName=“DetailRegion“ />

</ShellView>

Page 24: Как написать XAML-приложение без Message Bus

24

View-First: по вьюхе создаем ViewModel

public partial class MasterView{ [Import] public MasterViewModel ViewModel { set { DataContext = value; } }}

Page 25: Как написать XAML-приложение без Message Bus

25

Главный вопрос

Как DetailViewModel узнает об изменении выделенного элемента в MasterViewModel?

Page 26: Как написать XAML-приложение без Message Bus

26

MessageBus: обмен сообщениями между ViewModel

ShellView

MasterRegion DetailRegion

MessageBus

Page 27: Как написать XAML-приложение без Message Bus

27

MasterViewModelpublic class MasterViewModel{ public User SelectedUser { get { return _selectedUser; } set { SetProperty(ref _selectedUser, value);

EventAggregator.GetEvent<UserChangedMessage>() .Publish(_selectedUser);

} }

public ObservableCollection<User> Users { get; set; } }

Page 28: Как написать XAML-приложение без Message Bus

28

MasterViewModelpublic class MasterViewModel{ public User SelectedUser { get { return _selectedUser; } set { SetProperty(ref _selectedUser, value);

EventAggregator.GetEvent<UserChangedMessage>() .Publish(_selectedUser);

} }

public ObservableCollection<User> Users { get; set; } }

Page 29: Как написать XAML-приложение без Message Bus

29

MasterViewModelpublic class MasterViewModel{ public User SelectedUser { get { return _selectedUser; } set { SetProperty(ref _selectedUser, value);

EventAggregator.GetEvent<UserChangedMessage>() .Publish(_selectedUser);

} }

public ObservableCollection<User> Users { get; set; } }

Page 30: Как написать XAML-приложение без Message Bus

30

MasterViewModelpublic class MasterViewModel{ public User SelectedUser { get { return _selectedUser; } set { SetProperty(ref _selectedUser, value);

EventAggregator.GetEvent<UserChangedMessage>() .Publish(_selectedUser);

} }

public ObservableCollection<User> Users { get; set; } }

Page 31: Как написать XAML-приложение без Message Bus

31

DetailViewModelpublic class DetailViewModel : BindableBase{ public DetailViewModel(IEventAggregator eventAggregator) { eventAggregator.GetEvent<UserChangedMessage>() .Subscribe(OnUserChanged); }

public User User { get; set; }

public void OnUserChanged(User user) { User = user; }

}

Page 32: Как написать XAML-приложение без Message Bus

32

DetailViewModelpublic class DetailViewModel : BindableBase{ public DetailViewModel(IEventAggregator eventAggregator) { eventAggregator.GetEvent<UserChangedMessage>() .Subscribe(OnUserChanged); }

public User User { get; set; }

public void OnUserChanged(User user) { User = user; }

}

Page 33: Как написать XAML-приложение без Message Bus

33

DetailViewModelpublic class DetailViewModel : BindableBase{ public DetailViewModel(IEventAggregator eventAggregator) { eventAggregator.GetEvent<UserChangedMessage>() .Subscribe(OnUserChanged); }

public User User { get; set; }

public void OnUserChanged(User user) { User = user; }

}

Page 34: Как написать XAML-приложение без Message Bus

34

DetailViewModelpublic class DetailViewModel : BindableBase{ public DetailViewModel(IEventAggregator eventAggregator) { eventAggregator.GetEvent<UserChangedMessage>() .Subscribe(OnUserChanged); }

public User User { get; set; }

public void OnUserChanged(User user) { User = user; }

}

Page 35: Как написать XAML-приложение без Message Bus

35

DetailViewModelpublic class DetailViewModel : BindableBase{ public DetailViewModel(IEventAggregator eventAggregator) { eventAggregator.GetEvent<UserChangedMessage>() .Subscribe(OnUserChanged); }

public User User { get; set; }

public void OnUserChanged(User user) { User = user; }

}

Page 36: Как написать XAML-приложение без Message Bus

36

Это работает, но …Чем плохо:• Для UserChangedMessage только один получатель• MessageBus – из пушки по воробьям• Но без него никак Чего хочется:• Биндинг MasterVM.SelectedUser на DetailVM.User

Page 37: Как написать XAML-приложение без Message Bus

37

Как обойтись: ViewModel-FirstShellRegion

MasterRegion DetailRegion

Page 38: Как написать XAML-приложение без Message Bus

38

View-First vs ViewModel-FirstView-First ViewModel-First

Первой создается ViewMessageBus Родительская

ViewModel

ViewModel

Не нуженНеобходим

Взаимодействие ViewModelMessageBus

Page 39: Как написать XAML-приложение без Message Bus

39

MasterViewModelpublic class MasterViewModel{ public User SelectedUser { get; set; }

public List<User> Users { get; set; }}

Page 40: Как написать XAML-приложение без Message Bus

40

DetailViewModelpublic class DetailViewModel { public User User { get; set; }}

Page 41: Как написать XAML-приложение без Message Bus

41

ShellViewModel (ReactiveUI)public class ShellViewModel{ public MasterViewModel MasterViewModel { get; } public DetailViewModel DetailViewModel { get; }

public ShellViewModel() { MasterViewModel

.WhenAny(m => m.SelectedUser, user => user.Value) .BindTo(DetailViewModel, d => d.User); }

}

Page 42: Как написать XAML-приложение без Message Bus

42

ShellViewModel (ReactiveUI)public class ShellViewModel{ public MasterViewModel MasterViewModel { get; } public DetailViewModel DetailViewModel { get; }

public ShellViewModel() { MasterViewModel

.WhenAny(m => m.SelectedUser, user => user.Value) .BindTo(DetailViewModel, d => d.User); }

}

Page 43: Как написать XAML-приложение без Message Bus

43

ShellViewModel (ReactiveUI)public class ShellViewModel{ public MasterViewModel MasterViewModel { get; } public DetailViewModel DetailViewModel { get; }

public ShellViewModel() { MasterViewModel

.WhenAny(m => m.SelectedUser, user => user.Value) .BindTo(DetailViewModel, d => d.User); }

}

Page 44: Как написать XAML-приложение без Message Bus

44

ShellViewModel (ReactiveUI)public class ShellViewModel{ public MasterViewModel MasterViewModel { get; } public DetailViewModel DetailViewModel { get; }

public ShellViewModel() { MasterViewModel

.WhenAny(m => m.SelectedUser, user => user.Value) .BindTo(DetailViewModel, d => d.User); }

}

Page 45: Как написать XAML-приложение без Message Bus

45

ShellView (Caliburn)<ShellView>

<ContentControl x:Name=“MasterViewModel" />

<ContentControl x:Name="DetailViewModel" />

</ShellView>

Page 46: Как написать XAML-приложение без Message Bus

46

Такой разный Mediator• MessageBus – универсальный Mediator• Родительская ViewModel – частный медиатор

Page 47: Как написать XAML-приложение без Message Bus

47

Подвох от PrismPrism предлагает RegionManager (ViewFirt) для:• View-Switching Navigation• UI Composition

Page 48: Как написать XAML-приложение без Message Bus

48

Что делать?• Разделить задачи ViewSwitching Navigation и UI

Composition• RegionManager – только для навигации• Не использовать RegionManager для UI

Composition

Page 49: Как написать XAML-приложение без Message Bus

49

UI Composition vs View-Switching Navigation

UI Composition View-Switching Navigation

Делим форму на части Да ДаЗачем делим Части проще и повторно

используются

Меняем регионы на рантайме

Нет

Выделяем статическую часть (меню) и динамическую (регион)Да

Нельзя (менять подход к навигации)

Можно (скрепя сердце)Не использовать MessageBus

Page 50: Как написать XAML-приложение без Message Bus

50

ShellView: Было<ShellView>

<ContentControl regions:RegionManager.RegionName=“MasterRegion“ />

<ContentControl regions:RegionManager.RegionName=“DetailRegion“ />

</ShellView>

Page 51: Как написать XAML-приложение без Message Bus

51

ShellView: MasterDetail без RegionManager

<ShellView>

<ContentControl regions:RegionManager.RegionName=“MasterDetailRegion“ />

</ShellView>

Page 52: Как написать XAML-приложение без Message Bus

52

MasterDetailViewModelpublic class MasterDetailViewModel{ public MasterViewModel MasterViewModel { get; } public DetailViewModel DetailViewModel { get; }

public MasterDetailViewModel() { MasterViewModel

.WhenAny(m => m.SelectedUser, user => user.Value) .BindTo(DetailViewModel, d => d.User); }}

Page 53: Как написать XAML-приложение без Message Bus

53

UI Composition без ViewSwitching

<MasterDetailView>

<MasterView DataContext="{Binding MasterViewModel}" />

<DetailView DataContext="{Binding DetailViewModel}" />

</MasterDetailView>

Page 54: Как написать XAML-приложение без Message Bus

54

UI Composition: ViewSwitching ViewModel-Frist (Caliburn)

<Grid>

<ContentControl x:Name=“MasterViewModel" Grid.Column="0" />

<ContentControl x:Name="DetailViewModel" Grid.Column="1" />

</Grid>

Page 55: Как написать XAML-приложение без Message Bus

55

View-Switching в разных MVVMViewModel-First (можно использовать для UI Composition)• Caliburn.Micro• MugenMvvmToolkit View-First (нельзя использовать для UI Composition)• Prism.WPF

• ReactiveUI (UI Composition сделать сложно)

Page 56: Как написать XAML-приложение без Message Bus

56

Не умеют View-Switching• Prism.Windows (UWP)• MvvmLight• MvvmCross

• ReactiveUI (умеет, UI Composition сделать сложно)

Page 57: Как написать XAML-приложение без Message Bus

57

Более сложный MasterDetail

Page 58: Как написать XAML-приложение без Message Bus

58

ShellViewModelpublic class ShellViewModel{ public MasterViewModel MasterViewModel { get; }

public DetailViewModel DetailViewModel { get; }

}

Page 59: Как написать XAML-приложение без Message Bus

59

ViewSwitching для UI Composition (Caliburn)

<Grid>

<ContentControl x:Name=“MasterViewModel" Grid.Column="0" />

<ContentControl x:Name="DetailViewModel" Grid.Column="1" />

</Grid>

Page 60: Как написать XAML-приложение без Message Bus

60

Как сделать ViewSwitching ViewModel-First?

• View-ViewModel маппинг – создание View по ViewModel

• ViewModelPresenter – отображение View

Page 61: Как написать XAML-приложение без Message Bus

61

View-ViewModel маппинг• Конвенция именования

ViewModels.MasterVewModel -> Views.MasterView

• РеестрViewResolver.Register<MasterView, MasterViewModel>();var view = ViewResolver.Resolve(viewModel);

• Метаданные (MEF)[View(typeof(BatchEditViewModel))] //наследник Exportpublic partial class BatchEditView : IView

пример кода №8 в списке полезных ссылок

Page 62: Как написать XAML-приложение без Message Bus

62

Отображение View• AttachedProperty (Prism, Caliburn.Micro)

<ContentControl regions:RegionManager.RegionName=“MainRegion“ />

• Xaml Extension (MugenMvvmToolkit) <ContentPresenter Content="{ViewModelToViewBinding Path=ViewModel}" />

• Наследник ContentControl (ViewModelPresenter)

Page 63: Как написать XAML-приложение без Message Bus

63

ViewModelPresenter: 1 экран кода

public class ViewModelPresenter : ContentControl{ public static readonly DependencyProperty ViewModel …

private void OnViewModelChanged() { var viewTypeResolver = ServiceLocator.Current.GetInstance<IViewTypeResolver>(); var view = viewTypeResolver.ResolveViewType(ViewModel);

view.DataContext = ViewModel; Content = view; }}

Page 64: Как написать XAML-приложение без Message Bus

64

ViewModelPresenter: 1 экран кода

public class ViewModelPresenter : ContentControl{ public static readonly DependencyProperty ViewModel …

private void OnViewModelChanged() { var viewTypeResolver = ServiceLocator.Current.GetInstance<IViewTypeResolver>(); var view = viewTypeResolver.ResolveViewType(ViewModel);

view.DataContext = ViewModel; Content = view; }}

Page 65: Как написать XAML-приложение без Message Bus

65

ViewModelPresenter: 1 экран кода

public class ViewModelPresenter : ContentControl{ public static readonly DependencyProperty ViewModel …

private void OnViewModelChanged() { var viewTypeResolver = ServiceLocator.Current.GetInstance<IViewTypeResolver>(); var view = viewTypeResolver.ResolveViewType(ViewModel);

view.DataContext = ViewModel; Content = view; }}

Page 66: Как написать XAML-приложение без Message Bus

66

ViewModelPresenter: 1 экран кода

public class ViewModelPresenter : ContentControl{ public static readonly DependencyProperty ViewModel …

private void OnViewModelChanged() { var viewTypeResolver = ServiceLocator.Current.GetInstance<IViewTypeResolver>(); var view = viewTypeResolver.ResolveViewType(ViewModel);

view.DataContext = ViewModel; Content = view; }}

Page 67: Как написать XAML-приложение без Message Bus

67

ViewModelPresenter: 1 экран кода

public class ViewModelPresenter : ContentControl{ public static readonly DependencyProperty ViewModel …

private void OnViewModelChanged() { var viewTypeResolver = ServiceLocator.Current.GetInstance<IViewTypeResolver>(); var view = viewTypeResolver.ResolveViewType(ViewModel);

view.DataContext = ViewModel; Content = view; }}

Page 68: Как написать XAML-приложение без Message Bus

68

ViewModelPresenter: 1 экран кода

public class ViewModelPresenter : ContentControl{ public static readonly DependencyProperty ViewModel …

private void OnViewModelChanged() { var viewTypeResolver = ServiceLocator.Current.GetInstance<IViewTypeResolver>(); var view = viewTypeResolver.ResolveViewType(ViewModel);

view.DataContext = ViewModel; Content = view; }}

Page 69: Как написать XAML-приложение без Message Bus

69

ViewModelPresenter: 1 экран кода

public class ViewModelPresenter : ContentControl{ public static readonly DependencyProperty ViewModel …

private void OnViewModelChanged() { var viewTypeResolver = ServiceLocator.Current.GetInstance<IViewTypeResolver>(); var view = viewTypeResolver.ResolveViewType(ViewModel);

view.DataContext = ViewModel; Content = view; }}

Page 70: Как написать XAML-приложение без Message Bus

70

ViewSwitching для UI Composition (Caliburn)

<Grid>

<ContentControl x:Name=“MasterViewModel" Grid.Column="0" />

<ContentControl x:Name="DetailViewModel" Grid.Column="1" />

</Grid>

Page 71: Как написать XAML-приложение без Message Bus

71

ShellView ViewModel-First<Grid> <ViewModelPresenter ViewModel="{Binding MasterViewModel}" Grid.Column="0" />

<ViewModelPresenter ViewModel="{Binding DetailViewModel}" Grid.Column="1" />

</Grid>

Page 72: Как написать XAML-приложение без Message Bus

72

View-Switching• Отдельная задача от Navigation • Табы• MDI

• Отдельная задача от UI Composition • UI Composition не требует менять UI на рантайме

Page 73: Как написать XAML-приложение без Message Bus

73

UI Composition без MessageBus• Разделить задачи View-Switching Navigation и UI Composition• Не использовать ViewSwitching View-First для UI Composition• RegionManager из Prism

• Можно использовать ViewSwitching ViewModel-First для UI Composition • Caliburn.Micro• MugenMvvmToolkit

• UI Composition без ViewSwitching (ViewModel в DataContext для View)• MVVM без View-Switching (Prism.Windows)• Если нужен ViewSwitching для UI Composition

• Отдельный View-ViewModel маппинг • ViewModelPresenter

Page 74: Как написать XAML-приложение без Message Bus

74

Задача 2: Глобальные события• Изменение контекста • Изменение этапов Lifecycle

Page 75: Как написать XAML-приложение без Message Bus

75

Контекст: Log in/ Log out

Page 76: Как написать XAML-приложение без Message Bus

76

Контекст: организация

Page 77: Как написать XAML-приложение без Message Bus

77

Контекст: настройка

Page 78: Как написать XAML-приложение без Message Bus

78

Этапы Lifecycle: Suspend, Resume

Suspended AppRunning AppSuspending

Resuming

Page 79: Как написать XAML-приложение без Message Bus

79

Изменение контекста: Как обойтись попроще

• Частное решение вместо MessageBus• Обычные события вместо широковещательных

Page 80: Как написать XAML-приложение без Message Bus

80

ApplicationStateManagerpublic enum ApplicationState{ LoggingIn, Initializing, Active, Terminating,}

public class ApplicationStateChangedEventArgs : EventArgs{ public ApplicationState OldState { get; set; } public ApplicationState NewState { get; set; }}

Page 81: Как написать XAML-приложение без Message Bus

81

ApplicationStateManagerpublic enum ApplicationState{ LoggingIn, Initializing, Active, Terminating,}

public class ApplicationStateChangedEventArgs : EventArgs{ public ApplicationState OldState { get; set; } public ApplicationState NewState { get; set; }}

Page 82: Как написать XAML-приложение без Message Bus

82

ApplicationStateManagerpublic enum ApplicationState{ LoggingIn, Initializing, Active, Terminating,}

public class ApplicationStateChangedEventArgs : EventArgs{ public ApplicationState OldState { get; set; } public ApplicationState NewState { get; set; }}

Page 83: Как написать XAML-приложение без Message Bus

83

ApplicationStateManagerpublic enum ApplicationState{ LoggingIn, Initializing, Active, Terminating,}

public class ApplicationStateChangedEventArgs : EventArgs{ public ApplicationState OldState { get; set; } public ApplicationState NewState { get; set; }}

Page 84: Как написать XAML-приложение без Message Bus

84

Обычные события вместо широковещательных

public class ApplicationStateManager {

public event EventHandler<ApplicationStateChangedEventArgs> ApplicationStateChanged;

public ApplicationState ApplicationState { get { return _applicationState; } set { var oldValue = _applicationState; _applicationState = value; OnApplicationStateChanged(oldValue, _applicationState); } } }

Page 85: Как написать XAML-приложение без Message Bus

85

Обычные события вместо широковещательных

public class ApplicationStateManager {

public event EventHandler<ApplicationStateChangedEventArgs> ApplicationStateChanged;

public ApplicationState ApplicationState { get { return _applicationState; } set { var oldValue = _applicationState; _applicationState = value; OnApplicationStateChanged(oldValue, _applicationState); } } }

Page 86: Как написать XAML-приложение без Message Bus

86

Обычные события вместо широковещательных

public class ApplicationStateManager {

public event EventHandler<ApplicationStateChangedEventArgs> ApplicationStateChanged;

public ApplicationState ApplicationState { get { return _applicationState; } set { var oldValue = _applicationState; _applicationState = value; OnApplicationStateChanged(oldValue, _applicationState); } } }

Page 87: Как написать XAML-приложение без Message Bus

87

Обычные события вместо широковещательных

public class ApplicationStateManager {

public event EventHandler<ApplicationStateChangedEventArgs> ApplicationStateChanged;

public ApplicationState ApplicationState { get { return _applicationState; } set { var oldValue = _applicationState; _applicationState = value; OnApplicationStateChanged(oldValue, _applicationState); } } }

Page 88: Как написать XAML-приложение без Message Bus

88

ApplicationStateManager vs MessageBus

Частных решений не будет много (у нас - одно)

ApplicationStateManager MessageBus

Подписчик Любой ЛюбойОтправитель ApplicationStateManager Любой

Событие ApplicationStateChanged Любое

Page 89: Как написать XAML-приложение без Message Bus

89

Изменение контекста: Как обойтись лучше

Цепочка вызовов методов

Page 90: Как написать XAML-приложение без Message Bus

90

Пример цепочки вызовов методов

Package Prism.WindowsКласс PrismApplication.csМетод OnSuspending

https://github.com/PrismLibrary/Prism/blob/master/Source/Windows10/Prism.Windows/PrismApplication.cs

Page 91: Как написать XAML-приложение без Message Bus

91

Цепочка вызовов vs частное решение

Цепочка вызовов+ Не требует генерации событий- Нужна продуманная архитектураЧастное решение+ Их не будет много- Хочется заменить частное на универсальное

(MessageBus)

Page 92: Как написать XAML-приложение без Message Bus

92

Костыли – Messages межу уровнями

View

ViewModel

Logic

Page 93: Как написать XAML-приложение без Message Bus

93

Фокус в поле при нажатии Add

Page 94: Как написать XAML-приложение без Message Bus

94

Message между ViewModel и View

MessageBus

DetailViewModel DetailView

UserChanged Focus Focus

Page 95: Как написать XAML-приложение без Message Bus

95

DetailViewModelpublic class DetailViewModel : IHandle<UserChangedMessage>{ public User User { get; set; }

public void Handle(UserChangedMessage message) { User = message.User;

if (User?.IsNew) { EventAggregator.PublishOnUIThread(new FocusMessage()); } }}

Page 96: Как написать XAML-приложение без Message Bus

96

DetailViewModelpublic class DetailViewModel : IHandle<UserChangedMessage>{ public User User { get; set; }

public void Handle(UserChangedMessage message) { User = message.User;

if (User?.IsNew) { EventAggregator.PublishOnUIThread(new FocusMessage()); } }}

Page 97: Как написать XAML-приложение без Message Bus

97

DetailViewModelpublic class DetailViewModel : IHandle<UserChangedMessage>{ public User User { get; set; }

public void Handle(UserChangedMessage message) { User = message.User;

if (User?.IsNew) { EventAggregator.PublishOnUIThread(new FocusMessage()); } }}

Page 98: Как написать XAML-приложение без Message Bus

98

DetailViewpublic partial class DetailView : UserControl, IHandle<FocusMessage>{ public void Handle(FocusMessage message) { FirstNameTextBox.Focus(); }

}

Page 99: Как написать XAML-приложение без Message Bus

99

DetailViewpublic partial class DetailView : UserControl, IHandle<FocusMessage>{ public void Handle(FocusMessage message) { FirstNameTextBox.Focus(); }

}

Page 100: Как написать XAML-приложение без Message Bus

100

Как обойтись – Behavior (или Trigger)

public class FocusNewUserBehavior : Behavior<TextBox>{ public static readonly DependencyProperty User /* код опущен */

private static void OnUserChanged (DependencyObject d, DependencyPropertyChangedEventArgs e) { var behavior = (FocusNewUserBehavior) d;

if (behavior.User?.IsNew) { behavior.AssociatedObject.Focus(); } } }

Page 101: Как написать XAML-приложение без Message Bus

101

Как обойтись – Behavior (или Trigger)

public class FocusNewUserBehavior : Behavior<TextBox>{ public static readonly DependencyProperty User /* код опущен */

private static void OnUserChanged (DependencyObject d, DependencyPropertyChangedEventArgs e) { var behavior = (FocusNewUserBehavior) d;

if (behavior.User?.IsNew) { behavior.AssociatedObject.Focus(); } } }

Page 102: Как написать XAML-приложение без Message Bus

102

Как обойтись – Behavior (или Trigger)

public class FocusNewUserBehavior : Behavior<TextBox>{ public static readonly DependencyProperty User /* код опущен */

private static void OnUserChanged (DependencyObject d, DependencyPropertyChangedEventArgs e) { var behavior = (FocusNewUserBehavior) d;

if (behavior.User?.IsNew) { behavior.AssociatedObject.Focus(); } } }

Page 103: Как написать XAML-приложение без Message Bus

103

Как обойтись – Behavior (или Trigger)

public class FocusNewUserBehavior : Behavior<TextBox>{ public static readonly DependencyProperty User /* код опущен */

private static void OnUserChanged (DependencyObject d, DependencyPropertyChangedEventArgs e) { var behavior = (FocusNewUserBehavior) d;

if (behavior.User?.IsNew) { behavior.AssociatedObject.Focus(); } } }

Page 104: Как написать XAML-приложение без Message Bus

104

Как обойтись – Behavior (или Trigger)

public class FocusNewUserBehavior : Behavior<TextBox>{ public static readonly DependencyProperty User /* код опущен */

private static void OnUserChanged (DependencyObject d, DependencyPropertyChangedEventArgs e) { var behavior = (FocusNewUserBehavior) d;

if (behavior.User?.IsNew) { behavior.AssociatedObject.Focus(); } } }

Page 105: Как написать XAML-приложение без Message Bus

105

Как обойтись – Behavior (или Trigger)

public class FocusNewUserBehavior : Behavior<TextBox>{ public static readonly DependencyProperty User /* код опущен */

private static void OnUserChanged (DependencyObject d, DependencyPropertyChangedEventArgs e) { var behavior = (FocusNewUserBehavior) d;

if (behavior.User?.IsNew) { behavior.AssociatedObject.Focus(); } } }

Page 106: Как написать XAML-приложение без Message Bus

106

Attach Behavior к TestBox<TextBox Name="FirstNameTextBox" Text="{Binding User.FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">

<i:Interaction.Behaviors> <behaviors:FocusNewUserBehavior User="{Binding User}"/> </i:Interaction.Behaviors>

</TextBox>

Page 107: Как написать XAML-приложение без Message Bus

107

Logic-ViewModel Message – не Observable модель

• Изменение объектов кеша• Изменение пользовательских настроек

Page 108: Как написать XAML-приложение без Message Bus

108

Пример: Stock Trader (Prism)

Page 109: Как написать XAML-приложение без Message Bus

109

Не Observable модели (Prism)protected void UpdatePrice(string tickerSymbol, decimal newPrice){ _priceList[tickerSymbol] = newPrice;

OnPricesUpdated();}

private void OnPricesUpdated(){ var clonedPriceList = new Dictionary<string, decimal>(_priceList);

EventAggregator.GetEvent<MarketPricesUpdatedEvent>().Publish(clonedPriceList);}

Page 110: Как написать XAML-приложение без Message Bus

110

Не Observable модели (Prism)protected void UpdatePrice(string tickerSymbol, decimal newPrice){ _priceList[tickerSymbol] = newPrice;

OnPricesUpdated();}

private void OnPricesUpdated(){ var clonedPriceList = new Dictionary<string, decimal>(_priceList);

EventAggregator.GetEvent<MarketPricesUpdatedEvent>().Publish(clonedPriceList);}

Page 111: Как написать XAML-приложение без Message Bus

111

Не Observable модели (Prism)protected void UpdatePrice(string tickerSymbol, decimal newPrice){ _priceList[tickerSymbol] = newPrice;

OnPricesUpdated();}

private void OnPricesUpdated(){ var clonedPriceList = new Dictionary<string, decimal>(_priceList);

EventAggregator.GetEvent<MarketPricesUpdatedEvent>().Publish(clonedPriceList);}

Page 112: Как написать XAML-приложение без Message Bus

112

Не Observable модели (Prism)protected void UpdatePrice(string tickerSymbol, decimal newPrice){ _priceList[tickerSymbol] = newPrice;

OnPricesUpdated();}

private void OnPricesUpdated(){ var clonedPriceList = new Dictionary<string, decimal>(_priceList);

EventAggregator.GetEvent<MarketPricesUpdatedEvent>().Publish(clonedPriceList);}

Page 113: Как написать XAML-приложение без Message Bus

113

Не Observable модели (Prism)protected void UpdatePrice(string tickerSymbol, decimal newPrice){ _priceList[tickerSymbol] = newPrice;

OnPricesUpdated();}

private void OnPricesUpdated(){ var clonedPriceList = new Dictionary<string, decimal>(_priceList);

EventAggregator.GetEvent<MarketPricesUpdatedEvent>().Publish(clonedPriceList);}

Page 114: Как написать XAML-приложение без Message Bus

114

Не Observable модели (Prism)protected void UpdatePrice(string tickerSymbol, decimal newPrice){ _priceList[tickerSymbol] = newPrice;

OnPricesUpdated();}

private void OnPricesUpdated(){ var clonedPriceList = new Dictionary<string, decimal>(_priceList);

EventAggregator.GetEvent<MarketPricesUpdatedEvent>().Publish(clonedPriceList);}

Page 115: Как написать XAML-приложение без Message Bus

115

Не Observable модели (Prism)protected void UpdatePrice(string tickerSymbol, decimal newPrice){ _priceList[tickerSymbol] = newPrice;

OnPricesUpdated();}

private void OnPricesUpdated(){ var clonedPriceList = new Dictionary<string, decimal>(_priceList);

EventAggregator.GetEvent<MarketPricesUpdatedEvent>().Publish(clonedPriceList);}

Page 116: Как написать XAML-приложение без Message Bus

116

Решение – Observable модельpublic class ObservableDictionary<TKey, TValue>:INotifyCollectionChanged, INotifyPropertyChanged

{}

https://code.msdn.microsoft.com/windowsdesktop/Samples-for-Parallel-b4b76364

Page 117: Как написать XAML-приложение без Message Bus

117

Не Observable модели (Prism)protected void UpdatePrice(string tickerSymbol, decimal newPrice){ _priceList[tickerSymbol] = newPrice;

OnPricesUpdated();}

private void OnPricesUpdated(){ var clonedPriceList = new Dictionary<string, decimal>(_priceList);

EventAggregator.GetEvent<MarketPricesUpdatedEvent>().Publish(clonedPriceList);}

Page 118: Как написать XAML-приложение без Message Bus

118

Observable модельprotected void UpdatePrice(string tickerSymbol, decimal newPrice){ _priceList[tickerSymbol] = newPrice;

OnPricesUpdated();}

private void OnPricesUpdated(){ var clonedPriceList = new Dictionary<string, decimal>(_priceList);

EventAggregator.GetEvent<MarketPricesUpdatedEvent>().Publish(clonedPriceList);}

Page 119: Как написать XAML-приложение без Message Bus

119

Как избежать Messages между уровнями

Messages

Page 120: Как написать XAML-приложение без Message Bus

120

MVVM фреймворки без MessageBus

• Таких нет • В отдельном пакете• Prism 5 – SubEvents (в Prism 6 уже неотделяем)• MvvmCross – Messenger plugin

Page 121: Как написать XAML-приложение без Message Bus

121

MessageBus нужно знать в лицо!Prism – EventAggregatorCaliburn.Micro – EventAggregatorMugenMvvmToolkit - EventAggregatorMVVM Light – MessengerReactiveUI – MessageBus

Page 122: Как написать XAML-приложение без Message Bus

122

Как жить без MessageBus на WPF и UWP

• Caliburn.Micro• MugenMvvmToolkit• MVVM без UI Composition• + свой ViewModel-First UI Composition

Page 123: Как написать XAML-приложение без Message Bus

123

Не позволяет жить без MessageBus

WPF• Prism.WPF

UWP• Таких нет • Prism.Windows без UI Composition Как добавить UI Composition: https://github.com/denis-tsv/Prism.StoreApps.Extensions.Mvvm

Page 124: Как написать XAML-приложение без Message Bus

124

MessageBus на UIДля чего используется Как обойтисьUI Composition Разделение UI Composition и ViewSwitching

Глобальные события (смена контекста или lifecycle)

Цепочка вызовов методовЧастные решения с обычными событиями

ViewModel-View Message Triggers, BehaviorsLogic-ViewModel Message Observable Model

Избежать Messages между уровнями Разные сборки для View, ViewModel, Logic Messages в сборке ViewModel

Page 125: Как написать XAML-приложение без Message Bus

125

Главная проблема MessageBus на UI – костыли!

Messages между слоями• Messages вместо Events (Logic -> ViewModel

Messages)• Messages вместо Bindings (ViewModel -> View

Messages)

Page 126: Как написать XAML-приложение без Message Bus

126

Как можно жить с MessageBus на UI

• UI Composition в стиле View-First (Prism.WPF)• Отдельные сборки View, ViewModel, Logic• Messages только в ViewModel

• Глобальные Messages

Page 127: Как написать XAML-приложение без Message Bus

127

Моя позиция• Большинство бизнес-приложений маленькие и средние• Команда 3-7 человек• Срок пара месяцев – пара лет

• UI сложнее бэкэнда• Не нужно CQRS, Microservices, DataAccess Abstraction

• MessageBus • Ещё сильнее запутывает и так сложный UI• Любую UI задачу можно решить без MessageBus• Примеры Prism без EventAggregator полезная ссылка №2• Строка «MessageBus» ломает билд полезная ссылка №9

Page 128: Как написать XAML-приложение без Message Bus

128

Paul Betts (ReactiveUI author) about MessageBus

… there are often more correct ways to solve problems, given a bit of ingenuity …

Page 129: Как написать XAML-приложение без Message Bus

129

В параллельной вселенной JSСходства• Fontend не уступает Backend по сложности • Развесистые JS-фреймворки (Angular, React)• Компонент JS = Регион XAML • Компонент (Шаблон + Поведение)• Регион (View + ViewModel)

Page 130: Как написать XAML-приложение без Message Bus

130

Olical EventEmittervar ee = new EventEmitter();

function listener() { }

ee.addListener('event', listener);ee.emitEvent('event');ee.removeListener('event', listener);

Page 131: Как написать XAML-приложение без Message Bus

131

Olical EventEmittervar ee = new EventEmitter();

function listener() { }

ee.addListener('event', listener);ee.emitEvent('event');ee.removeListener('event', listener);

Page 132: Как написать XAML-приложение без Message Bus

132

Olical EventEmittervar ee = new EventEmitter();

function listener() { }

ee.addListener('event', listener);ee.emitEvent('event');ee.removeListener('event', listener);

Page 133: Как написать XAML-приложение без Message Bus

133

Angular2 EventEmitter – Observer, а не MessageBus

class EventEmitter {

constructor(isAsync?: boolean)

emit(value?: T)

subscribe(generatorOrNext?: any, error?: any, complete?: any)

: any}

Page 134: Как написать XAML-приложение без Message Bus

134

Angular2 EventEmitter – Observer, а не MessageBus

class EventEmitter {

constructor(isAsync?: boolean)

emit(value?: T)

subscribe(generatorOrNext?: any, error?: any, complete?: any)

: any}

Page 135: Как написать XAML-приложение без Message Bus

135

Angular2 EventEmitter – Observer, а не MessageBus

class EventEmitter {

constructor(isAsync?: boolean)

emit(value?: T)

subscribe(generatorOrNext?: any, error?: any, complete?: any)

: any}

Page 136: Как написать XAML-приложение без Message Bus

136

Angular2 EventEmitter – Observer, а не MessageBus

class EventEmitter {

constructor(isAsync?: boolean)

emit(value?: T)

subscribe(generatorOrNext?: any, error?: any, complete?: any)

: any}

Page 137: Как написать XAML-приложение без Message Bus

137

MasterDetail: Parent@Component({ selector: 'my-app', template: `

<my-hero-master [heroes]="heroes" (onHeroSelected)="onSelect($event)“ /> <my-hero-detail [hero]="selectedHero“ /> `,})export class AppComponent { selectedHero: Hero;

onSelect(hero: Hero): void { this.selectedHero = hero; }}

Page 138: Как написать XAML-приложение без Message Bus

138

MasterDetail: Parent@Component({ selector: 'my-app', template: `

<my-hero-master [heroes]="heroes" (onHeroSelected)="onSelect($event)“ /> <my-hero-detail [hero]="selectedHero“ /> `,})export class AppComponent { selectedHero: Hero;

onSelect(hero: Hero): void { this.selectedHero = hero; }}

Page 139: Как написать XAML-приложение без Message Bus

139

MasterDetail: Parent@Component({ selector: 'my-app', template: `

<my-hero-master [heroes]="heroes" (onHeroSelected)="onSelect($event)“ /> <my-hero-detail [hero]="selectedHero“ /> `,})export class AppComponent { selectedHero: Hero;

onSelect(hero: Hero): void { this.selectedHero = hero; }}

Page 140: Как написать XAML-приложение без Message Bus

140

MasterDetail: Parent@Component({ selector: 'my-app', template: `

<my-hero-master [heroes]="heroes" (onHeroSelected)="onSelect($event)“ /> <my-hero-detail [hero]="selectedHero“ /> `,})export class AppComponent { selectedHero: Hero;

onSelect(hero: Hero): void { this.selectedHero = hero; }}

Page 141: Как написать XAML-приложение без Message Bus

141

MasterDetail: Parent@Component({ selector: 'my-app', template: `

<my-hero-master [heroes]="heroes" (onHeroSelected)="onSelect($event)“ /> <my-hero-detail [hero]="selectedHero“ /> `,})export class AppComponent { selectedHero: Hero;

onSelect(hero: Hero): void { this.selectedHero = hero; }}

Page 142: Как написать XAML-приложение без Message Bus

142

MasterDetail: Master@Component({ selector: 'my-hero-master', template: ` <ul> <li *ngFor="let hero of heroes“ (click)="onSelect(hero)"> {{hero.name}} </li> </ul> `,})export class HeroMasterComponent { @Input() heroes: Hero[]; @Output() onHeroSelected = new EventEmitter<Hero>(); onSelect(hero: Hero): void { this.onHeroSelected.emit(hero); }}

Page 143: Как написать XAML-приложение без Message Bus

143

MasterDetail: Master@Component({ selector: 'my-hero-master', template: ` <ul> <li *ngFor="let hero of heroes“ (click)="onSelect(hero)"> {{hero.name}} </li> </ul> `,})export class HeroMasterComponent { @Input() heroes: Hero[]; @Output() onHeroSelected = new EventEmitter<Hero>(); onSelect(hero: Hero): void { this.onHeroSelected.emit(hero); }}

Page 144: Как написать XAML-приложение без Message Bus

144

MasterDetail: Master@Component({ selector: 'my-hero-master', template: ` <ul> <li *ngFor="let hero of heroes“ (click)="onSelect(hero)"> {{hero.name}} </li> </ul> `,})export class HeroMasterComponent { @Input() heroes: Hero[]; @Output() onHeroSelected = new EventEmitter<Hero>(); onSelect(hero: Hero): void { this.onHeroSelected.emit(hero); }}

Page 145: Как написать XAML-приложение без Message Bus

145

MasterDetail: Master@Component({ selector: 'my-hero-master', template: ` <ul> <li *ngFor="let hero of heroes“ (click)="onSelect(hero)"> {{hero.name}} </li> </ul> `,})export class HeroMasterComponent { @Input() heroes: Hero[]; @Output() onHeroSelected = new EventEmitter<Hero>(); onSelect(hero: Hero): void { this.onHeroSelected.emit(hero); }}

Page 146: Как написать XAML-приложение без Message Bus

146

MasterDetail: Detail@Component({ selector: 'my-hero-detail', template: `

<div> <label>name: </label> <input [(ngModel)]="hero.name" placeholder="name"/>

</div> `})export class HeroDetailComponent { @Input() hero: Hero;}

Page 147: Как написать XAML-приложение без Message Bus

147

MasterDetail: Detail@Component({ selector: 'my-hero-detail', template: `

<div> <label>name: </label> <input [(ngModel)]="hero.name" placeholder="name"/>

</div> `})export class HeroDetailComponent { @Input() hero: Hero;}

Page 148: Как написать XAML-приложение без Message Bus

148

React: Lifting State Up

Page 149: Как написать XAML-приложение без Message Bus

149

Lifting State Up: Childclass TemperatureInput extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); }

handleChange(e) { this.props.onChange(e.target.value); }

render() { const value = this.props.value; return ( <input value={value} onChange={this.handleChange} /> ); }}

Page 150: Как написать XAML-приложение без Message Bus

150

Lifting State Up: Childclass TemperatureInput extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); }

handleChange(e) { this.props.onChange(e.target.value); }

render() { const value = this.props.value; return ( <input value={value} onChange={this.handleChange} /> ); }}

Page 151: Как написать XAML-приложение без Message Bus

151

Lifting State Up: Childclass TemperatureInput extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); }

handleChange(e) { this.props.onChange(e.target.value); }

render() { const value = this.props.value; return ( <input value={value} onChange={this.handleChange} /> ); }}

Page 152: Как написать XAML-приложение без Message Bus

152

Lifting State Up: Childclass TemperatureInput extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); }

handleChange(e) { this.props.onChange(e.target.value); }

render() { const value = this.props.value; return ( <input value={value} onChange={this.handleChange} /> ); }}

Page 153: Как написать XAML-приложение без Message Bus

153

Lifting State Up: Parentclass Calculator extends React.Component { constructor(props) { super(props); this.handleCelsiusChange = this.handleCelsiusChange.bind(this); this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this); }

handleCelsiusChange(value) { this.setState({value}); }

handleFahrenheitChange(value) { this.setState({value}); }}

Page 154: Как написать XAML-приложение без Message Bus

154

Lifting State Up: Parentclass Calculator extends React.Component { constructor(props) { super(props); this.handleCelsiusChange = this.handleCelsiusChange.bind(this); this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this); }

handleCelsiusChange(value) { this.setState({value}); }

handleFahrenheitChange(value) { this.setState({value}); }}

Page 155: Как написать XAML-приложение без Message Bus

155

Lifting State Up: Parentclass Calculator extends React.Component { constructor(props) { super(props); this.handleCelsiusChange = this.handleCelsiusChange.bind(this); this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this); }

handleCelsiusChange(value) { this.setState({value}); }

handleFahrenheitChange(value) { this.setState({value}); }}

Page 156: Как написать XAML-приложение без Message Bus

156

Lifting State Up: Parentrender() { const value = this.state.value; const celsius = Convert(value, toCelsius); const fahrenheit = Convert(value, toFahrenheit);

return ( <div> <TemperatureInput value={celsius} onChange={this.handleCelsiusChange} /> <TemperatureInput value={fahrenheit}

onChange={this.handleFahrenheitChange} /> </div> );}

Page 157: Как написать XAML-приложение без Message Bus

157

Lifting State Up: Parentrender() { const value = this.state.value; const celsius = Convert(value, toCelsius); const fahrenheit = Convert(value, toFahrenheit);

return ( <div> <TemperatureInput value={celsius} onChange={this.handleCelsiusChange} /> <TemperatureInput value={fahrenheit}

onChange={this.handleFahrenheitChange} /> </div> );}

Page 158: Как написать XAML-приложение без Message Bus

158

Lifting State Up: Parentrender() { const value = this.state.value; const celsius = Convert(value, toCelsius); const fahrenheit = Convert(value, toFahrenheit);

return ( <div> <TemperatureInput value={celsius} onChange={this.handleCelsiusChange} /> <TemperatureInput value={fahrenheit}

onChange={this.handleFahrenheitChange} /> </div> );}

Page 159: Как написать XAML-приложение без Message Bus

159

React: Context

Page 160: Как написать XAML-приложение без Message Bus

160

Context: Childclass TemperatureInput extends React.Component { render() { return ( <input style={{background: this.context.color}} /> ); }}

TemperatureInput.contextTypes = { color: React.PropTypes.string};

Page 161: Как написать XAML-приложение без Message Bus

161

Context: Childclass TemperatureInput extends React.Component { render() { return ( <input style={{background: this.context.color}} /> ); }}

TemperatureInput.contextTypes = { color: React.PropTypes.string};

Page 162: Как написать XAML-приложение без Message Bus

162

Context: Childclass TemperatureInput extends React.Component { render() { return ( <input style={{background: this.context.color}} /> ); }}

TemperatureInput.contextTypes = { color: React.PropTypes.string};

Page 163: Как написать XAML-приложение без Message Bus

163

Context: Parentclass Calculator extends React.Component { getChildContext() { return {color: this.state.color}; } handleCelsiusChange(value) { this.setState({color : 'green'}); }}Calculator.childContextTypes = { color: React.PropTypes.string};

Page 164: Как написать XAML-приложение без Message Bus

164

Context: Parentclass Calculator extends React.Component { getChildContext() { return {color: this.state.color}; } handleCelsiusChange(value) { this.setState({color : 'green'}); }}Calculator.childContextTypes = { color: React.PropTypes.string};

Page 165: Как написать XAML-приложение без Message Bus

165

Context: Parentclass Calculator extends React.Component { getChildContext() { return {color: this.state.color}; } handleCelsiusChange(value) { this.setState({color : 'green'}); }}Calculator.childContextTypes = { color: React.PropTypes.string};

Page 166: Как написать XAML-приложение без Message Bus

166

Context: Parentclass Calculator extends React.Component { getChildContext() { return {color: this.state.color}; } handleCelsiusChange(value) { this.setState({color : 'green'}); }}Calculator.childContextTypes = { color: React.PropTypes.string};

Page 167: Как написать XAML-приложение без Message Bus

167

React Context – не MessageBusЗадача• Передача от родителя к вложенным компонентам• С уровня 1 на уровень 3 минуя уровень 2Особенности реализации• Доступен родительскому и вложенным компонентам

(не любым компонентам)• Обновление контекста во вложенных компонентах не

работаетЭто не MessageBus

Page 168: Как написать XAML-приложение без Message Bus

168

Резюме: MessageBus на JS• MessageBus существует, но в виде отдельных библиотек• MessageBus нет в коробке флагманов Angular2 и React• Официальный гайд не рекомендует MesageBus• Компонент != ViewModel• Компонент = Шаблон + Поведение• ViewModel = Поведение• На JS нет разных видов композиции ViewFirst и ViewModelFirst

• Для JS MessageBus не является проблемой, как в XAML• Продуманная архитектура JS фреймворков – аргумент за

переписывание UI на web-стек

Page 169: Как написать XAML-приложение без Message Bus

169

Полезные ссылки1. MessageBus, врага нужно знать в лицоhttps://msdn.microsoft.com/en-us/library/ff647328.aspx

2. Примеры StockTrader и UIComposition для Prism.WPF, откуда выпилен EventAggregator https://github.com/denis-tsv/Prism-Samples-Wpf-NoEventAggregetor

3. Как добавить UI Composition для Prism.Windows (UWP)https://github.com/denis-tsv/Prism.StoreApps.Extensions.Mvvm

4. Paul Betts. MessageBus and why you shouldn't use ithttp://log.paulbetts.org/messagebus-and-why-you-shouldnt-use-it/

5, Caliburn.Micro Классный, минималистичный, кроссплатформенный и мультипарадигменный MVVM, в котором нет ничего лишнегоhttp://caliburnmicro.com/

6. MugenMvvmToolkit. Мощный UI фреймворк с офигительными кастомными XAML биндингамиhttps://github.com/MugenMvvmToolkithttps://habrahabr.ru/post/236745/

7. ReactiveUI. Биндинг свойств одной ViewModel на свойства другой ViewModel в родительской ViewModelhttp://reactiveui.net/

8. Пример View-ViewModel mapping с использованием MEFhttp://www.intuit.ru/EDI/14_12_14_2/1418505481-18353/tutorial/1050/objects/1/files/SampleSolution.zip

9. Как сломать билд (о, да!) словом MessageBus, EventAggregator, Messenger и любой другой реализацией MessageBushttp://hmemcpy.com/2013/03/even-more-resharper-annotations-hackery-marking-3rd-party-code-as-obsolete/

Page 170: Как написать XAML-приложение без Message Bus

170

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

Вопросы?

Денис Цветцих[email protected]