Skip to main content

Using IDataErrorInfo for Validation

I read an article on CodeProject titled Total View Validation where the author complains that IDataErrorInfo is inadequate for WPF validation.  The assumption he makes is that all the validation code needs to go into the IDataErrorInfo.this[string] property as such:

[csharp]
public string this[string name]
{
get
{
string result = null;
if (name == "Age")
{
if (this.age < 0 || this.age > 150)
{
result = "Age must not be less than 0 or greater than 150.";
}
}
return result;
}
}
[/csharp]

This should not be the way that IDataErrorInfo is used as it puts business logic in the model, as the author points out. His solution though was to create another mechanism to notifiy the user of validation errors and to not use IDataErrorInfo at all.

A solution I would propose though would be to follow how DataTables and DataTables use IDataErrorInfo by implementing methods to set and clear the objects error information:

[csharp]
public class SomeClass : IDataErrorInfo
{

public string Name { get; set; }

public string Age { get; set; }

#region Data Error Info

private Dictionary<string, string> _propertyErrors;

private void InitDataErrorInfo()
{
var properties = this.GetType().GetProperties();

_propertyErrors = new Dictionary<string, string>
{
// This will act as an overall error message for the entire object.
{this.GetHashCode().ToString(CultureInfo.InvariantCulture), string.Empty}
};


foreach (var propertyInfo in properties)
_propertyErrors.Add(propertyInfo.Name, string.Empty);
}

public void ClearDataErrorInfo()
{
foreach (var property in _propertyErrors.Keys)
_propertyErrors[property] = string.Empty;
}

public void ClearDataErrorInfo(string propertyName)
{
AssertThisHasPropertyWithName(propertyName);
_propertyErrors[propertyName] = string.Empty;
}

public void SetError(string error)
{
SetError(this.GetHashCode().ToString(CultureInfo.InvariantCulture), error);
}

public void SetError(string propertyName, string error)
{
AssertThisHasPropertyWithName(propertyName);
_propertyErrors[propertyName] = string.Format("{0}{1}{2}", _propertyErrors[propertyName]
, Environment.NewLine, error);
}

public string this[string propertyName]
{
get
{
AssertThisHasPropertyWithName(propertyName);
return _propertyErrors[propertyName];
}

}

public string Error
{
get
{
var errors = new StringBuilder();
foreach (
var propertyError in
_propertyErrors.Where(propertyError => !string.IsNullOrEmpty(propertyError.Value)))
errors.AppendLine(propertyError.Value);

return errors.ToString().Trim();
}
}

protected void AssertThisHasPropertyWithName(string propertyName)
{
if (!_propertyErrors.ContainsKey(propertyName))
{
throw new ArgumentException(string.Format("No property named {0} on {1}."
, propertyName, this.GetType().FullName));
}
}

#endregion
}
[/csharp]

Note that there is no validation here, only reporting if the object has errors. Using this takes advantage of the already existing validation notification built into WPF as well as WinForms.

Comments

Popular posts from this blog

C# Spirograph Point Generators

Spirograph's  are cool.  See here and here . I put together three ways to generate points for a Spirograph, first using a Brute Force straight generate the points, second using a Parallel.For and third using LINQ.

FileSystemWatcher With the BlockingCollection

While working with the FileSystemWatcher I found that if too many files were created the built in buffer will overflowed and files will be skipped.  After much research I found out about the Producer-Consumer Problem .  Then I found that .Net 4 has the BlockingCollection which helps solve the issue.  But how to use it with the FileSystemWatcher? On StackOverflow I found  Making PLINQ and BlockingCollection work together .  I'm not so interested in the PLINQ issue but this is a great example of using The BlockingCollection with FileSystemWatcher. [csharp] using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; namespace ConsoleApplication4 {     public class Program     {         private const string Folder = "C:\\Temp\\InputData";         static void Main(string[] args) {             var cts = new CancellationTokenSource();             foreach (var obj in Input(cts.Token))            

Remote Controlled RoboTank

This is my version of the ever popular to build RoboTank. It uses an Arduino Mega 2560 with the AdaFruit motor shield and an XBee S1 to communicate to the DFRobot Gamepad. The sketch for the RoboTank makes use of the AFMotor.h to drive the motors and includes a serial parser to read and process the commands coming from the Gamepad. Robotank-Sketch.zip DFRobot Wireless Joystick