Search code examples
wpfdependencyobjectvalidationrule

ValidationRule and DependencyObject


I need to send Binding values to a ValidationRule. I am using a DependencyObject, but the values are always null.

Here is my DependencyObject

public class MyDependencyObject  : DependencyObject
{
    public string BindingValue
    {
        get { return (string)GetValue(BindingValueProperty); }
        set { SetValue(BindingValueProperty, value); }
    }
    public static readonly DependencyProperty BindingValueProperty =
        DependencyProperty.Register("BindingValue", typeof(string), typeof(MyDependencyObject), new UIPropertyMetadata(null));    
}

Here is my ValidationRule:

public class MyTextBoxValidationRule : ValidationRule
{
    private MyDependencyObject _TxtVal;
    public MyDependencyObject TxtVal
    {
        get { return _TxtVal; }
        set { _TxtVal = value; }
    }
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        //Validation Logic
    }   
}

Here is the XAML:

<TextBox >
    <TextBox.Text>
        <Binding Path="DataUserTypes" 
            NotifyOnValidationError="True" 
            ValidatesOnDataErrors="True" 
            Mode="TwoWay" 
            UpdateSourceTrigger="PropertyChanged"  
            NotifyOnSourceUpdated="True" 
            NotifyOnTargetUpdated="True"
            Delay="100">
            <Binding.ValidationRules>
                <local:MyTextBoxValidationRule ValidatesOnTargetUpdated="True"  >
                    <local:MyTextBoxValidationRule.TxtVal >
                        <local:MyDependencyObject   
                            TxtVal="{Binding Path=ValidateValue}" />
                    </local:MyTextBoxValidationRule.TxtVal>
                </local:MyTextBoxValidationRule>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

When I step through Validate in MyTextBoxValidationRule TxtVal.BindingValue is always null. I have no idea why

edit:

I changed DisplayValue to DataUserTypes as there seems to be some confusion.

<Binding Path="DataUserTypes" 

That is the binding for the Text value of the textbox. I need to validate DataUserTypes based on the property ValidateValue. I am trying to do that with the DependencyObject TxtVal.

I also fixed a copy and paste type. There was a field called TextValID that should have been TxtVal. Sorry about that.


Solution

  • <local:MyTextBoxValidationRule ValidatesOnTargetUpdated="True"  >
        <local:MyTextBoxValidationRule.TxtVal >
            <local:MyDependencyObject   
                BindingValue="{Binding ValidateValue, PresentationTraceSources.TraceLevel=High}" 
                />
        </local:MyTextBoxValidationRule.TxtVal>
    </local:MyTextBoxValidationRule>
    

    PresentationTraceSources.TraceLevel=High produces the following trace output in the VS Output pane:

    System.Windows.Data Warning: 56 : Created BindingExpression (hash=24230272) for Binding (hash=28316044)
    System.Windows.Data Warning: 58 :   Path: 'ValidateValue'
    System.Windows.Data Warning: 60 : BindingExpression (hash=24230272): Default mode resolved to OneWay
    System.Windows.Data Warning: 61 : BindingExpression (hash=24230272): Default update trigger resolved to PropertyChanged
    System.Windows.Data Warning: 62 : BindingExpression (hash=24230272): Attach to Lines.MyDependencyObject.BindingValue (hash=37689768)
    System.Windows.Data Warning: 64 : BindingExpression (hash=24230272): Use Framework mentor <null>
    System.Windows.Data Warning: 67 : BindingExpression (hash=24230272): Resolving source 
    System.Windows.Data Warning: 69 : BindingExpression (hash=24230272): Framework mentor not found
    System.Windows.Data Warning: 65 : BindingExpression (hash=24230272): Resolve source deferred
    'Lines.exe' (CLR v4.0.30319: Lines.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\PresentationFramework.Aero\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.Aero.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
    System.Windows.Data Warning: 67 : BindingExpression (hash=24230272): Resolving source 
    System.Windows.Data Warning: 69 : BindingExpression (hash=24230272): Framework mentor not found
    

    Long story short, that instance of local:MyDependencyObject has no place to inherit a DataContext from, because its parent isn't in the visual tree.

    You could use a BindingProxy to work around that, but what are you binding to? ValidateValue must be a property of something; but of what? If it's a property of the parent viewmodel, this will do it:

    <TextBox.Resources>
        <local:BindingProxy
            x:Key="ViewmodelProxy"
            Data="{Binding}"
            />
    </TextBox.Resources>
    <TextBox.Text>
        <Binding Path="DataUserTypes" 
            NotifyOnValidationError="True" 
            ValidatesOnDataErrors="True" 
            Mode="TwoWay" 
            UpdateSourceTrigger="PropertyChanged"  
            NotifyOnSourceUpdated="True" 
            NotifyOnTargetUpdated="True"
            Delay="100">
            <Binding.ValidationRules>
                <local:MyTextBoxValidationRule ValidatesOnTargetUpdated="True"  >
                    <local:MyTextBoxValidationRule.TxtVal>
                        <local:MyDependencyObject   
                            BindingValue="{Binding Data.ValidateValue, Source={StaticResource ViewmodelProxy}}" 
                            />
                    </local:MyTextBoxValidationRule.TxtVal>
                </local:MyTextBoxValidationRule>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
    

    Binding proxy:

    public class BindingProxy : Freezable
    {
        #region Overrides of Freezable
    
        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }
    
        #endregion
    
        public object Data
        {
            get { return (object)GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }
    
        // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DataProperty =
            DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
    }