Binding ValidationRule from ViewModel

Viewed 102

I need to use the list 'lFx' from my ModelView

<TextBox Grid.Row="2" Grid.Column="0"
         Style="{StaticResource StyleTxtErr}"
         Validation.ErrorTemplate="{StaticResource ValidationGrp}"
         >
    <TextBox.Text>
        <Binding Path="sGrp" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
         <Binding.ValidationRules>
                <local:cDataGrpRule lFx="{Binding lFx}"/>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Actually, this code shows me this error at line '<local:cDataGrpRule ...'
A binding cannot be set to lFx, ...
A binding can only be set to DependencyProperty of a DependenxyObject

How can I initialize my ValidationRule Object with an object set in my ViewModel ?

Thanks in advance. Eric.

1 Answers

Since you can only bind to a dependency property, as the error message explains, yould create a wrapper class that derives from DependencyObject and exposes a dependency property.

You also need a proxy object that captures the current DataContext:

public class BindingProxy : Freezable
{
    protected override Freezable CreateInstanceCore() => 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));
}

Then you add a CLR property to the ValidationRule class that returns an instance of this wrapper type:

public class cDataGrpRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        //your validation logic...
        return ValidationResult.ValidResult;
    }

    public Wrapper Wrapper { get; set; }
}

public class Wrapper : DependencyObject
{
    public static readonly DependencyProperty lFxProperty =
         DependencyProperty.Register("lFx", typeof(object), typeof(Wrapper));

    public object lFx
    {
        get { return GetValue(lFxProperty); }
        set { SetValue(lFxProperty, value); }
    }
}

You probably want to consider renaming the property to comply with the C# naming conventions.

XAML:

<TextBox Grid.Row="2" Grid.Column="0"
         Style="{StaticResource StyleTxtErr}"
         Validation.ErrorTemplate="{StaticResource ValidationGrp}">
    <TextBox.Resources>
        <local:BindingProxy x:Key="proxy" Data="{Binding}"/>
    </TextBox.Resources>
    <TextBox.Text>
        <Binding Path="sGrp" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <local:cDataGrpRule>
                    <local:cDataGrpRule.Wrapper>
                        <local:Wrapper lFx="{Binding Data.lFx, Source={StaticResource proxy}}"/>
                    </local:cDataGrpRule.Wrapper>
                </local:cDataGrpRule>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Please refer to the this article for more information and a full example.

Related