Search code examples
c#wpfidataerrorinfo

Validating whole object with IDataErrorInfo


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);
    }

Solution

  • 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.