This is what I would do.
The DataGrid control on Windows Form can read errors from objects implementing IDataErrorInfo
As MSDN says.
IDataErrorInfo Provides the functionality to offer custom error information that a user interface can bind to.
Your POCO object inside the data source collection for the grid, should implement IDataErrorInfo
let's say something like this:
public class MyEntity : IDataErrorInfo
{
public string this[string columnName]
{
get
{
// here you can validate each property of your class (POCO object)
var result = string.Join(Environment.NewLine, Validator.Validate(this, columnName).Select(x => x.ErrorMessage));
return result;
}
}
public string Error
{
get
{
// here you can errors related to the whole object (ex: Password, and PasswordConfirmation do not match)
return string.Join(Environment.NewLine, Validator.Validate(this)
.Select(x => x.ErrorMessage));
}
}
public Boolean IsValid
{
get { return string.IsNullOrEmpty(Error); }
}
}
Then you could use some validation technique to set up your validation rules.
I like to use DataAnnotation to implement my validation logic.
So, let's say your class has a property (name) that cannot be null you class could be:
public class MyEntity : IDataErrorInfo
{
[Required]
public string Name { get; set; }
public string this[string columnName]
{
get
{
// here you can validate each property of your class (POCO object)
var result = string.Join(Environment.NewLine, Validator.Validate(this, columnName).Select(x => x.ErrorMessage));
return result;
}
}
public string Error
{
get
{
// here you can validate errors related to the whole object (ex: Password, and PasswordConfirmation do not match)
return string.Join(Environment.NewLine, Validator.Validate(this)
.Select(x => x.ErrorMessage)
.Union(ModelError.Select(m => m.Value)));
}
}
public Boolean IsValid
{
get { return string.IsNullOrEmpty(Error); }
}
}
Then if you use a validator like this
public class Validator : IValidator
{
public IEnumerable<ErrorInfo> Validate(object instance)
{
IEnumerable<ErrorInfo> errores = from property in instance.GetType().GetProperties()
from error in GetValidationErrors(instance, property)
select error;
if (!errores.Any())
{
errores = from val in instance.GetAttributes<ValidationAttribute>(true)
where
val.GetValidationResult(null, new ValidationContext(instance, null, null)) !=
ValidationResult.Success
select
new ErrorInfo(null,
val.GetValidationResult(null, new ValidationContext(instance, null, null)).ErrorMessage,
instance);
}
return errores;
}
public IEnumerable<ErrorInfo> Validate(object instance, string propertyName)
{
PropertyInfo property = instance.GetType().GetProperty(propertyName);
return GetValidationErrors(instance, property);
}
private IEnumerable<ErrorInfo> GetValidationErrors(object instance, PropertyInfo property)
{
var context = new ValidationContext(instance, null, null);
context.MemberName = property.Name;
IEnumerable<ErrorInfo> validators = from attribute in property.GetAttributes<ValidationAttribute>(true)
where
attribute.GetValidationResult(property.GetValue(instance, null), context) !=
ValidationResult.Success
select new ErrorInfo(
property.Name,
attribute.FormatErrorMessage(property.Name),
instance
);
return validators.OfType<ErrorInfo>();
}
}
The errors will appear on the grid per cell or per row depending on the error.
Notice that you should also implement INotifyPropertyChanged
if you are planning to in-line editing your objects.
This approach has the benefit of decoupling the validation logic from the user interface.