Show Validation Error Template on Controls within a UserControl in WPF
Asked Answered
R

1

13

How do you get the WPF error template to appear on a control within a UserControl in WPF?

I have a UserControl containing two Labels, two TextBoxes, and a CheckBox. One of the TextBoxes represents the name of the entity and it is bound to a Name property off of a Model property exposed by my ViewModel, which is the DataContext of my Window. The Model class implements the IDataErrorInfo interface and I have confirmed through Unit Testing that when the Name is blank an error is returned through the property indexer implementation. I have bound to the Dependency Property backing the Name TextBox in my UserControl and when the validation error is encountered the WPF error template places a red border around the entire UserControl rather than just the Name TextBox.

The binding to the name field of the UserControl is as follows.

<vc:MyUserControl ItemName="{Binding Model.Name, ValidatesOnDataErrors=True}" />

A simiplified version of my UserControl and the backing DependencyProperty is as follows.

<UserControl>
    <Grid>
       <TextBox Text="{Binding ItemName}" />
    </Grid>
</UserControl>

public partial class MyUserControl: UserControl
{
    public static readonly DependencyProperty ItemNameProperty = 
        DependencyProperty.Register(
            "ItemName", 
            typeof(string), 
            typeof(MyUserControl), 
            new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
    );

    public string ItemName
    {
        get { return (string)GetValue(ItemNameProperty); }
        set { SetValue(ItemNameProperty, value); }
    }
}

The information I have found relating to this issue thus far has all been in regards to Silverlight or using a converter to not show the red border (which did not make sense to me). This information was all found here on stackoverflow.

Has anyone been able to solve this issue with WPF? Am I overlooking something obvious?

Reclusion answered 19/2, 2013 at 18:49 Comment(2)
Related question here and hereFirebox
Thanks, LPL. I had seen both of these questions during my research on this issue. The accepted answer on the second link is the use of the converter that I referenced in my question. This seems quite wrong to me. The accepted answer in the first question also did not seem to work for me.Reclusion
F
12

The ErrorTemplate for UserControl will be used if bindings to your UserControl use ValidatesOnDataErrors=True. But you can remove the red border with the Validation.ErrorTemplate Attached Property.

All controls within your UserControl will only show a red border if you validate their bindings by implementing IDataErrorInfo for the backing DependencyProperties too.

public class MyUserControl : UserControl, IDataErrorInfo
{
   public static readonly DependencyProperty ItemNameProperty =
       DependencyProperty.Register(
           "ItemName",
           typeof(string),
           typeof(MyUserControl),
           new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
   );

   public string ItemName
   {
      get { return (string)GetValue(ItemNameProperty); }
      set { SetValue(ItemNameProperty, value); }
   }

   public string Error
   {
      get { throw new NotImplementedException(); }
   }

   public string this[string columnName]
   {
      get
      {
         // use a specific validation or ask for UserControl Validation Error 
         return Validation.GetHasError(this) ? "UserControl has Error" : null;
      }
   }
}

and here the simplified XAML

<UserControl Validation.ErrorTemplate="{x:Null}">
   <Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
      <TextBox Text="{Binding ItemName, ValidatesOnDataErrors=True}" />
   </Grid>
</UserControl>

Addition

If you want to differentiate between errors you can get the BindingExpression for your DependencyProperty and check the HasError Property.

BindingExpression be = BindingOperations.GetBindingExpression(this, ItemNameProperty);
return be != null && be.HasError ? "ItemName has Error" : null;
Firebox answered 20/2, 2013 at 13:8 Comment(3)
Thanks again LPL. I had originally attempted a similar change but I was unable to determine which of the TextBoxes internal to the UserControl had an error as calling Validation.GetHasError on the TextBox itself always returned false. With two TextBoxes within my UserControl is there anyway to determine which box actually has the error?Reclusion
Not sure what I did wrong previously but this seems to work for me. I still do not see how to differentiate between errors on the unique TextBoxes but at this point I will only have validation on one of them anyways. Thanks, LPL!Reclusion
I've added a suggestion to differentiate between errors at that. ;)Firebox

© 2022 - 2024 — McMap. All rights reserved.