Validating a TextBox as the User Types

,

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.


LISTING 26.1. UpdateSourceTriggerExtender Class


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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.60.20