Search code examples
c#wpfvalidationxamlinotifydataerrorinfo

Display error validation on another control


I have a TextBlock and a CheckBox, as such:

<StackPanel >
    <TextBlock Text="Colors"/>
    <CheckBox Content="Blue" IsChecked="{Binding Model.Blue, ValidatesOnNotifyDataErrors=False}"/>
</StackPanel>

In my model I am implementing INotifyDataErrorInfo and validating whether the checkbox is checked or not. If it's not checked, I'm treating it as an error:

public class MyModel : INotifyPropertyChanged, INotifyDataErrorInfo
{
    [CustomValidation(typeof(MyModel), "CheckBoxRequired")]
    public bool Blue
    {
        get { return _blue; }
        set { _blue = value; RaisePropertyChanged(nameof(Blue)); }
    }

    public static ValidationResult CheckBoxRequired(object obj, ValidationContext context)
    {
        var model = (MyModel)context.ObjectInstance;
        if (model.Blue == false)
            return new ValidationResult("Blue required", new string[] { "Blue" });
        else
            return ValidationResult.Success;
    }

    //...
    //INotifyPropertyChanged & INotifyDataErrorInfo implementations omitted
}

When I have ValidatesOnNotifyDataErrors set to true, it correctly displays a red box around the CheckBox. It looks like this:

enter image description here

I do not want the red checkbox to appear. To do this, I explictly set ValidatesOnNotifyDataErrors to false. This works fine.

What I want to do when there is an error is to display an error on the TextBlock, such as changing the font color of the TextBlock. How can the TextBlock be aware of any errors present on the CheckBox and what is the best way of going about it?

My intended outcome would be something like this:

enter image description here


Solution

  • First of all setting ValidatesOnNotifyDataErrorsis not the right way to get rid of the red border. It will cause your data to not be validated at all. What you want is this:

    <CheckBox Content="Blue" IsChecked="{Binding Model.Blue, ValidatesOnNotifyDataErrors=True}" Validation.ErrorTemplate="{x:Null}"/>
    

    Secondy, to get the desired result, I would use this kind of approach. You can use a Trigger to know if there is an Error in your CheckBox (the ErrorsChanged event and the HasError property should be useful here) and set the text color of your TextControl.

    Here is the code to accomplish this:

    <TextBlock Text="Color">
        <TextBlock.Style>
            <Style TargetType="TextBlock">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding ElementName=checkBox, Path=(Validation.HasError)}" Value="True">
                        <Setter Property="Foreground" Value="Red" />
                        <Setter Property="ToolTip" Value="{Binding ElementName=checkBox, Path=(Validation.Errors).CurrentItem.ErrorContent}" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBlock.Style>
    </TextBlock>
    <CheckBox x:Name="checkBox"
              Margin="4,0"
              Content="Blue"
              IsChecked="{Binding Model.Blue}"
              Validation.ErrorTemplate="{x:Null}" />