All my model classes inherit from a base class that implements INotifyPropertyChanged and IDataErrorInfo. OnValidate returns validation errors from the annotations. Plenty of examples how to do this- just google IDataErrorInfo Annotations.
public class ModelBase : INotifyPropertyChanged, IDataErrorInfo
{
#region " INotifyPropertyChanged "
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region " IDataErrorInfo "
public string this[string propertyName]
{
get
{
return OnValidate(propertyName);
}
}
/// <summary>
/// Validates current instance properties using Data Annotations.
/// </summary>
/// <param name=“propertyName”>This instance property to validate.</param>
/// <returns>Relevant error string on validation failure or <see cref=“System.String.Empty”/> on validation success.</returns>
private string OnValidate(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
throw new ArgumentException("Property may not be null or empty", propertyName);
string error = string.Empty;
var value = this.GetType().GetProperty(propertyName).GetValue(this, null);
var results = new List<ValidationResult>();
var context = new ValidationContext(this, null, null) { MemberName = propertyName };
var result = Validator.TryValidateProperty(value, context, results);
if (!result)
{
var validationResult = results.First();
error = validationResult.ErrorMessage;
}
return error;
}
public string Error
{
get
{
throw new NotSupportedException("IDataErrorInfo.Error is not supported, use IDataErrorInfo.this[propertyName] instead.");
}
}
#endregion
}
The implementation in my model classes looks more or less like this:
public class MyClass: ModelBase
{
private string someProperty;
[Required]
[StringLength(255, ErrorMessage = "Some Property maximum length is 255 characters")]
public string SomeProperty
{
get { return someProperty; }
set
{
if (value == someProperty) return;
someProperty= value;
RaisePropertyChanged("SomeProperty");
}
}
}
And then in my view models I also implement IDataErrorInfo. I first perform the view model validations, then if there are no errors I go to the model's IDataErrorInfo's implementations and check for errors there. The basic idea came from http://stackoverflow.com/questions/13113779/implementing-idataerrorinfo-in-a-view-model
public class MyViewModel: IDataErrorInfo
{
private MyClass _myClass { get; set; }
public string SomeProperty
{
get { return _myClass.SomeProperty; }
set
{
_myClass.SomeProperty= value;
RaisePropertyChanged("SomeProperty");
}
}
#region " Implementation of IDataErrorInfo "
public string Error
{
get { throw new NotSupportedException("Not supported"); }
}
// Put business rule validation here
public string this[string propertyName]
{
get
{
if (propertyName.Equals("SomeProperty"))
{
if (SomeProperty == "FooBar")
return "SomeProperty cannot be FooBar.";
}
// If we get to here, ViewModel validation passed. So do the Model validations-
return _selectedRacer[propertyName];
}
}
#endregion
}
And finally, the binding in the view looks like this:
<TextBox Text="{Binding SomeProperty, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True}">
No comments:
Post a Comment