WinForms / .NETFramework: Format DataGridViewCell according to ValidationResult
07:20 31 Mar 2026

Consider a small winforms application, that has a list of custom objects bound to a DataGridView.

The data class:

public class MyAwesomeClass : IDataErrorInfo, INotifyPropertyChanged{
    string _Name;
    public string Name { get { return _Name; } set { if(Name is null || !Name.Equals(value)) { _Name = value; Changed(nameof(Name)); } } }

    int _MyVal;
    [CustomValidation(typeof(MyAwesomeValidator), nameof(MyAwesomeValidator.ValidateIntVal))]
    public int MyVal { get { return _MyVal; } set { if(!MyVal.Equals(value)) { _MyVal = value; Changed(nameof(MyVal)); } } }

    private void Changed(string property) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property)); }
    
    //Nicked from: https://reza-aghaei.com/dataannotations-validation-attributes-in-windows-forms/
    [Browsable(false)]
    public string this[string property]
    {
        get
        {
            var propertyDescriptor = TypeDescriptor.GetProperties(this)[property];
            if (propertyDescriptor == null)
                return string.Empty;

            var results = new List();
            var result = Validator.TryValidateProperty(
                                      propertyDescriptor.GetValue(this),
                                      new ValidationContext(this, null, null)
                                      { MemberName = property },
                                      results);
            if (!result)
                return results.First().ErrorMessage;
            return string.Empty;
        }
    }

    [Browsable(false)]
    public string Error
    {
        get
        {
            var results = new List();
            var result = Validator.TryValidateObject(this,
                new ValidationContext(this, null, null), results, true);
            if (!result)
                return string.Join(Environment.NewLine, results.Select(x => x.ErrorMessage));
            else
                return null;
        }
    }

public static IEnumerable DummyData {get{return new []{
    new MyAwesomeClass("a",-2),
new MyAwesomeClass("b",3),
new MyAwesomeClass("c",8)
};}}

}

The custom ValidationResult:

public class CustomValidationResult : ValidationResult {
    public enum ResultState { Warning, Error}
    
    public ResultState State {get; private set;}
    
    public CustomValidationResult(string errorMessage, ResultState state):base(errorMessage) {
    ResultState = state;
}
    public static ValidationResult Success {get{return ValidationResult.Success;}}
}

The validator:

public class MyAwesomeValidator{
    public static ValidationResult ValidateIntVal(int value, ValidationContext Context) {
    if(value < 0) return new CustomValidationResult("The value cannot be smaller than 0", CustomValidationResult.ResultState.Error);
    if(value<5) return new CustomValidationResult("The value is a bit small, isn't it?", CustomValidationResult.ResultState.Warning);
    return CustomValidationResult.Success;
}
}

A class responsible for styling cells with errors:

   class DataGridViewCellFormatter
   {
       DataGridView Dgv;
       public DataGridViewCellFormatter(DataGridView dgv)
       {
           Dgv = dgv;
           Dgv.CellFormatting += Dgv_CellFormatting;
       }


       private void Dgv_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
       {
           DataGridViewCell cell = Dgv.Rows[e.RowIndex].Cells[e.ColumnIndex];

           if (!string.IsNullOrWhiteSpace(cell.ErrorText))
           {
               cell.Style.BackColor = Color.Red;
           }
           else
           {
               cell.Style.BackColor = cell.OwningColumn.DefaultCellStyle.BackColor;
           }


       }

   }

The form (Excerpt):

public partial class frm_DemoForm:Form{
    BindingList Data;
DataGridViewCellFormatter coloring;
    public frm_DemoForm(){
        Data = new BindingList(MyAwesomeClass.DummyData.ToList());
        
        InitializeComponent();
        
        coloring = new DataGridViewCellFormatter(dataGridView1);

        dataGridView1.DataSource = Data;
}
}

Now, if I wrote everything correctly, the DataGridView should display 3 rows (a, b and c) and show errors on the first two (the first value is below zero and thus an error and the second is in the "still a bit small"-range and thus a warning). The DataGridViewCellFormatter will check each cell's ErrorText and color the cell accordingly.

BUT.

This form of validation allows only a binary formatting: Error or no error. If I want to colour the cell in orange if it is only a warning, I need to prefix or suffix the error text with "[Warning]" and "[Error]" (That i can probably skip and take everything that has no prefix as an error) to be able to catch the differences in severity.

BUT.

Since I already have such a nice CustomValidationResult that provides the granualrity I want via the State property, I was wondering if there is a way to elegantly hook the DataGridView up to the actual ValidationResult object(s) and act on them rather than on the ErrorText of each cell which I don't want to spam with a "[Warning]" token.

c# winforms validation datagridview .net-4.8