Кривая обучения разработчика 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-битном компьютере?
Возможно нет 
Ну, по крайней мере, я надеюсь, что кто-то найдет это полезным.
До следующего раза, счастливого кодирования!