WinRT XAML Validation library: Property-Group Validation Attributes

All predefined validation attributes in the System.ComponentModel.DataAnnotations namespace can be used to annotate a single property of an entity. So they cannot be used for validation that spans multiple properties.

For this case, the WinRT XAML Validation library comes with the GroupValidationAttribute base class. Its member CausativeProperties can be used to define properties, which cause the validation to run, when their values are changing. With the member AffectedProperties you can set properties, that will be marked invalid when the validation of a property from the CausativeProperties group fails.

The following code gives an example of a concrete validation class that extends the GroupValidationAttribute base class. CausativeProperties equals AffectedProperties, thus the group of properties that cause the validation equals the group of properties that are affected by the validation:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class ValidateAgainstMaxBidAttribute : GroupValidationAttribute
{
    protected override IEnumerable<Expression<Func<object>>> AffectedProperties
    {
        get
        {
            AuctionBid entity = null;
            return new Expression>[]
                {
                    () => entity.NewBid,
                    () => entity.MaxNewBid
                };
        }
    }

    protected override IEnumerable<Expression<Func<object>>> CausativeProperties
    {
        get { return this.AffectedProperties; }
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var auctionBid = (AuctionBid)validationContext.ObjectInstance;

        return auctionBid.MaxNewBid.HasValue && auctionBid.NewBid > auctionBid.MaxNewBid.Value
            ? new ValidationResult("New bid must not be greater than highest bid.")
            : ValidationResult.Success;
    }
}

This group validation property can now be used to annotate the whole model entity on class level:

[ValidateAgainstMaxBid(ShowMessageOnProperty = false)]
public class AuctionBid : ValidationBindableBase
{
    public double CurrentBid { ... }
    public double NewBid { ... }
    public double? MaxNewBid { ... }
}

Additionally, the GroupValidationAttribute class derives from the ExtendedValidationAttribute class, so you’re able to use additional functionality brought by the ValidationLevel,  ShowMessageOnProperty and ShowMessageInSummary properties and others.

WinRT XAML Validation library: Basic Validation Attributes

Validation in the WinRT XAML Validation library is mainly based on .NET Data Annotations from the System.ComponentModel.DataAnnotations namespace. Here you can find several attributes, that you can attach to your entities and its properties. This annotates them with additional information/metadata, which’s evaluated elsewhere in your code or by the .NET framework.

WPF, Silverlight and e.g. ASP.NET MVC can evaluate those attributes automatically to display further information for an entity like a property label, etc.. And you can define validation logic with Data Annotations on your entity, which gets evaluated and presented to the user when the entity is validated.

ValidationAttribute

The WinRT XAML Validation library uses the Data Annotations approach as well to define validation logic on model entities. First you can use the already defined validation attributes from the System.ComponentModel.DataAnnotations namespace: RangeAttribute, RegularExpressionAttribute, RequiredAttribute, StringLengthAttribute, CustomValidationAttribute. All of those predefined attributes implement the ValidationAttribute base class. You can derive from this class as well to implement validation attributes with your encapsulated custom validation logic, for example:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class ValidateGreaterCurrentBidAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var newBid = (double?)value;
        var auctionBid = (AuctionBid)validationContext.ObjectInstance;

        return newBid.HasValue && newBid.Value <= auctionBid.CurrentBid
            ? new ValidationResult("Value must be greater than current bid")
            : ValidationResult.Success;
    }
}

Now you can easily decorate your model entities with this attribute:

public class AuctionBid : ValidationBindableBase
{
    private double currentBid;
    private double newBid;
    private double? maxNewBid;

    public double CurrentBid
    {
        get { return this.currentBid; }
        set { this.SetProperty(ref this.currentBid, value); }
    }

    [Range(0, double.MaxValue, ErrorMessage = "Value must not be negative")]
    [ValidateGreaterCurrentBid]
    public double NewBid
    {
        get { return this.newBid; }
        set { this.SetProperty(ref this.newBid, value); }
    }

    [ValidateGreaterCurrentBid]
    public double? MaxNewBid
    {
        get { return this.maxNewBid; }
        set { this.SetProperty(ref this.maxNewBid, value); }
    }
}

 

ExtendedValidationAttribute

With the abstract ExtendedValidationAttribute class there’s another base validation attribute in the WinRT XAML Validation library that extends the functionality of the ValidationAttribute. Specifically with this base class you can:

  • define a level for the validation message (Warning or Error)
  • define if the validation message should be shown on the property and in the validation summary on the UI
  • define if the validation attribute should be considered in implicit validation (when a property changes)

For example, a validation level „Warning“ leads to the following output on the UI:

The definition on the entity property (taking a custom validation attribute ValidatePossibleMisentryBid as example) looks as follows:

[ValidatePossibleMisentryBid(ValidationLevel = ValidationLevel.Warning)]
public double NewBid { ... }

Like the ValidationAttribute you can use the ExtendedValidationAttribute as base class for your custom validation attributes.