Validating whole object with IDataErrorInfo

1.9k views Asked by At

I implemented a IDataErrorInfo interface in my class to validate the objects. But what I want to do now, is to validate a whole object programatically to know if it´s valid or not. I have been searching in google but can´t find anything. I thought I could do something like a object.Validate() and know if its valid, but IDataErrorInfo does not provides any method like that.

I already validate it using XAML, but I need to check it in a method too.

Can anybody help me to achieve this? Just in case, this is my class:

    namespace Plutus.Data.Domain
{
    using Plutus.Data.Domain.NameMappings;
    using Plutus.Data.Domain.Validation;
    using System;
    using System.ComponentModel;

    public class Phone : IDataErrorInfo
    {
        /// <summary>
        /// Código de área.
        /// </summary>
        public string AreaCode { get; set; }

        /// <summary>
        /// Identificador de tipo de teléfono.
        /// </summary>
        public short? PhoneTypeID { get; set; }

        /// <summary>
        /// Número de teléfono.
        /// </summary>
        public long? PhoneNumber { get; set; }

        /// <summary>
        /// Número de interno.
        /// </summary>
        public string ExtensionNumber { get; set; }

        public string Error { get { return null; } }

        public string this[string columnName]
        {
            get
            {
                string result = null;
                if (columnName == "AreaCode")
                {
                    if (string.IsNullOrWhiteSpace(AreaCode))
                        result = ErrorMessages.ErrorMessage(ErrorMessages.FieldIsRequired, PhoneNameDictionary.GetValue(columnName));
                }
                if (columnName == "PhoneTypeID")
                {
                    if (!PhoneTypeID.HasValue)
                        result = ErrorMessages.ErrorMessage(ErrorMessages.FieldIsRequired, PhoneNameDictionary.GetValue(columnName));
                }
                if (columnName == "PhoneNumber")
                {
                    if (!PhoneNumber.HasValue)
                        result = ErrorMessages.ErrorMessage(ErrorMessages.FieldIsRequired, PhoneNameDictionary.GetValue(columnName));
                }

                return result;
            }
        }
    }
}

EXTENDED SOLUTION:

Maybe I didnt explain very good, but what I needed was to know if an object has any validation error or not. Based on ethicallogics solution, I created a new method called IsValid, where I check if there is any error on the dictionary:

public bool IsValid()
    {
        ValidateProperty("AreaCode");
        ValidateProperty("PhoneNumber");
        ValidateProperty("PhoneTypeID");

        return Errors.Count == 0 ? true : false;
    }

To implement this, I had to change the ValidateProperty method so as not to add again the error key in the dictionary (or you will get an exception). Then I checked first if the error is already in the dictionary, and I add it only if it doesnt:

public void ValidateProperty(string propertyName)
    {
        if (propertyName == "AreaCode" && string.IsNullOrWhiteSpace(AreaCode))
        {
            if (!Errors.ContainsKey(propertyName))
            Errors.Add(propertyName, ErrorMessages.ErrorMessage(ErrorMessages.FieldIsRequired, PhoneNameDictionary.GetValue(propertyName)));
        }
        else if (propertyName == "PhoneTypeID" && !PhoneTypeID.HasValue)
        {
            if (!Errors.ContainsKey(propertyName))
            Errors.Add(propertyName, ErrorMessages.ErrorMessage(ErrorMessages.FieldIsRequired, PhoneNameDictionary.GetValue(propertyName)));
        }
        else if (propertyName == "PhoneNumber" && !PhoneNumber.HasValue)
        {
            if (!Errors.ContainsKey(propertyName))
            Errors.Add(propertyName, ErrorMessages.ErrorMessage(ErrorMessages.FieldIsRequired, PhoneNameDictionary.GetValue(propertyName)));
        }

        else if (Errors.ContainsKey(propertyName))
            Errors.Remove(propertyName);
    }
3

There are 3 answers

2
yo chauhan On BEST ANSWER

First your class must implement INotifyPropertyChanged

    public class Phone : IDataErrorInfo, INotifyPropertyChanged
{
    string areaCode;
    public string AreaCode
    {
        get
        {
            return areaCode; 
        }
        set
        {
            if (areaCode != value)
            {
                areaCode = value;
                ValidateProperty("AreaCode"); //Validate on PropertyChanged
                Notify("AreaCode");
            }
        }
    }

    short? phoneTypeId;
    public short? PhoneTypeID
    {
        get
        {
            return phoneTypeId;
        }
        set
        {
            if (phoneTypeId != value)
            {
                phoneTypeId = value;
                ValidateProperty("PhoneTypeID");
                Notify("PhoneTypeID");
            }
        }
    }

    long? phoneNumber;
    public long? PhoneNumber
    {
        get
        {
            return phoneNumber;
        }
        set
        {
            if (phoneNumber != value)
            {
                phoneNumber = value;
                ValidateProperty("PhoneNumber");
                Notify("PhoneNumber");
            }
        }
    }

    string extensionNumber;
    public string ExtensionNumber
    {
        get
        {
            return extensionNumber;
        }
        set
        {
            if (extensionNumber != value)
                extensionNumber = value; Notify("ExtensionNumber");
        }
    }

    public string Error { get { return null; } }

    Dictionary<string, string> errors = new Dictionary<string, string>();

    public string this[string columnName]
    {
        get
        {
            if(errors.ContainsKey(columnName)
                return errors[columnName];

            return null;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    void Notify(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    //This could be much more generic but for simplicity 
    void ValidateProperty(string propertyName)
    {
        if (propertyName=="AreaCode" && string.IsNullOrWhiteSpace(AreaCode))
                errors.Add(propertyName,"AreaCode is Mandatory");

        else if (propertyName == "PhoneNumber" && !PhoneNumber.HasValue)
                errors.Add(propertyName, "PhoneNumber can not be null");

        else if(propertyName == "PhoneTypeID" && !PhoneTypeID.HasValue)
            errors.Add(propertyName, "PhoneTypeID can not be null");

        else if(errors.ContainsKey(propertyName))
            errors.Remove(propertyName);
    }

    public void ValidatePhoneObject()
    {
        ValidateProperty("AreaCode");
        ValidateProperty("PhoneNumber");
        ValidateProperty("PhoneTypeID");
        //Must fire property changed so that binding validation System calls IDataErrorInfo indexer
        Notify("AreaCode");
        Notify("PhoneNumber");
        Notify("PhoneTypeID");

    }
}

in xaml binding ValidateOnDataErrors must be True

 <TextBox x:Name="PhoneNumber" Text="{Binding PhoneNumber, ValidatesOnDataErrors=True}"/>

Here I have returned simple string instead of your ErrorMessage because I dont have idea what it is , you can simply replace string with your ErrorMessage. I hope this will give you an idea.

0
Foole On

ReactiveValidatedObject from ReactiveUI has an IsObjectValid method which does what you want. You could use that, or adapt that code.

0
AbdAlrhman AbdAlmonam On
// you can implement this Vali class in any of your classes
// which you need to check their validation

// Vali class will loop throw the properties of any another  class 
// which implement it. and check their validations
// and has IsValid which return the state of validation 
// return true if valid

public abstract class Vali {
    public bool IsValid {
        get {
            PropertyInfo propInfo = GetType().GetProperty("Error");
            object itemValue = propInfo.GetValue(this , null);

            string iiiii = (string)itemValue;

            foreach (PropertyInfo prop in GetType().GetProperties()) {
                if (!String.IsNullOrWhiteSpace(met(prop.Name))) {
                    //MessageBox.Show("تاكد من صحة البيانات");
                    return false;
                }
            }
            return true;

        }

    }
    public abstract string met(string columnName);
}

// as example your class will be like this  
public class Phone : Vali , IDataErrorInfo {
    /// <summary>
    /// Código de área.
    /// </summary>
    public string AreaCode { get; set; }

    /// <summary>
    /// Identificador de tipo de teléfono.
    /// </summary>
    public short? PhoneTypeID { get; set; }

    /// <summary>
    /// Número de teléfono.
    /// </summary>
    public long? PhoneNumber { get; set; }

    /// <summary>
    /// Número de interno.
    /// </summary>
    public string ExtensionNumber { get; set; }

    public string Error {
        get { return null; }
    }

    public string this[string columnName] {
        get {
            return met(columnName);
        }
    }

    public override string met(string columnName)
    {
        if (columnName == "AreaCode") {
            if (string.IsNullOrWhiteSpace(AreaCode))
                Error = ErrorMessages.ErrorMessage(ErrorMessages.FieldIsRequired , PhoneNameDictionary.GetValue(columnName));
        }
        if (columnName == "PhoneTypeID") {
            if (!PhoneTypeID.HasValue)
                Error = ErrorMessages.ErrorMessage(ErrorMessages.FieldIsRequired , PhoneNameDictionary.GetValue(columnName));
        }
        if (columnName == "PhoneNumber") {
            if (!PhoneNumber.HasValue)
                Error = ErrorMessages.ErrorMessage(ErrorMessages.FieldIsRequired , PhoneNameDictionary.GetValue(columnName));
        }

        return Error;
    }
}

class sdfasfd
{
    public sdfasfd()
    {
        // u can do this in ur code
        Phone p = new Phone();
        //assign properties here for p
        MessageBox.Show(p.IsValid.ToString());
    }
}