I have a parent class implementing INotifyPropertyChanged, and the parent class has multiple children. The children have different properties that all call PropertyChanged. I want to add validation, but I really don't want to have to write validations for every child class. The validation rules are supplied from the database, so I would have to eventually pull the validation rules for each child, then check the value against the rules. If I did that, I think that it would have too much redundant code, and I would like to have it placed at the parent level since PropertyChanged triggers on the string value of the value itself.
Is it possible to have the validation method on the parent class so I wouldn't have to write a validation method for every child class? Mind you, the properties in every child class are different.
Below is what I currently have, with validation in the child class.
public Parent : INotifyChanged {
/// <summary>
/// Occurs when a property is changed
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises the <see cref="PropertyChanged"/> for a given
/// property.
/// </summary>
/// <param name="propertyName"></param>
protected void OnPropertyChanged(String propertyName) {
// Get the hanlder
PropertyChangedEventHandler handler = this.PropertyChanged;
// Check that the event handler is not null
if(null != handler) {
// Fire the event
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Child1 Class:
public Child1 : Parent, IDataErrorInfo {
private Dictionary<string, string> m_validationErrors = new Dictionary<string, string>();
private void Validate() {
this.RemoveError("Child1Description");
if(!Regex.IsMatch(Child1Description, "^([a-zA-Z '-]+)$") && !String.IsNullOrWhiteSpace(Description)) {
this.AddError("Child1Description", "Only non-numerics allowed.");
}
}
private void AddError(string columnName, string msg) {
if(!m_validationErrors.ContainsKey(columnName)) {
m_validationErrors.Add(columnName, msg);
}
}
private void RemoveError(string columnName) {
if(m_validationErrors.ContainsKey(columnName)) {
m_validationErrors.Remove(columnName);
}
}
public string Error {
get {
if(m_validationErrors.Count > 0) {
return "Field data is invalid.";
}
else return null;
}
}
public string this[string columnName] {
get {
if(m_validationErrors.ContainsKey(columnName)) {
return m_validationErrors[columnName];
}
else {
return null;
}
}
}
/// <summary>
/// Description of the air entity
/// </summary>
public string Child1Description {
get {
return Child1description;
}
set {
description = value;
Validate();
OnPropertyChanged("Child1Description");
}
}
}
Child2 Class:
public Child2 : Parent, IDataErrorInfo {
private Dictionary<string, string> m_validationErrors = new Dictionary<string, string>();
private void Validate() {
this.RemoveError("Child2Description");
if(!Regex.IsMatch(Child2Description, "^([a-zA-Z '-]+)$") && !String.IsNullOrWhiteSpace(Description)) {
this.AddError("Child2Description", "Only non-numerics allowed.");
}
}
private void AddError(string columnName, string msg) {
if(!m_validationErrors.ContainsKey(columnName)) {
m_validationErrors.Add(columnName, msg);
}
}
private void RemoveError(string columnName) {
if(m_validationErrors.ContainsKey(columnName)) {
m_validationErrors.Remove(columnName);
}
}
public string Error {
get {
if(m_validationErrors.Count > 0) {
return "Field data is invalid.";
}
else return null;
}
}
public string this[string columnName] {
get {
if(m_validationErrors.ContainsKey(columnName)) {
return m_validationErrors[columnName];
}
else {
return null;
}
}
}
/// <summary>
/// Description of the air entity
/// </summary>
public string Child2Description {
get {
return Child2description;
}
set {
description = value;
Validate();
OnPropertyChanged("Child2Description");
}
}
}
I ended up passing the property name, the property value and the list of rules I wanted to use to validate. Storing the Validate method in the parent, so it would run regardless if which child used it, it would use its own rules, but keep the same validation error message dictionary.
protected void Validate(string propertyName, string propertyValue, List<ValidRule> validRules) {
string temp = propertyValue.ToString();
this.RemoveError(propertyName);
if(propertyName.Equals("Description")) {
foreach(ValidRule validRule in validRules) {
if(!Regex.IsMatch(propertyValue, validRule.Rule) && !String.IsNullOrWhiteSpace(propertyValue)) {
this.AddError(propertyName, validRule.ErrorMessage);
break;
}
}
}
}