5

I am trying to perform validation in my WPF application using the solution in Detecting WPF Validation Errors.

public static bool IsValid(DependencyObject parent)
{
    // Validate all the bindings on the parent        
    bool valid = true;
    LocalValueEnumerator localValues = parent.GetLocalValueEnumerator();
    while (localValues.MoveNext())
    {
        LocalValueEntry entry = localValues.Current;
        if (BindingOperations.IsDataBound(parent, entry.Property))
        {
            Binding binding = BindingOperations.GetBinding(parent, entry.Property);
            foreach (ValidationRule rule in binding.ValidationRules)
            {
                ValidationResult result = rule.Validate(parent.GetValue(entry.Property), null);
                if (!result.IsValid)
                {
                    BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property);
                    System.Windows.Controls.Validation.MarkInvalid(expression, new ValidationError(rule, expression, result.ErrorContent, null));
                    valid = false;
                }
            }
        }
    }
    // Validate all the bindings on the children
    for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i)
    {
        DependencyObject child = VisualTreeHelper.GetChild(parent, i);
        if (!IsValid(child))
        {
            valid = false;
        }
    }
    return valid;
}

The problem I am running into is that when I step through the code for a TextBox, I'm not getting the Text property. The only properties I get are "PageHeight", "Instance", and "UndoManagerInstance". Therefore, I can not Validate the rules for the binding on the TextBox.

Does anyone have any idea why I wouldn't be getting the correct properties? Is there another way to force validaton on controls in WPF? I haven't been able to find anyone else who has had this problem.

Update: The TextBoxes I am trying to validate are within a DataTemplate. I found that if I copy one of the TextBoxes and place it directly in the Window, I am able to get the data. Using Woodstock, I saw that the data source for the TextBoxes in the template is "ParentTemplate", but it's "Local" for the TextBox outside of the template.

So, the question now is, how can I get the DependencyProperties for controls inside a DataTemplate?

Community
  • 1
  • 1
a_hardin
  • 4,991
  • 4
  • 32
  • 40

1 Answers1

6

It's more than two years but recently I was struggling with the same problem using the same method.

My solution to that problem is to get all DependencyProperties of object using reflection instead of using GetLocalValueEnumerator which is not working corectlly with DataTemplates.

Code:

    public static bool IsValid(DependencyObject parent)
    {
        // Validate all the bindings on the parent        
        bool valid = true;
        var infos = parent.GetType().GetFields(
                        BindingFlags.Public
                        | BindingFlags.FlattenHierarchy
                        | BindingFlags.Instance
                        | BindingFlags.Static).Where(f => f.FieldType == typeof(DependencyProperty));
        foreach (FieldInfo field in infos)
        {
            var dp = (DependencyProperty)field.GetValue(null);
            if (BindingOperations.IsDataBound(parent, dp))
            {
                Binding binding = BindingOperations.GetBinding(parent, dp);
                foreach (ValidationRule rule in binding.ValidationRules)
                {
                    ValidationResult result = rule.Validate(parent.GetValue(dp), null);
                    if (!result.IsValid)
                    {
                        BindingExpression expression = BindingOperations.GetBindingExpression(parent, dp);
                        Validation.MarkInvalid(expression, new ValidationError(rule, expression, result.ErrorContent, null));
                        valid = false;
                    }
                }
            }
        }
        // Validate all the bindings on the children
        for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i)
        {
            DependencyObject child = VisualTreeHelper.GetChild(parent, i);
            if (!IsValid(child))
            {
                valid = false;
            }
        }
        return valid;
    }

This code works only with Properties owned by the object to extend it for attached properties you can use this code:

    public static List<DependencyProperty> GetAttachedProperties(Object element)
    {
        List<DependencyProperty> attachedProperties = new List<DependencyProperty>();
        System.Windows.Markup.Primitives.MarkupObject markupObject = 
            System.Windows.Markup.Primitives.MarkupWriter.GetMarkupObjectFor(element);
        if (markupObject != null)
        {
            foreach (System.Windows.Markup.Primitives.MarkupProperty mp in markupObject.Properties)
            {
                if (mp.IsAttached)
                {
                    attachedProperties.Add(mp.DependencyProperty);
                }
            }
        }

        return attachedProperties;
    }
bartosz.lipinski
  • 2,627
  • 2
  • 21
  • 34
  • Brilliant solution! How do you handle a `TabControl` with multiple tabs? It seems it only updates the selected tab. – l33t May 14 '19 at 15:49