Delayed Binding in Silverlight
While Delayed Binding doesn’t exist in Silverlight (new in WPF .NET 4.5), we can make do with UpdateSourceTrigger.Explicit and a little attached property.
Here’s the end result:
<TextBox x:Name="FittedPartsFilter"
Margin="10,7,48,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
ext:DelayTextChangedExtension.TextChangedDelay="1000"
Style="{StaticResource SearchTextBoxStyle}"
Text="{Binding FittedPartsFilter,
Mode=TwoWay}" />
public class DelayTextChangedExtension
{
#region Private Properties
internal static Lazy<DispatcherTimer> _timer = new Lazy<DispatcherTimer>(() => { return new DispatcherTimer(); });
internal static WeakReference _textBox = null;
#endregion
#region TextChangedDelay
/// <summary>
/// TextChangedDelay Attached Dependency Property
/// </summary>
public static readonly DependencyProperty TextChangedDelayProperty =
DependencyProperty.RegisterAttached("TextChangedDelay", typeof(double), typeof(TextBox),
new FrameworkPropertyMetadata((double)1001,
new PropertyChangedCallback(OnTextChangedDelayChanged)));
/// <summary>
/// Gets the TextChangedDelay property. This dependency property
/// indicates number of milliseconds elapsed before updating the binding source.
/// </summary>
public static double GetTextChangedDelay(DependencyObject d)
{
return (double)d.GetValue(TextChangedDelayProperty);
}
/// <summary>
/// Sets the TextChangedDelay property. This dependency property
/// indicates number of milliseconds elapsed before updating the binding source.
/// </summary>
public static void SetTextChangedDelay(DependencyObject d, double value)
{
d.SetValue(TextChangedDelayProperty, value);
SetupBindingAndHandlers(d as TextBox, textBox_TextChanged2, true);
}
/// <summary>
/// Handles changes to the TextChangedDelay property.
/// </summary>
private static void OnTextChangedDelayChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
double oldTextChangedDelay = (double)e.OldValue;
double newTextChangedDelay = (double)d.GetValue(TextChangedDelayProperty);
SetupBindingAndHandlers(d as TextBox, textBox_TextChanged2, true);
}
static void textBox_TextChanged2(object sender, TextChangedEventArgs e)
{
_textBox = new WeakReference(sender);
_timer.Value.Interval = TimeSpan.FromMilliseconds(GetTextChangedDelay(_textBox.Target as TextBox));
_timer.Value.Stop();
_timer.Value.Start();
_timer.Value.Tick += new EventHandler(UpdateSourceOnTimer);
}
static void UpdateSourceOnTimer(object sender, EventArgs e)
{
(_textBox.Target as TextBox).GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
#endregion
#region Helper
internal static void SetupBindingAndHandlers(TextBox textBox, TextChangedEventHandler handler,
bool firstTime)
{
BindingExpression be = textBox.GetBindingExpression(TextBox.TextProperty);
if (be != null)
{
if (be.ParentBinding.UpdateSourceTrigger != UpdateSourceTrigger.Explicit)
{
Binding bindingNew = Clone(be.ParentBinding);
bindingNew.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
textBox.SetBinding(TextBox.TextProperty, bindingNew);
}
textBox.TextChanged += new TextChangedEventHandler(handler);
}
else
{
// Drats, the attached property is attached before the Text
// binding is set. Wait until textbinding is set then try again.
// TODO: This routine will fail if UpdateSourceTrigger is already set to Explicit
RegisterForNotification("Text", textBox, (s, e) =>
{
BindingExpression be2 = textBox.GetBindingExpression(TextBox.TextProperty);
if (be2 != null && be2.ParentBinding.UpdateSourceTrigger != UpdateSourceTrigger.Explicit)
SetupBindingAndHandlers(textBox, handler, false);
});
}
}
internal static Binding Clone(Binding bindingOld)
{
Binding bindingNew = new Binding()
{
BindsDirectlyToSource = bindingOld.BindsDirectlyToSource,
Converter = bindingOld.Converter,
ConverterCulture = bindingOld.ConverterCulture,
ConverterParameter = bindingOld.ConverterParameter,
FallbackValue = bindingOld.FallbackValue,
Mode = bindingOld.Mode,
NotifyOnValidationError = bindingOld.NotifyOnValidationError,
Path = bindingOld.Path,
StringFormat = bindingOld.StringFormat,
TargetNullValue = bindingOld.TargetNullValue,
UpdateSourceTrigger = bindingOld.UpdateSourceTrigger,
ValidatesOnDataErrors = bindingOld.ValidatesOnDataErrors,
ValidatesOnExceptions = bindingOld.ValidatesOnExceptions,
ValidatesOnNotifyDataErrors = bindingOld.ValidatesOnNotifyDataErrors,
};
if (bindingOld.RelativeSource != null)
bindingNew.RelativeSource = bindingOld.RelativeSource;
else if (bindingOld.Source != null)
bindingNew.Source = bindingOld.Source;
else if (bindingOld.ElementName != null)
bindingNew.ElementName = bindingOld.ElementName;
return bindingNew;
}
/// Listen for change of the dependency property
public static void RegisterForNotification(string propertyName, FrameworkElement element, PropertyChangedCallback callback)
{
//Bind to a dependency property
Binding b = new Binding(propertyName) { Source = element };
var prop = System.Windows.DependencyProperty.RegisterAttached(
"ListenAttached" + propertyName,
typeof(object),
typeof(UserControl),
new System.Windows.PropertyMetadata(callback));
element.SetBinding(prop, b);
}
#endregion
}
About this entry
You’re currently reading “ Delayed Binding in Silverlight ,” an entry on Chui's Counterpoint
- Published:
- 8.19.12 / 4pm
- Category:
- .Net, Silverlight
Comments are closed
Comments are currently closed on this entry.