The BindingExpression
class’s UpdateSource
method forces the target property value to be assigned to its source property. This allows you to perform input validation while the user is entering text into a TextBox
, rather than after the TextBox
loses focus.
I have created a class called UpdateSourceTriggerExtender
, which contains an attached property that is used to trigger an update of the binding source whenever the TextBox
’s text changes.
When the attached property is placed on a TextBox
, the UpdateSourceTriggerExtender
class’s HandleUpdatePropertyChanged
method is called, which subscribes to the TextBox.TextChanged
event (see Listing 26.1). When the text is changed the BindingExpression
for the TextProperty
is retrieved, and its UpdateSource
method is called, which effectively pushes the Text
string value to the source property.
The attached property can also be placed on a PasswordBox
, in which case its PasswordChanged
event is used to monitor for user input.
Note
The TextBox.LostFocus
event is used to explicitly set the visual state of the TextBox
. This is required because the XAML data-binding system does not place the TextBox
into the correct visual state when it loses focus if the Text
property has not changed, which is the case when you call UpdateSource
on the TextChanged
event.
public class UpdateSourceTriggerExtender
{
public static readonly DependencyProperty UpdateSourceOnTextChanged
= DependencyProperty.RegisterAttached(
"UpdateSourceOnTextChanged", typeof(bool),
typeof(UpdateSourceTriggerExtender),
new PropertyMetadata(HandleUpdatePropertyChanged));
public static bool GetUpdateSourceOnTextChanged(DependencyObject d)
{
return (bool)d.GetValue(UpdateSourceOnTextChanged);
}
public static void SetUpdateSourceOnTextChanged(
DependencyObject d, bool value)
{
d.SetValue(UpdateSourceOnTextChanged, value);
}
static void HandleUpdatePropertyChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextBox textBox = d as TextBox;
if (textBox != null)
{
if ((bool)e.OldValue)
{
textBox.TextChanged -= HandleTextBoxTextChanged;
textBox.LostFocus -= HandleBoxLostFocus;
}
if ((bool)e.NewValue)
{
textBox.TextChanged += HandleTextBoxTextChanged;
textBox.LostFocus += HandleBoxLostFocus;
}
return;
}
PasswordBox passwordBox = d as PasswordBox;
if (passwordBox == null)
{
throw new Exception("UpdateSourceTrigger can only be used "
+ "on a TextBox or PasswordBox.");
}
/* Wire up for password box. */
if ((bool)e.OldValue)
{
passwordBox.PasswordChanged -= HandlePasswordBoxTextChanged;
passwordBox.LostFocus -= HandleBoxLostFocus;
}
if ((bool)e.NewValue)
{
passwordBox.PasswordChanged += HandlePasswordBoxTextChanged;
passwordBox.LostFocus += HandleBoxLostFocus;
}
}
static void HandlePasswordBoxTextChanged(object sender, RoutedEventArgs e)
{
UpdateSource((PasswordBox)sender, PasswordBox.PasswordProperty);
}
static void HandleTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
UpdateSource((TextBox)sender, TextBox.TextProperty);
}
static void UpdateSource(
FrameworkElement element, DependencyProperty property)
{
if (element == null)
{
return;
}
BindingExpression bindingExpression
= element.GetBindingExpression(property);
if (bindingExpression != null)
{
bindingExpression.UpdateSource();
}
}
static void HandleBoxLostFocus(object sender, RoutedEventArgs e)
{
/* This method prevents the control from being placed
* into the valid state when it loses focus. */
var control = sender as Control;
if (control == null)
{
return;
}
bool hasError = Validation.GetHasError(control);
if (hasError)
{
VisualStateManager.GoToState(control, "InvalidFocused", false);
}
}
}
The following example demonstrates how the UpdateSourceTriggerExtender
can be applied to a TextBox
:
<TextBox Text="{Binding ValidatedString1, Mode=TwoWay,
UpdateSourceTrigger=Explicit,
NotifyOnValidationError=True,
ValidatesOnExceptions=True}"
Style="{StaticResource ValidatingTextBoxStyle}"
u:UpdateSourceTriggerExtender.UpdateSourceOnTextChanged="True" />
The UpdateSourceTrigger
binding property determines when the source property is updated. It can be set to either Default or Explicit. Default causes the source property to be updated when the TextBox
loses focus, and Explicit prevents the source from being updated until the binding’s UpdateSource
method is explicitly called.
As soon as the text changes, the source property named ValidatedString1
is updated, which causes the input to be validated.
18.119.235.79