Разработка WPF приложений в стиле ViewModel FirstDenis Tsvettsih
Презентация к докладу «Разработка WPF приложений в стиле ViewModel First» с одиннадцатой конференции dotnetconf (Челябинск, 31 октября 2015)
http://dotnetconf.ru/materialy/viewmodelfirst
Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)Ontico
РИТ++ 2017, AppsConf
Зал Касабланка, 6 июня, 16:00
Тезисы:
http://appsconf.ru/2017/abstracts/2704.html
В последнее время паттерн MVP будоражит Android-комьюнити. Уже есть несколько довольно приличных библиотек, которые помогают использовать этот подход. Но с ними вам придётся писать много boilerplate-кода. Поэтому я хочу познакомить вас с Moxy. Покажу, как использовать её компоненты для решения задач, которые будут вставать перед вами, когда вы решите использовать паттерн MVP. И расскажу, как устроены эти компоненты, и почему именно так, чтобы вы не боялись использовать Moxy из-за потенциальных подводных камней.
Разработка WPF приложений в стиле ViewModel FirstDenis Tsvettsih
Презентация к докладу «Разработка WPF приложений в стиле ViewModel First» с одиннадцатой конференции dotnetconf (Челябинск, 31 октября 2015)
http://dotnetconf.ru/materialy/viewmodelfirst
Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)Ontico
РИТ++ 2017, AppsConf
Зал Касабланка, 6 июня, 16:00
Тезисы:
http://appsconf.ru/2017/abstracts/2704.html
В последнее время паттерн MVP будоражит Android-комьюнити. Уже есть несколько довольно приличных библиотек, которые помогают использовать этот подход. Но с ними вам придётся писать много boilerplate-кода. Поэтому я хочу познакомить вас с Moxy. Покажу, как использовать её компоненты для решения задач, которые будут вставать перед вами, когда вы решите использовать паттерн MVP. И расскажу, как устроены эти компоненты, и почему именно так, чтобы вы не боялись использовать Moxy из-за потенциальных подводных камней.
«Как я научился не волноваться и полюбил Android-MVP», Никита Бартишок, ABBYYMail.ru Group
Доклад о подходе к разработке Android-приложений с использованием MVP и Clean Architecture. Никита рассмотрит преимущества этого подхода перед традиционным, уделит отдельное внимание вопросам сохранения состояния в Android-MVP, а также особенностям взаимодействия между V и P.
Слайды доклада на конференции C++ Corehard Winter 2017 (г.Минск).
Автор доклада давно и успешно использует Модель Акторов при разработке приложений на C++. В основном это был положительный опыт. Но есть некоторые неочевидные моменты, про которые было бы хорошо узнать заранее. О том, где использование Модели Акторов уместно, а где нет, на какие грабли довелось наступить, какие шишки были набиты, как можно упростить себе жизнь и пойдет речь в докладе.
Где кончается react native? / Павел Кондратенко (Rambler&Co)Ontico
РИТ++ 2017, Frontend Сonf
Зал Мумбаи, 5 июня, 11:00
Тезисы:
http://frontendconf.ru/2017/abstracts/2496.html
В своем выступлении я расскажу про то, как библиотека бумажных книг в нашей компании переехала в онлайн и причем тут react native. Погружаясь в архитектуру этой технологии я постараюсь дать представление о том, что можно выжать из нее и где заканчиваются ее возможности. Разберем потоки в приложении, возможные проблемы и все это на таких простых примерах как ActivityIndicator.
Если у вас еще не дошли руки до react native, но всегда хотели разобраться - приходите обязательно! Из моего доклада вы сможете, как минимум, получить представление об этой технологии.
Реактивный двигатель для вашего Android-приложения — Матвей Мальков, 2ГИС2ГИС Технологии
Если вы еще не знаете, что RxJava – это круто, то после доклада обязательно будете так считать! Вы также замотивируетесь писать крутые отзывчивые приложения и получите кладезь советов, как этого добиться с помощью реактивщины.
«Как я научился не волноваться и полюбил Android-MVP», Никита Бартишок, ABBYYMail.ru Group
Доклад о подходе к разработке Android-приложений с использованием MVP и Clean Architecture. Никита рассмотрит преимущества этого подхода перед традиционным, уделит отдельное внимание вопросам сохранения состояния в Android-MVP, а также особенностям взаимодействия между V и P.
Слайды доклада на конференции C++ Corehard Winter 2017 (г.Минск).
Автор доклада давно и успешно использует Модель Акторов при разработке приложений на C++. В основном это был положительный опыт. Но есть некоторые неочевидные моменты, про которые было бы хорошо узнать заранее. О том, где использование Модели Акторов уместно, а где нет, на какие грабли довелось наступить, какие шишки были набиты, как можно упростить себе жизнь и пойдет речь в докладе.
Где кончается react native? / Павел Кондратенко (Rambler&Co)Ontico
РИТ++ 2017, Frontend Сonf
Зал Мумбаи, 5 июня, 11:00
Тезисы:
http://frontendconf.ru/2017/abstracts/2496.html
В своем выступлении я расскажу про то, как библиотека бумажных книг в нашей компании переехала в онлайн и причем тут react native. Погружаясь в архитектуру этой технологии я постараюсь дать представление о том, что можно выжать из нее и где заканчиваются ее возможности. Разберем потоки в приложении, возможные проблемы и все это на таких простых примерах как ActivityIndicator.
Если у вас еще не дошли руки до react native, но всегда хотели разобраться - приходите обязательно! Из моего доклада вы сможете, как минимум, получить представление об этой технологии.
Реактивный двигатель для вашего Android-приложения — Матвей Мальков, 2ГИС2ГИС Технологии
Если вы еще не знаете, что RxJava – это круто, то после доклада обязательно будете так считать! Вы также замотивируетесь писать крутые отзывчивые приложения и получите кладезь советов, как этого добиться с помощью реактивщины.
Реактивный двигатель вашего Android приложенияMatvey Malkov
Презентация, объясняющая концепцию реактивных потоков с использованием Android SDK и RxJavа. Рассчитано на программистов с любым стажем, которые жеалют начать использовать эту концепцию в своих программах.
Реактивные потоки -- это круто.
Roslyn API: SyntaxTree vs CodeDom, SemanticModel vs ReflectionDenis Tsvettsih
Презентация к докладу «Roslyn API: SyntaxTree vs CodeDom, SemanticModel vs Reflection» с конференции .NEXT SPb 2015 (Санкт-Петербург, 5 июня 2015)
http://spb2015.dotnext.ru/#cvetkih_talk
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NETGoSharp
Наша команда в DevExpress недавно выпустила Preview версию нового продукта, RTF web-редактора – ASPxRichEdit.
Продукт требует высокой отзывчивости на действия пользователя и максимальной производительности. Поэтому клиент получился «толстым» в отличие от «тонких клиентов» большинства бизнес-приложений.
В составе продукта два полнофункциональных компонента - клиентский и серверный текстовые процессоры. Оба компонента работают независимо друг от друга. Клиентская часть создавалась как оптимизированная версия серверного компонента, переписанного с .NET на TypeScript.
Клиентская часть не уступает в сложности серверной. Кроме того, возникают дополнительные проблемы синхронизации состояний моделей на клиенте и сервере и глубокого тестирования клиент-серверного взаимодействия.
В этом докладе вы узнаете, как мы разрабатывали этот продукт, какие проблемы встретили и какие методики тестирования использовали.
Rempl — крутая платформа для крутых инструментов - Роман Дворнов (Avito)AvitoTech
Роман Дворнов (Avito)
Фронтенд усложняется с каждым днем, и уже не представить жизнь разработчика без инструментов. Инструментов становится все больше, но нельзя сказать, что их достаточно. Если у вас собственный стек или технологическое решение, вам рано или поздно потребуется сделать свой инструмент. Это не так просто! Особенно если вы захотите интегрировать его интерфейс в браузерные Developer Tools, IDE, редакторы или открыть их на другом устройстве. Добавьте сюда проблему версионирования и другие сложности, и вам покажется, что задача неподъемная.
Но есть хорошая новость! Большинство из этих проблем решает Rempl — платформа для создания и использования удаленных инструментов (на самом деле не только инструментов). Сделаем небольшой обзор Rempl: что это, зачем нужно, какие проблемы решает. А также посмотрим примеры готовых решений, построенных на Rempl.
После докладов мы проведём дискуссионную панель на тему "Организация системы компонент", в которой примут участие докладчики, а также приглашенные эксперты.
* Почему Angular 2 такой быстрый и как его ускорить еще сильнее?
* Как работает Change Detection механизм и как им управлять?
* Зачем нам Zone.js и Функциональное Реактивное Программирование?
* Как работать с Redux и Mobx в Angular 2 и что можно от этого выиграть?
Об этом и ряде других вещей вы узнаете из этого доклада.
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)Ontico
* Почему Angular 2 такой быстрый и как его ускорить еще сильнее?
* Как работает Change Detection механизм и как им управлять?
* Зачем нам Zone.js и Функциональное Реактивное Программирование?
* Как работать с Redux и Mobx в Angular 2 и что можно от этого выиграть?
Об этом и ряде других вещей вы узнаете из этого доклада.
Что нам стоит DAL построить? Акуляков Артём D2D Just.NETDev2Dev
Работа с данными - это ключевая функция большинства приложений. Но работать с данными не так просто как кажется. С одной стороны, нам нужна производительность, с другой все best practices диктуют нам принцип persistence ignorance, с третьей еще и хочется писать красивый и понятный код. Как найти баланс между всем этим? Чем хороший IRepository отличается от плохого? Что такое CQRS и причем тут функциональное программирование? Об этом и пойдет речь, а так же немного граблей и личного опыта.
Реактивные микросервисы с Apache Kafka / Денис Иванов (2ГИС)Ontico
HighLoad++ 2017
Зал «Дели + Калькутта», 7 ноября, 14:00
Тезисы:
http://www.highload.ru/2017/abstracts/3031.html
Apache Kafka - довольно популярная опенсорс-платформа для обработки потоков сообщений. Абстракция распределенного лога, лежащая в основе Kafka, дает возможность использовать ее в качестве системы очередей, но при этом дает некоторые очень полезные преимущества, недоступные даже решениям ESB-уровня.
...
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Sergey Platonov
В последнее время в промышленной разработке ПО особую популярность обретают Domain-Specific Lanugages (DSL). Они драматически упрощают разработку и дают возможность “программировать” не только программистам, но и пользователям прикладных программ.
В своем докладе я расскажу об опыте использования DSL применительно к С++, причем упор будет сделан на производительность кода DSL, и его мгновенную “встраиваемость” в запущенную программу путем компиляции DSL-кода в нативный код с помощью инструментария LLVM.
Презентация подготовлена по материалам выступления Владислава Плясова на витебском MiniQ#23, который был проведен 27 февраля 2020 года:
https://communities.by/events/miniq-vitebsk-23 .
Про доклад:
Большинство нас слышало про такую технологию как GraphQL, но кто слышал про Apollo GraphQL Federation?
В своем докладе я бы хотел освятить следующие темы:
- GraphQL. Базовые понятия.
- Apollo Gateway. Что это за зверь и как им пользоваться. Проблема, которую он решает.
- Как создать свою "федерацию" из нескольких GraphQL сервисов.
- Какие плюшки предоставляет нам Apollo Gateway со старта.
- Ваши вопросы.
Similar to Как приручить реактивное программирование в XAML приложениях (20)
5. 5
Сэмплы не показательны
Rx – 101 пример задач, где можно обойтись без Rx
ReactiveUI – единственный жизненный сэмпл – поиск (на
главной странице)
6. 6
О чем мы поговорим?
• Что такое реактивное программирование
• Примеры задач из продакшен проектов, которые проще
решаются с помощью Rx и ReactiveUI
• Антипаттерны: как ReactiveUI лучше не использовать
11. 11
Реактивная программа
var A = 10;
var B <- A + 1;
<- оператор «судьбы»
// Чему равно В?
11
A = A + 1;
// А теперь чему равно В?
12
12. 12
Реактивное программирование
Реактивное программирование – это парадигма
программирования, ориентированная на потоки данных и
распространение изменений.
Поток данных – последовательность значений переменной
или свойства класса.
Распространение изменений – уведомления
«заинтересованных» об изменениях.
13. 13
Pull
Pull – класс A взаимодействует с классом B и вытягивает из
него необходимые данные
Class A Class B
GetData()
Interacts with
14. 14
Push
Push – класс B самостоятельно выталкивает данные классу
A, как только они становятся доступны.
Class A
ProcessData()
Class BReacts on
16. 16
Реактивное программирование на .NET
• Reactive Extensions (Rx)
• Эрик Мейер из MS Research
• ReactiveUI
• Разработана в MS Research
• библиотека на базе Rx для создания элегантных UI для всех
XAML платформ
17. 17
Внутри .NET
public interface IObservable<T>
{
IDisposable Subscribe(IObserver<T> observer);
}
public interface IObserver<T>
{
void OnNext(T value);
void OnCompleted();
void OnError(Exception error);
}
18. 18
Внутри .NET
public interface IObservable<T>
{
IDisposable Subscribe(IObserver<T> observer);
}
public interface IObserver<T>
{
void OnNext(T value);
void OnCompleted();
void OnError(Exception error);
}
19. 19
Внутри .NET
public interface IObservable<T>
{
IDisposable Subscribe(IObserver<T> observer);
}
public interface IObserver<T>
{
void OnNext(T value);
void OnCompleted();
void OnError(Exception error);
}
20. 20
Внутри .NET
public interface IObservable<T>
{
IDisposable Subscribe(IObserver<T> observer);
}
public interface IObserver<T>
{
void OnNext(T value);
void OnCompleted();
void OnError(Exception error);
}
21. 21
Внутри .NET
public interface IObservable<T>
{
IDisposable Subscribe(IObserver<T> observer);
}
public interface IObserver<T>
{
void OnNext(T value);
void OnCompleted();
void OnError(Exception error);
}
22. 22
Оператор судьбы через Rx
var A = new Subject<int>();
// B <- A + 1
var B = A.Select(a => a + 1);
A.OnNext(10);
23. 23
Оператор судьбы через Rx
var A = new Subject<int>();
// B <- A + 1
var B = A.Select(a => a + 1);
A.OnNext(10);
24. 24
Оператор судьбы через Rx
var A = new Subject<int>();
// B <- A + 1
var B = A.Select(a => a + 1);
A.OnNext(10);
25. 25
Оператор судьбы через Rx
var A = new Subject<int>();
// B <- A + 1
var B = A.Select(a => a + 1);
A.OnNext(10);
35. 35
Обычная подписка на событие PropertyChanged
var filter = new Filter();
// Подписка
filter.PropertyChanged += OnPropertyChanged;
// Реакция на событие
private void OnPropertyChanged
(object sender, PropertyChangedEventArgs e)
{
}
// Отписка
filter.PropertyChanged -= OnPropertyChanged;
36. 36
Обычная подписка на событие PropertyChanged
var filter = new Filter();
// Подписка
filter.PropertyChanged += OnPropertyChanged;
// Реакция на событие
private void OnPropertyChanged
(object sender, PropertyChangedEventArgs e)
{
}
// Отписка
filter.PropertyChanged -= OnPropertyChanged;
37. 37
var filter = new Filter();
// Подписка
filter.PropertyChanged += OnPropertyChanged;
// Реакция на событие
private void OnPropertyChanged
(object sender, PropertyChangedEventArgs e)
{
}
// Отписка
filter.PropertyChanged -= OnPropertyChanged;
Обычная подписка на событие PropertyChanged
38. 38
Обычная подписка на событие PropertyChanged
var filter = new Filter();
// Подписка
filter.PropertyChanged += OnPropertyChanged;
// Реакция на событие
private void OnPropertyChanged
(object sender, PropertyChangedEventArgs e)
{
}
// Отписка
filter.PropertyChanged -= OnPropertyChanged;
39. 39
filter = new Filter();
// Подписка
IDisposable subscription =
Observable.FromEventPattern<PropertyChangedEventArgs>
(filter, "PropertyChanged")
.Subscribe(OnPropertyChanged);
// Реакция на событие
private void OnPropertyChanged
(EventPattern<PropertyChangedEventArgs> eventPattern)
{
//eventPattern.Sender и eventPattern.EventArgs
}
// Отписка
subscription.Dispose();
Rx подписка на событие PropertyChanged
40. 40
filter = new Filter();
// Подписка
IDisposable subscription =
Observable.FromEventPattern<PropertyChangedEventArgs>
(filter, "PropertyChanged")
.Subscribe(OnPropertyChanged);
// Реакция на событие
private void OnPropertyChanged
(EventPattern<PropertyChangedEventArgs> eventPattern)
{
//eventPattern.Sender и eventPattern.EventArgs
}
// Отписка
subscription.Dispose();
Rx подписка на событие PropertyChanged
41. 41
filter = new Filter();
// Подписка
IDisposable subscription =
Observable.FromEventPattern<PropertyChangedEventArgs>
(filter, "PropertyChanged")
.Subscribe(OnPropertyChanged);
// Реакция на событие
private void OnPropertyChanged
(EventPattern<PropertyChangedEventArgs> eventPattern)
{
//eventPattern.Sender и eventPattern.EventArgs
}
// Отписка
subscription.Dispose();
Rx подписка на событие PropertyChanged
42. 42
filter = new Filter();
// Подписка
IDisposable subscription =
Observable.FromEventPattern<PropertyChangedEventArgs>
(filter, "PropertyChanged")
.Subscribe(OnPropertyChanged);
// Реакция на событие
private void OnPropertyChanged
(EventPattern<PropertyChangedEventArgs> eventPattern)
{
//eventPattern.Sender и eventPattern.EventArgs
}
// Отписка
subscription.Dispose();
Rx подписка на событие PropertyChanged
43. 43
Rx подписка на событие PropertyChanged
filter = new Filter();
// Подписка
IDisposable subscription =
Observable.FromEventPattern<PropertyChangedEventArgs>
(filter, "PropertyChanged")
.Subscribe(OnPropertyChanged);
// Реакция на событие
private void OnPropertyChanged
(EventPattern<PropertyChangedEventArgs> eventPattern)
{
//eventPattern.Sender и eventPattern.EventArgs
}
// Отписка
subscription.Dispose();
44. 44
Rx подписка на событие PropertyChanged
filter = new Filter();
// Подписка
IDisposable subscription =
Observable.FromEventPattern<PropertyChangedEventArgs>
(filter, "PropertyChanged")
.Subscribe(OnPropertyChanged);
// Реакция на событие
private void OnPropertyChanged
(EventPattern<PropertyChangedEventArgs> eventPattern)
{
//eventPattern.Sender и eventPattern.EventArgs
}
// Отписка
subscription.Dispose();
45. 45
Достоинства Rx подписки
• Подписка инкапсулируется в IDisposable объекте, просто
сделать отписку
• Подписка – это поток данных, над которым можно
выполнять операции
47. 47
Обычная подписка на изменение свойства FromDate
Filter filter = new Filter();
filter.PropertyChanged += OnPropertyChanged;
private void OnPropertyChanged
(object sender, PropertyChangedEventArgs eventArgs)
{
if (eventArgs.PropertyName == “FromDate")
{
// Действия
}
}
48. 48
Обычная подписка на изменение свойства FromDate
Filter filter = new Filter();
filter.PropertyChanged += OnPropertyChanged;
private void OnPropertyChanged
(object sender, PropertyChangedEventArgs eventArgs)
{
if (eventArgs.PropertyName == “FromDate")
{
// Действия
}
}
49. 49
Обычная подписка на изменение свойства FromDate
Filter filter = new Filter();
filter.PropertyChanged += OnPropertyChanged;
private void OnPropertyChanged
(object sender, PropertyChangedEventArgs eventArgs)
{
if (eventArgs.PropertyName == “FromDate")
{
// Действия
}
}
50. 50
ReactiveUI подписка на изменение свойства FromDate
var filter = new Filter();
var subscription = filter
.ObservableForProperty(f => f.FromDate)
.Subscribe(FromDateChanged);
private void FromDateChanged
(IObservedChange<Filter, DateTime> change)
{
// Действия
}
51. 51
ReactiveUI подписка на изменение свойства FromDate
var filter = new Filter();
var subscription = filter
.ObservableForProperty(f => f.FromDate)
.Subscribe(FromDateChanged);
private void FromDateChanged
(IObservedChange<Filter, DateTime> change)
{
// Действия
}
52. 52
ReactiveUI подписка на изменение свойства FromDate
var filter = new Filter();
var subscription = filter
.ObservableForProperty(f => f.FromDate)
.Subscribe(FromDateChanged);
private void FromDateChanged
(IObservedChange<Filter, DateTime> change)
{
// Действия
}
53. 53
ReactiveUI подписка на изменение свойства FromDate
var filter = new Filter();
var subscription = filter
.ObservableForProperty(f => f.FromDate)
.Subscribe(FromDateChanged);
private void FromDateChanged
(IObservedChange<Filter, DateTime> change)
{
// Действия
}
56. 56
Добавим выбор страны и города
Добавим в фильтр:
• Список стран и городов
• При выборе страны актуализируем список городов
57. 57
Валидация выбранного периода
Условие: FromDate не превосходит ToDate
Если FromDate > ToDate, тогда ToDate приравниваем к
FromDate
Если ToDate < FromDate, тогда FromDate приравниваем к
ToDate
58. 58
Обычное решение
private DateTime _fromDate;
public DateTime FromDate
{
get { return _fromDate; }
set
{
_fromDate = value;
OnPropertyChanged();
if (_fromDate > ToDate) ToDate = _fromDate;
}
}
59. 59
Обычное решение
private DateTime _fromDate;
public DateTime FromDate
{
get { return _fromDate; }
set
{
_fromDate = value;
OnPropertyChanged();
if (_fromDate > ToDate) ToDate = _fromDate;
}
}
68. 68
Задача 4: мониторинг одновременно нескольких
свойств
Поля фильтра:
• От
• До
• Страна
• Город
При изменении любого – обновляем данные
69. 69
ViewModel с фильтром
public class ViewModel
{
public Filter Filter { get; set; }
public ViewModel()
{
Filter = new Filter();
// Подписка на изменение свойств фильтра
}
}
70. 70
ViewModel с фильтром
public class ViewModel
{
public Filter Filter { get; set; }
public ViewModel()
{
Filter = new Filter();
// Подписка на изменение свойств фильтра
}
}
71. 71
ViewModel с фильтром
public class ViewModel
{
public Filter Filter { get; set; }
public ViewModel()
{
Filter = new Filter();
// Подписка на изменение свойств фильтра
}
}
72. 72
Обычное решение
public ViewModel()
{
Filter = new Filter();
// Подписка на изменение свойств фильтра
Filter.PropertyChanged += OnFilterPropertyChanged;
}
private void OnFilterPropertyChanged
(object sender, PropertyChangedEventArgs eventArgs)
{
if (eventArgs.PropertyName == "FromDate" ||
eventArgs.PropertyName == "ToDate" ||
eventArgs.PropertyName == "SelectedCountry" ||
eventArgs.PropertyName == "SelectedCity")
{
Refresh();
}
}
73. 73
Обычное решение
public ViewModel()
{
Filter = new Filter();
// Подписка на изменение свойств фильтра
Filter.PropertyChanged += OnFilterPropertyChanged;
}
private void OnFilterPropertyChanged
(object sender, PropertyChangedEventArgs eventArgs)
{
if (eventArgs.PropertyName == "FromDate" ||
eventArgs.PropertyName == "ToDate" ||
eventArgs.PropertyName == "SelectedCountry" ||
eventArgs.PropertyName == "SelectedCity")
{
Refresh();
}
}
74. 74
ReactiveUI решение
public ViewModel()
{
Filter = new Filter();
// Подписка на изменение свойств фильтра
_subscription =
Filter.WhenAnyValue(
f => f.FromDate,
f => f.ToDate,
f => f.SelectedCountry,
f => f.SelectedCity)
.Subscribe(_ => Refresh());
}
75. 75
ReactiveUI решение
public ViewModel()
{
Filter = new Filter();
// Подписка на изменение свойств фильтра
_subscription =
Filter.WhenAnyValue(
f => f.FromDate,
f => f.ToDate,
f => f.SelectedCountry,
f => f.SelectedCity)
.Subscribe(_ => Refresh());
}
76. 76
ReactiveUI решение
public ViewModel()
{
Filter = new Filter();
// Подписка на изменение свойств фильтра
_subscription =
Filter.WhenAnyValue(
f => f.FromDate,
f => f.ToDate,
f => f.SelectedCountry,
f => f.SelectedCity)
.Subscribe(_ => Refresh());
}
77. 77
ReactiveUI решение
public ViewModel()
{
Filter = new Filter();
// Подписка на изменение свойств фильтра
_subscription =
Filter.WhenAnyValue(
f => f.FromDate,
f => f.ToDate,
f => f.SelectedCountry,
f => f.SelectedCity)
.Subscribe(_ => Refresh());
}
79. 79
Задача 5: проверка CanExecute
команды
• Добавляем кнопку «Обновить» на фильтр
• Страна и город – обязательные поля
80. 80
RelayCommand из MvvmLight
public RelayCommand RefreshCommand { get; private set; }
public Filter()
{
RefreshCommand = new RelayCommand(
OnRefresh,
() => SelectedCity != null && SelectedCountry != null);
}
81. 81
RelayCommand из MvvmLight
public RelayCommand RefreshCommand { get; private set; }
public Filter()
{
RefreshCommand = new RelayCommand(
OnRefresh,
() => SelectedCity != null && SelectedCountry != null);
}
82. 82
RelayCommand из MvvmLight
public RelayCommand RefreshCommand { get; private set; }
public Filter()
{
RefreshCommand = new RelayCommand(
OnRefresh,
() => SelectedCity != null && SelectedCountry != null);
}
83. 83
RelayCommand из MvvmLight
public RelayCommand RefreshCommand { get; private set; }
public Filter()
{
RefreshCommand = new RelayCommand(
OnRefresh,
() => SelectedCity != null && SelectedCountry != null);
}
84. 84
Подергивания
public string SelectedCountry {
get { return _selectedCountry; }
set {
_selectedCountry = value; OnPropertyChanged();
RefreshCommand.RaiseCanExecuteChanged();
}
}
public string SelectedCity {
get { return _selectedCity; }
set {
_selectedCity = value; OnPropertyChanged();
RefreshCommand.RaiseCanExecuteChanged();
}
}
85. 85
ReactiveCommand
public Filter()
{
var canExecute = this.WhenAny(
f => f.SelectedCity,
f => f.SelectedCountry,
(city, country) =>
city.Value != null && country.Value != null);
var command = ReactiveCommand.Create(canExecute);
command.Subscribe(_ => OnRefresh());
RefreshCommand = command;
}
public string SelectedCountry { get; set; }
86. 86
ReactiveCommand
public Filter()
{
var canExecute = this.WhenAny(
f => f.SelectedCity,
f => f.SelectedCountry,
(city, country) =>
city.Value != null && country.Value != null);
var command = ReactiveCommand.Create(canExecute);
command.Subscribe(_ => OnRefresh());
RefreshCommand = command;
}
public string SelectedCountry { get; set; }
87. 87
ReactiveCommand
public Filter()
{
var canExecute = this.WhenAny(
f => f.SelectedCity,
f => f.SelectedCountry,
(city, country) =>
city.Value != null && country.Value != null);
var command = ReactiveCommand.Create(canExecute);
command.Subscribe(_ => OnRefresh());
RefreshCommand = command;
}
public string SelectedCountry { get; set; }
88. 88
ReactiveCommand
public Filter()
{
var canExecute = this.WhenAny(
f => f.SelectedCity,
f => f.SelectedCountry,
(city, country) =>
city.Value != null && country.Value != null);
var command = ReactiveCommand.Create(canExecute);
command.Subscribe(_ => OnRefresh());
RefreshCommand = command;
}
public string SelectedCountry { get; set; }
89. 89
ReactiveCommand
public Filter()
{
var canExecute = this.WhenAny(
f => f.SelectedCity,
f => f.SelectedCountry,
(city, country) =>
city.Value != null && country.Value != null);
var command = ReactiveCommand.Create(canExecute);
command.Subscribe(_ => OnRefresh());
RefreshCommand = command;
}
public string SelectedCountry { get; set; }
90. 90
ReactiveCommand
public Filter()
{
var canExecute = this.WhenAny(
f => f.SelectedCity,
f => f.SelectedCountry,
(city, country) =>
city.Value != null && country.Value != null);
var command = ReactiveCommand.Create(canExecute);
command.Subscribe(_ => OnRefresh());
RefreshCommand = command;
}
public string SelectedCountry { get; set; }
// RefreshCommand.RaiseCanExecuteChanged();
91. 91
Достоинства ReactiveCommand
• Не нужны «подергивания» в сеттерах
• При изменения условия нельзя забыть убрать или
добавить «подергивание»
92. 92
Задача 6: команда запускает асинхронное
действие
• При выполнении команды – вызов вебсервиса
• Пока не получим ответ от сервиса – команду нельзя
выполнить
93. 93
Асинхронное действие RelayCommand
public bool IsBusy { get; set; }
//RefreshCommand.RaiseCanExecuteChanged();
public Filter(Task refreshTask)
{
RefreshTask = refreshTask;
RefreshCommand = new RelayCommand(OnRefresh,
() => SelectedCity != null &&
SelectedCountry != null &&
!IsBusy);
}
94. 94
Асинхронное действие RelayCommand
public bool IsBusy { get; set; }
//RefreshCommand.RaiseCanExecuteChanged();
public Filter(Task refreshTask)
{
RefreshTask = refreshTask;
RefreshCommand = new RelayCommand(OnRefresh,
() => SelectedCity != null &&
SelectedCountry != null &&
!IsBusy);
}
95. 95
Асинхронное действие RelayCommand
public bool IsBusy { get; set; }
//RefreshCommand.RaiseCanExecuteChanged();
public Filter(Task refreshTask)
{
RefreshTask = refreshTask;
RefreshCommand = new RelayCommand(OnRefresh,
() => SelectedCity != null &&
SelectedCountry != null &&
!IsBusy);
}
106. 106
Задача 7: поиск по подстроке
Вместо фильтра делаем поиск
Если длина строки < 3, то ждем 0,5 сек перед поиском
Если длина строки >= 3, то ждем 0,25 сек перед поиском
107. 107
Поиск по строке
public class ViewModel
{
public Filter Filter { get; set; }
public string SearchText { get; set; }
}
108. 108
Поиск по строке
public class ViewModel
{
public Filter Filter { get; set; }
public string SearchText { get; set; }
}
109. 109
Поиск по строке
public class ViewModel
{
public Filter Filter { get; set; }
public string SearchText { get; set; }
}
123. 123
ViewModel First навигация
var viewModel =
(ViewModel) Locator.CurrentMutable.GetService(typeof(ViewModel));
viewModel.Title = "New ViewModel";
Router.Navigate.Execute(viewModel);
124. 124
ViewModel First навигация
var viewModel =
(ViewModel) Locator.CurrentMutable.GetService(typeof(ViewModel));
viewModel.Title = "New ViewModel";
Router.Navigate.Execute(viewModel);
125. 125
ViewModel First навигация
var viewModel =
(ViewModel) Locator.CurrentMutable.GetService(typeof(ViewModel));
viewModel.Title = "New ViewModel";
Router.Navigate.Execute(viewModel);
126. 126
ViewModel First навигация
var viewModel =
(ViewModel) Locator.CurrentMutable.GetService(typeof(ViewModel));
viewModel.Title = "New ViewModel";
Router.Navigate.Execute(viewModel);
127. 127
Нет CompositeUI
<Grid UseLayoutRounding="True">
<!--
COOLSTUFF: RoutedViewHost
RoutedViewHost will take a Router and display whatever ViewModel
is at the front of the route stack.
-->
<rx:RoutedViewHost
Router="{Binding Router}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" />
</Grid>
128. 128
Нельзя избавиться от Splat IoC
В Splat
• Получить экземпляр сервиса – аналог ServiceLocator
Locator.CurrentMutable.GetService(typeof(Service))
Выход
• Реализовать IMutableDependencyResolver (для AutoFac)
129. 129
Недостатки ReactiveUI как MVVM
• Нельзя задать конвенцию именования для маппинга View-
ViewModel
• Нет API, упрощающего задание соответствия View-ViewModel
• Splat – это ServiceLocator в чистом виде
• Нельзя заменить Splat на свой любимый IoC
• Нельзя реализовать CompositeUI
• ViewModelFirst навигация неудобная для UWP, WinRT, Windows
Phone
131. 131
Достоинства ReactiveUI
• Меньше инфраструктурного кода
• Повышает читаемость кода, группируя зависимости между
свойствами в конструкторе
• Решение типовых задач
• Подписка на изменение свойства
• Подписка на изменение нескольких свойств
• Автоматическая проверка состояния команды при:
• Изменении любого свойства, входящего в условие
• Начале/окончании асинхронной операции, запускаемой командой
133. 133
Недостатки Rx и ReactiveUI
• Требует аккуратного использования, написать что-то
нечитаемое просто
• ReactiveUI лучше использовать как дополнение к MVVM
фреймворку
135. 135
Что дальше делать?
• Посмотреть сэмплы Rx или ReactiveUI
• Попробовать Rx и ReactiveUI на отдельных
формах, если понравится – внедрять:
• ReactiveCommand
• ObservableForProperty
• WhenAny, WhenAnyValue
• Select, Merge, Zip