Binding ValidationRules Implementation

April 24, 2008

Hi,

While creating a WPF application I used the Binding element to bind between my Model and my GUI Viewer.
As you know the Binding element gives us the ability to invoke a validation rule collection for the binding activity before actually update the Model element, the binded Property.

I see basic two options to implement the Binding ValidationRules

1. In the scope of the ValidationRule implement class
2. On the Model

For this article I will use for a Model the MyModel class

/// Start Code
The Model class

public class MyModel : INotifyPropertyChanged
{
#region Fields

private string myBindedProperty;
#endregion


#region Properties
public string MyBindedProperty
{
get { return myBindedProperty; }
set { myBindedProperty = value; NotifyPropertyChanged("MyBindedProperty"); }
}
#endregion

#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;

private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion

}

/// End Code


1. Implement the Binding ValidationRules in the scope of the ValidationRule implement class

If you look at the next code section, we can see that I added to the binding element an instance of StringValidationRule class that implements the ValidationRule class.

/// Start Code
The binding

Binding myBinding = new Binding();
myBinding.Source = myModel;
myBinging.Path = new PropertyPath("MyBindedProperty");
myBinging.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
myBinging.ValidationRules.Add(new StringValidationRule (0,10));

TextBox myTBViewer=new TextBox();
myTBViewer.SetBinding(TextBox.TextProperty, myBinding);
/// End Code
Here the StringValidationRule Class implement the ValidationRule logic on himself ( I NOT recommend is run the logic on the Validate method it’s self)

/// Start Code
public class stringValidationRule : ValidationRule
{
public int Min { get;set;}
public int Max { get;set;}

public stringValidationRule(int min, int max)
{
this.Min = min;
this.Max = max;
}
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
//Do validation
return new ValidationResult(…
}
}
/// End Code

I do not recommend this way because that here the logic of the Model (myclass) is not on the Model himself , but on an outer method and each change on the Model will enforce us to "open" the method and change the logic.
More than that the Model class should not implement the ValidationRule class. The validation is part of the Model but not the ValidationRule implementation. The ValidationRule is for the Windows Controls, but the Model is for all Viewers type.
Do not be confused the Model validation should be on the Model, but the Model should not implement the ValidationRule class.

So, how should we do it?

2. Implement the Binding ValidationRules on the Model
First the validation logic for the Model have to be on the Model himself.
Second the ValidationRule will invoke the Model’s validation and we return the result by it.

I prefer to do it by reflection and use attributes
Let create an interface that we use for all our validation attribute

/// Start Code public interface IValidationAttribute
{
AttributeVlidationResult Validate(object value);
}


public struct AttributeVlidationResult
{
public bool IsValid {get; set;}

public string ValidationDate {get; set;}
}

/// End Code


Next define attribute that will check string validation

/// Start Code
[AttributeUsageAttribute(AttributeTargets.Property, AllowMultiple = true)]
public class StringValidationAttribute : Attribute, IValidationAttribute
{
public int Min { get;set;}
public int Max { get;set;}

#region IValidationAttribute Members

public AttributeVlidationResult Validate(object value)
{

//Do Validation
return new AttributeVlidationResult(…

}
#endregion

}
/// End Code


Now lets add to the Model the new Property Attribute

/// Start Code
[StringValidation(Min=0, Max=10)]
public string MyBindedProperty
{
get { return myBindedProperty; }
set { myBindedProperty = value; NotifyPropertyChanged("MyBindedProperty"); }
}

/// End Code
Create a new class that will implements the ValidationRule class and support all defined validation attributes ( IValidationAttribute interface)


/// Start Code public class AttributesValidationRule : ValidationRule
{
public object Source { get;set;}
public string PropertyName { get;set;}

public AttributesValidationRule(object source, string propertyName)
{
this.Source = source;
this.PropertyName = propertyName;
}

public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
PropertyInfo propertyInfo = Source.GetType().GetProperty(PropertyName);

if (propertyInfo != null)
{
object[] validations = propertyInfo.GetCustomAttributes(typeof(IValidationAttribute), false);

if (validations == null)
return new ValidationResult(true, "No validation specified for this Property");

foreach (object var in validations)
{
AttributeVlidationResult result = ((IValidationAttribute )var).Validate(value);

if (!result.IsValid)
return new ValidationResult(result.IsValid, result.ValidationDate);
}
return new ValidationResult(true, "Passed Validations");
}
}
}

/// End Code

For last lets change the Binding element validation ruls

/// Start Code
myBinging.ValidationRules.Add(new AttributesValidationRule(myModel, "MyBindedProperty"));


/// End Code


Now we can define several attributes that will implement the IValidationAttribute interface and add them to the wanted Property. When the Binding ValidationRules will invoke they will be call in loop by the reflection, if the Property do not have any validation rules it will return true, else will invoke all the rule validations that this property has.

So, what is the big difference?

1. The validation of the Model, defined on the Model.
2. Any Change on the Model will not influence the Binding validation rules. By adding, change or removing the validation of one of the Properties.

The main thing is to insure minimal dependency between the layers / components, in that way the changes on one of the components will not flows a change to all of them.



Hope that is article assist you in any way


Amour Shmuel

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

one comment

  1. ArchuletaSeptember 6, 2013 ב 08:29

    When I initially commented I clicked the “Notify me when new comments have
    been added” checkbox and now each time a comment is added I figure out three emails with that the same comment.
    Is there any technique it is possible to remove me out of that service?

    Cheers!

    Reply