Search code examples
c#wpfvalidationdata-bindingvalidationrules

How to pass information from the view model to a validation rule


I'm quite new to programming C# and WPF. I have two TextBoxes where I want to validate user input with validation rules:

<TextBox
    x:Name="TextBox1">
    <TextBox.Text>
        <Binding
            NotifyOnValidationError="True"
            Path="String1"
            UpdateSourceTrigger="PropertyChanged"
            ValidatesOnDataErrors="True">
            <Binding.ValidationRules>
                <local:ValidationRule1 ValidatesOnTargetUpdated="True" />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>
<TextBox
    x:Name="TextBox2">
    <TextBox.Text>
        <Binding
            NotifyOnValidationError="True"
            Path="Int1"
            UpdateSourceTrigger="PropertyChanged"
            ValidatesOnDataErrors="True">
            <Binding.ValidationRules>
                <local:ValidationRule2 ValidatesOnTargetUpdated="True" />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

In TextBox1 the user can insert a string, in TextBox2 an int. ValidationRule1 only checks that TextBox1 is not null. With ValidationRule2 I want to check if the combination of both TextBoxes is already existing in my Database. Therefore I need to pass the string from TextBox1 to the ValidationRule2. Is there a possibility to do this?

I tried as described in this article with Wrapper and BindingBroxy:

Wrapper:

public class Wrapper : DependencyObject
{
    public static readonly DependencyProperty NewSitzeinteilungSachnummerProperty = DependencyProperty.Register("NewSitzeinteilungSachnummer", typeof(string), typeof(Wrapper));  //, new FrameworkPropertyMetadata());

    public string NewSitzeinteilungSachnummer
    {
        get { return (string)GetValue(NewSitzeinteilungSachnummerProperty); }
        set { SetValue(NewSitzeinteilungSachnummerProperty, value); }
    }
}

BindingProxy:

public class BindingProxy : Freezable
{
    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new PropertyMetadata());
}

Adjusted xaml:

<TextBox
    x:Name="TextBox2">
    <TextBox.Resources>
        <local2:BindingProxy x:Key="proxy" Data="{Binding}"/>
    </TextBox.Resources>
    <TextBox.Text>
        <Binding
            NotifyOnValidationError="True"
            Path="Int1"
            UpdateSourceTrigger="PropertyChanged"
            ValidatesOnDataErrors="True">
            <Binding.ValidationRules>
                <local1:ValidationRule2 ValidatesOnTargetUpdated="True" >
                    <local1:ValidationRule2.Wrapper>
                        <local1:Wrapper String1="{Binding String1, Source={StaticResource proxy}}"/>
                    </local1:ValidationRule2.Wrapper>
                </local1:ValidationRule2>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

The String1 property in the ValidationRule2 is always 'null'. What am I doing wrong?


Solution

  • Your solution is too complex. You don't need to create a wrapper. I recommend use standard Binding with validation on model like this:

     <TextBox Text="{Binding User.Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />
     <TextBox Text="{Binding User.Age, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />
    

    and next implement INotifyDataErrorInfo on User class.

    Let me know if you need more help.