views:

60

answers:

1

Situation: Many times with WPF, we use INotifyPropertyChanged and IDataErrorInfo to enable binding and validation on our data objects. I've got a lot of properties that look like this:

public SomeObject SomeData
{
    get { return _SomeData; }
    set { _SomeData = value; OnPropertyChanged("SomeData"); }
}

Of course, I have an appropriate overridden IDataErrorInfo.this[] in my class to do validation.

Question: In a binding situation, when does the validation code get executed? When is the property set? When is the setter code executed? What if the validation fails?

For example:

  1. User enters new data.
  2. Binding writes data to property.
  3. Property set method is executed.
  4. Binding checks this[] for validation.
  5. If the data is invalid, the binding sets the property back to the old value.
  6. Property set method is executed again.

This is important if you are adding "hooks" into the set method, like:

public string PathToFile
{
    get { return _PathToFile; }
    set
    {
        if (_PathToFile != value &&      // prevent unnecessary actions
            OnPathToFileChanging(value)) // allow subclasses to do something or stop the setter
        {
            _PathToFile = value;
            OnPathToFileChanged();  // allow subclasses to do something afterwards
            OnPropertyChanged("PathToFile");
        }
    }
}
+1  A: 

If you want fine-grained control over the timing of validation, you can have it:

private Dictionary<string, string> Errors = new Dictionary<string, string>();

private object _MyProperty;

public object MyProperty
{
   get { return _MyProperty; }
   set
   {
      Errors["MyProperty"] = null;
      if (value == _MyProperty)
      {
         return;
      }
      ValidateMyProperty(value);  // may set Errors["MyProperty"]
      if (Errors["MyProperty"] == null)
      {
         _MyProperty = value;
         OnPropertyChanged("MyProperty");
      }
   }
}

public string this[string propertyName]
{
   return Errors[propertyName];
}

No matter when data error information is requested and who's requesting it, it always returns the property's validation status as of the last time something tried to set the property.

Note that if you work at it, you can encapsulate the logic thusly:

public object MyProperty
{
   set { _MyProperty = Validate("MyProperty", value, _MyProperty); }
}

private Dictionary<string, Func<object, string>> ValidationFunctions;

private object Validate(string propertyName, object value, object field)
{
   Errors[propertyName] = null;
   if (value == field)
   {
      return;
   }
   if (!ValidationFunctions.ContainsKey(propertyName))
   {
      return value;
   }
   Errors[propertyName] = ValidationFunctions[propertyName](value);
   return (Errors[propertyName] == null) 
      ? value 
      : field;
   }
}
Robert Rossney
Thank you for taking the time to think up this way of taking control of validation. Is this something you have used in real-world programs?
Benny Jobigan
Yes, though for a number of reasons specific to my application my logic's a lot more involved than the above.
Robert Rossney