Search code examples
wpfvalidationmvvmerror-handlingidataerrorinfo

Test for both errors and "other" values


I am currently using MVVM and IDataErrorInfo to validate input in my TextBoxes in a simple data entry app. I currently mark the TextBox with a red background if the user enters a non-number:

<Style TargetType="TextBox">
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="True">
            <Setter Property="Background" Value="Pink"/>
            <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, 
                    Path=(Validation.Errors)[0].ErrorContent}"/>
        </Trigger>
    </Style.Triggers>
</Style>

However, I would like to also give a visual indicator (orange background?) if the entered value is a valid input but "out of spec" or similar. What are my options for multiple "error validation" types?

[using .NET Framework 4.5.1]


Solution

  • I ended up implementing the suggestion by @Will. I inserted a single character at the beginning of each IDataErrorInfo error string (in this case, all I needed was 2 options, so I used "0" and "1"). I created 2 IValueConverters; one for each of the Background and ToolTip properties:

    public class WarningErrorBkgdConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            char firstChar = ((string)value)[0];
            if (firstChar == '0')
            {
                return Brushes.Pink;  // Error
            }
    
            Debug.Assert(firstChar == '1', "CANTHAPPEN: Expecting 1st char of string to be 1, was " + firstChar);
            return Brushes.Gold;  // Warning
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return null;
        }
    }
    
    
    
    public class WarningErrorTextConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            // Just ignore the error code
            return ((string)value).Substring(1);
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return null;
        }
    }
    

    I modified the TextBox Style from the original question to incorporate these converters:

    <local:WarningErrorBkgdConverter x:Key="warningErrorBkgdConverter"/>
    <local:WarningErrorTextConverter x:Key="warningErrorTextConverter"/>
    <Style TargetType="TextBox">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
                <Setter Property="Background" 
                    Value="{Binding RelativeSource={RelativeSource Self}, 
                    Path=(Validation.Errors)[0].ErrorContent, 
                    Converter={StaticResource warningErrorBkgdConverter}}"/>
                <Setter Property="ToolTip" 
                    Value="{Binding RelativeSource={RelativeSource Self},
                    Path=(Validation.Errors)[0].ErrorContent,
                    Converter={StaticResource warningErrorTextConverter}}"/>
            </Trigger>
        </Style.Triggers>
    </Style>
    

    And then in my IDataErrorInfo implementation, a sample usage:

    public string this[string propertyName]
    {
        get
        {
            uint testUint;
    
            switch (propertyName)
            {
                case "YieldPsi1":
                    if (YieldPsi1 == "")
                        return null;
    
                    if (!UInt32.TryParse(YieldPsi1, out testUint))
                        return "0Must be a number";
    
                    if (testUint < 42000)
                        return "1Out of spec";
    
                    return null;
                ...
            }
        }
    }
    

    Everything works well!