Кривая обучения разработчика Windows Phone
При разработке автономного веб-браузера для Windows Phone мне пришлось создать небольшую инфраструктуру MVVM, чтобы не допустить логики в представления.
Одним из первых, что я обнаружил пропущенным, был способ принудительного немедленного распространения текста, введенного в элемент управления TextBox, в свойство привязки данных моей ViewModel.
По умолчанию привязка TextBox срабатывает только тогда, когда элемент управления теряет фокус, и это выглядит неубедительно.
Повторное использование кода не миф!
Это действительно старая проблема. Он существовал в настольном Silverlight с самого начала. И он пробился на платформу Windows Phone.
К счастью, я вспомнил, что в те дни, когда экспериментировал с платформой Silverlight MVVM, я уже решил эту проблему, создав собственный элемент управления TextBox для Silverlight, поэтому я решил просто повторно использовать этот код и создать поведение, которое заставит обновление привязки срабатывать при каждом нажатии клавиши в TextBox. ,
В этом прелесть разработки приложения для Windows Phone — вы можете повторно использовать большую часть кода Silverlight, созданного за эти годы
Но потом я заметил еще одну проблему. Поскольку теперь каждое нажатие клавиши распространяло изменения в моей ViewModel, если пользователь печатает быстро, и я, например, выполняю какой-то вызов веб-службы при каждом изменении свойства — тогда у меня может быть слишком много запросов к веб-службе (по одному на каждое нажатие клавиши), но на самом деле Я хочу сделать звонок только тогда, когда пользователь перестал печатать на некоторое время.
К счастью, решение очень простое — мы будем использовать Reactive Extensions (Rx), поскольку он идеально подходит и уже встроен в Windows Phone.
Нам просто нужно сослаться на Microsoft.Phone.Reactive.dll из GAC (дополнительная загрузка не требуется), и мы на полпути.
Хорошо, но покажи нам код!
Да, давайте посмотрим, как выглядит код для ThrottledImmediateBindingBehavior:
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Interactivity; using Microsoft.Phone.Reactive; namespace Roboblob.Mvvm.Behaviors { public class ThrottledImmediateBindingBehavior : Behavior { private BindingExpression _expression; public bool Throttle { get; set; } private double _throttleDelayInSeconds = 0.5; private IDisposable _currentObservable; public double ThrottleDelayInSeconds { get { return _throttleDelayInSeconds; } set { _throttleDelayInSeconds = value; } } protected override void OnAttached() { base.OnAttached(); if (Throttle) { this._expression = this.AssociatedObject.GetBindingExpression(TextBox.TextProperty); var keys = Observable.FromEvent(AssociatedObject, "TextChanged").Throttle(TimeSpan.FromSeconds(ThrottleDelayInSeconds)); _currentObservable = keys.ObserveOn(Deployment.Current.Dispatcher).Subscribe(evt => OnTextChanged(evt.Sender, evt.EventArgs)); } else { this._expression = this.AssociatedObject.GetBindingExpression(TextBox.TextProperty); this.AssociatedObject.TextChanged += this.OnTextChanged; } } protected override void OnDetaching() { base.OnDetaching(); if (Throttle) { if (_currentObservable != null) { _currentObservable.Dispose(); this._expression = null; } } else { this.AssociatedObject.TextChanged -= this.OnTextChanged; this._expression = null; } } private void OnTextChanged(object sender, EventArgs args) { if (_expression != null) { this._expression.UpdateSource(); } } } }
Просто красиво
Наш класс Binding имеет свойство bool Throttle, где мы можем включить / отключить регулирование, а также ThrottleDelayInSeconds, где мы указываем, через сколько секунд после того, как пользователь прекратит вводить, наше свойство обновляется.
В OnAttached — мы просто подключаемся к TextChanged и каждый раз, когда пользователь вводит что-то, мы обновляем источник привязки.
Но если для Throttle установлено значение true, мы подключаемся к событию TextChanged через Reactive Extensions и настраиваем регулирование так, чтобы привязка не запускалась, если изменения слишком быстрые.
При этом только после того, как изменения прекратят работу на время, определенное в ThrottleDelayInSeconds, только тогда будет инициировано обновление привязки.
Конечно, мы делаем небольшую очистку в OnDetaching, так как все хорошие кодеры
А как насчет Xaml ???
Да, в Xaml мы можем использовать это так:
<TextBox Text="{Binding SearchCriteriaText, Mode=TwoWay}"> <i:Interaction.Behaviors> <RoboblobBehaviors:ThrottledImmediateBindingBehavior Throttle="True" ThrottleDelayInSeconds="1" /> </i:Interaction.Behaviors> </TextBox>
Для тех, кому лень набирать текст, я прилагаю сжатый исходный код простого проекта Windows Phone Mango, который демонстрирует использование поведения.
Кстати, кто-нибудь помнит, как это было печатать исходный код из печатных компьютерных журналов на вашем 8-битном компьютере?
Возможно нет
Ну, по крайней мере, я надеюсь, что кто-то найдет это полезным.
До следующего раза, счастливого кодирования!