-1

In my WPF MVVM app I have a TextBox which has bound a validation rule. In validation rule class I have below property:

public bool CanBeValidated { get; set; } = false;

Then in the view my TextBox has below validation rule bound (I only put the relevant part):

        <TextBox.Text>
            <Binding Path="myPath"
                     UpdateSourceTrigger="PropertyChanged"
                     ValidatesOnDataErrors="True">
                <Binding.ValidationRules>
                    <vRules:RootPathValidationRule 
                        ValidatesOnTargetUpdated="True" 
                        CanBeValidated="{Binding Path=ValidationEnabled}"/>
                </Binding.ValidationRules>
            </Binding>                         
        </TextBox.Text> 

In my view model the property is defined as below:

public bool ValidationEnabled
{ 
     get { return _isValidationEnabled; }
     set { this._isValidationEnabled = value; OnPropertyChanged(); }
}

So I receive below compilation error:

A 'Binding' cannot be set on the 'CanBeValidated' property of type 'MyPathValidatorRule'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.

For first time when TextBox is loaded I want to avoid validation rule to fire until user edits it and avoid throwing a validation error since TextBox is empty.

Once user edits the TextBox, I would like to enable validation rule by performing a simple this.ValidationEnabled = true from view model.

How can I achieve this without using dependency properties? Is it possible?

Willy
  • 9,848
  • 22
  • 141
  • 284

1 Answers1

0

You could create a wrapper class that derives from DependencyObject and exposes a dependency property. Then you add a CLR property to the ValidationRule class that returns an instance of this wrapper type:

public class RootPathValidationRule : ValidationRule
{
    public Wrapper Wrapper { get; set; }

    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        bool canBeValidated = Wrapper?.CanBeValidated == true;
        ...
    }
}

public class Wrapper : DependencyObject
{
    public static readonly DependencyProperty CanBeValidatedProperty =
         DependencyProperty.Register(nameof(CanBeValidated), typeof(bool),
         typeof(Wrapper));

    public bool CanBeValidated
    {
        get { return (bool)GetValue(CanBeValidatedProperty); }
        set { SetValue(CanBeValidatedProperty, value); }
    }
}

Finally, you'll also need a binding proxy object that captures the DataContext where the source property is defined:

public class BindingProxy : System.Windows.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(null));
}

XAML:

<TextBox>
    <TextBox.Resources>
        <vRules:BindingProxy x:Key="proxy" Data="{Binding}"/>
    </TextBox.Resources>
    <TextBox.Text>
        <Binding Path="myPath"
                     UpdateSourceTrigger="PropertyChanged"
                     ValidatesOnDataErrors="True">
            <Binding.ValidationRules>
                <vRules:RootPathValidationRule ValidatesOnTargetUpdated="True">
                    <vRules:RootPathValidationRule.Wrapper>
                        <vRules:Wrapper CanBeValidated="{Binding Data.ValidationEnabled, 
                            Source={StaticResource proxy}}"/>
                    </vRules:RootPathValidationRule.Wrapper>
                </vRules:RootPathValidationRule>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Please refer to this article for details.

mm8
  • 163,881
  • 10
  • 57
  • 88