4

I was dabbling in WPF and noticed a peculiarity that I had never seen before. In the example below I have two text boxes that are bound to the same DP in the code-behind.

Code-behind:

public partial class MainWindow : Window
{
    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(Window), new FrameworkPropertyMetadata("Hello"));

    public MainWindow()
    {
        InitializeComponent();
    }


}

And the XAML:

    <TextBox>
        <TextBox.Text>
            <Binding RelativeSource = "{RelativeSource Mode=FindAncestor, AncestorType=Window}" Path="Text" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
                <Binding.ValidationRules>
                    <utils:RestrictInputValidator Restriction="IntegersOnly" ValidatesOnTargetUpdated="True"/>
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>
    <TextBox Name="TextBox" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Mode=TwoWay, Path=Text, UpdateSourceTrigger=PropertyChanged}"/>

I noticed that when I type something in the TextBox that contains the IntegerOnly validation which fails the validation (in this case anything that is not an integer), the underlying Text variable does not update. Is this a default behavior? Why does it do this? Is it possible to override?

Error example

denis morozov
  • 6,236
  • 3
  • 30
  • 45
Alexander Ventura
  • 1,150
  • 1
  • 10
  • 32
  • Why would you add validation if you're going to override it? – Bob. Jul 11 '13 at 20:35
  • 1
    I wouldn't, you are right. But there might be an obscure use case in which I need to look at the dirty data in the code-behind. – Alexander Ventura Jul 11 '13 at 20:36
  • Where do you need your validation? Do you need it for users? Or do you just need it for final input? If you need it for latter, just add a button and pop up a `MessageBox` that says which field is wrong and not continue until all validation passes. Have a look at [this](http://stackoverflow.com/a/1268648/1466627). – Bob. Jul 11 '13 at 20:38

1 Answers1

0

Set the ValidationStep of the ValidationRule to CommitedValue, which will run the validation after the value has been committed to the source (msdn).

<utils:RestrictInputValidator Restriction="IntegersOnly" ValidationStep="CommitedValue" ValidatesOnTargetUpdated="True"/>

edit: With validationstep set to CommitedValue or UpdatedValue, a BindingExpression is sent to the Validate-method and not the actual value. I don't know if there is another way to get the value of a BindingExpression, but I made an extension method that will get it:

public static class BindingExtensions
{
    public static T Evaluate<T>(this BindingExpression bindingExpr)
    {
        Type resolvedType = bindingExpr.ResolvedSource.GetType();
        PropertyInfo prop = resolvedType.GetProperty(
            bindingExpr.ResolvedSourcePropertyName);
        return (T)prop.GetValue(bindingExpr.ResolvedSource);
    }
}
Lorentz Vedeler
  • 5,101
  • 2
  • 29
  • 40
  • Although I thought this would work, this makes my code throw an `InvalidCastException` even when I change my Restriction to `Restriction="None"` (which essentially means do not validate). – Alexander Ventura Jul 11 '13 at 21:15