Dot Net For All

WPF validation using INotifyDataErrorInfo

In this article I will discuss about INotifyDataErrorInfo which is used to validate the data asynchronously in WPF. In my previous articles I have discussed about the ways to validate data entered in the UI.

Please find the previous articles link below

Validation using INotifyDataErrorInfo

Lets have a look at the INotifyDataErrorInfo interface in the below figure

INotifyDataErrorInfo interface

 

The interface has three members which the derived class need to implement. Whenever we are setting this validation, the HasErrors property is called by the binding which in turn calls the GetErrors method to return the error if the HasErrors return true. After some point when GetErrors is called the ErrorsChanged event is raised and it again calls GetErrors method. It is assuming that there are some asynchronous operation that filled the collection on which the GetErrors is working on.

As we can see the GetErrors method return the IEnumerable object. It means that it is working on some collection of the objects to get the errors.

Lets implement INotifyDataErrorInfo to our view model class. Please check the code below for the PersonVM class.

 public class PersonVM: INotifyPropertyChanged, INotifyDataErrorInfo
    {
        private string mobileNumber;

        public string MobileNumber
        {
            get { return mobileNumber; }
            set {               
                mobileNumber = value;
                PropertyChanged(this, new PropertyChangedEventArgs("MobileNumber"));
                GetErrorsForPhone(MobileNumber).ContinueWith((task) =>
                {
                    lock (_PropertyErrors)
                    {
                        _PropertyErrors["MobileNumber"] = task.Result;
                        ErrorsChanged(this, new DataErrorsChangedEventArgs("MobileNumber"));
                    }
                });
            }
        }

        private string name;

        public string Name {
            get { return name; }
            set
            {
                name = value;
                PropertyChanged(this, new PropertyChangedEventArgs("Name"));
            }
        }
        public event PropertyChangedEventHandler PropertyChanged = delegate { };


        public bool HasErrors
        {
            get
            {
                return PropertyErrorsPresent();
            }
        }

        private bool PropertyErrorsPresent()
        {
            bool errors = false;
            foreach (var key in _PropertyErrors.Keys)
            {
                if(_PropertyErrors[key] != null)
                {
                    errors = true;
                    break;
                }
            }

            return errors;
        }

        public IEnumerable GetErrors(string propertyName)
        {
            lock (_PropertyErrors)
            {
                if (_PropertyErrors.ContainsKey(propertyName))
                    return _PropertyErrors[propertyName];
            }

            return null;
        }
      
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged = delegate { };
      

        Dictionary<string, List<string>> _PropertyErrors = new Dictionary<string, List<string>>();

        Task<List<string>> GetErrorsForPhone(string value)
        {
            return Task.Factory.StartNew<List<string>>(() =>
            {
                Thread.Sleep(5000);
                Regex regex = new Regex(@"^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})$");
                if (regex.Match(value) == Match.Empty)
                    return new List<string> { "Invalid phone format" };
                else return null;

            });
        }
    }

In the above code I am checking for the validation of the MobileNumber property. To support the error collection for the property I have created a dictionary which will contain all the errors related to a property. Let’s understand the code one step at a time.

  1. In the application if we are checking the MobileNumber to have a valid 10 digit number. And if we change to invalid number we will call the GetErrorsForPhone() method which is acting as a time consuming operation analogous to some service, which is being is used to validate the data.
  2. In the setter of the MobilePhone property we are continuing with the result of the GetErrorForPhone() method, which is a list of the string. And we set these errors for the “MobileNumber” property in the dictionary and raise the ErrorChanged event.
  3. When the ErrorChanged event is raised, it notifies the “MobileNumber” property’s binding and which calls the HasErrors property. If there are errors GetErrors() method is called by the binding.
  4. If we have entered some invalid format which is not as per our requirement we will get the validation error on the UI.

Conclusion:

In this article I have discussed the way in which we can use INotifyDataErrorInfo with example.

 

 

Top career enhancing courses you can't miss

My Learning Resource

Excel your system design interview