Silverlight 4 INotifyDataErrorInfo


Hello everyone!

So recently I have been looking into Silverlight 4’s new INotifyDataErrorInfo interface for a solution to validation.  I have to say that I really like how this works.

I have built a sample validation framework that models can use to build a simple and easy-to-use system for adding validation that is supported by Silverlight.

You can download the sample code here, but let’s look at a few things.

First let’s take a look at the basics of the Interface.  INotifyDataErrorInfo has three primary items:

  • event ErrorsChanged
  • IEnumerable GetErrors(string propertyName)
  • bool HasErrors

The ErrorsChanged event is the key to validation notification in this system.  When ErrorsChanged is fired, a binding that specifies ValidatesOnNotifyDataErrors=True will then call the GetErrors() method with the property name of the property for which the validation rule exists, or null if you want to specify the object as a whole.  So when you create your custom validation engine and you want to notify Silverlight that the validation state for a property (or object) has changed, you will raise this event.

One cool thing to note about the fact ErrorsChanged is an event, is that it can now support async validation rules.  Imagine in your validation system you make an async call back to the server; when it returns, you can raise this event!

private void HandleRuleResult(string propertyName, RuleKey<T> ruleKey, RuleArgs args)
        {
            if (args.IsValid)
            {
                var foundRule = (from re in ErrorMessages
                                 where re.PropertyName == propertyName
                                 && re.RuleIdentifier == ruleKey.RuleIdentifier
                                 select re).FirstOrDefault();

                if (foundRule != null)
                {
                    ErrorMessages.Remove(foundRule);
                    OnErrorsChanged(propertyName);
                }
            }
            else
            {
                var foundRule = (from re in ErrorMessages
                                 where re.PropertyName == propertyName
                                 && re.RuleIdentifier == ruleKey.RuleIdentifier
                                 select re).FirstOrDefault();

                if (foundRule == null)
                {
                    RuleError error = new RuleError() { RuleIdentifier = ruleKey.RuleIdentifier, ErrorMessage = args.Message, PropertyName = propertyName };
                    ErrorMessages.Add(error);
                    OnErrorsChanged(propertyName);
                }
            }
        }

        protected void OnErrorsChanged(string propertyName)
        {
            if (ErrorsChanged != null)
                ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
        }

 

The method of GetErrors() is what the system uses in order retrieve the validation errors when it is notified that the validation rules have changed.  The propertyName parameter will be passed either a single property name, for which you will retrieve your rules, or null, which is your key to retrieve all rules for the entire object.  This is helpful for controls like my custom ValidationSummary control.

It is important to note here that this method needs to be able to get the validation messages after the ErrorsChanged event fires. This means in your custom system, you must have stored your broken validation rules in a way where this can be coded to retrieve before you fire the event.

public System.Collections.IEnumerable GetErrors(string propertyName)
        {
            List<string> errors = null;

            if (propertyName == null)
            {
                if (ErrorMessages.Count > 0)
                {
                    errors = new List<string>();

                    foreach (var re in ErrorMessages)
                    {
                        errors.Add(re.ErrorMessage);
                    }
                }
            }
            else
            {
                var foundRules = (from re in ErrorMessages
                                  where re.PropertyName == propertyName
                                  select re).ToList();

                if (foundRules != null && foundRules.Count > 0)
                {
                    errors = new List<string>();
                    foreach (var re in foundRules)
                        errors.Add(re.ErrorMessage);
                }
            }

            return errors;
        }

 

The last piece is the HasErrors property: this is how to notify your objects that the resulting object has errors.  Every time a rule changes you may want to raise OnPropertyChanged for this property and implement its getter in a way where it knows how to count your broken rules.

public bool HasErrors
        {
            get { return ErrorMessages.Count > 0; }
        }

 

So far what we should now realize is that the interface provides us a way to tie our own validation engine directly into Silverlight.  It does not provide a validation engine in itself, but gives you everything you need to notify Silverlight that states have changed.

The sample code posted above provides a sample implementation of how a validation rules engine might be created around this interface.  I was very inspired by CSLA’s (http://www.lhotka.net/) validation rule entry interface so I modeled it somewhat after its pattern.  We have the ability to add rules to our objects via static methods and property names.  These rules are only added once per type and stored statically.  The engine also allows for the creation of async rules and also provides a bindable collection that is populated with broken rules when they are added.

Once again if you would like to download the sample framework and code you can get it here.

I hope you enjoy it and have a good day!

  1. #1 by jamal on April 7, 2010 - 4:59 am

    thanks for posting ,

    is the code tested ?

    secondly , how do i add error messages coming from the resource file(resx)

  2. #2 by Jason Rainwater on April 13, 2010 - 2:30 am

    I do not have any unit tests for it if that is what you are asking since this is only a demo.

    I do not have an automatic way to provide error messages from a resx file since this is only a demo, but what you could do is in your method for your error is if the validation fails you could grab a value from your resx using a factory method that would grab the message by culture based on a key you pass it that would return the appropriate string for the intended error message.

  3. #3 by Gregory Moon on June 1, 2010 - 12:45 am

    You’ve done it again. Great read!

  4. #4 by Jesper on June 8, 2010 - 1:57 pm

    Hi, after download i open in VS2010 Ultimate, and the build fails with a :
    Error 1 The “CreateRiaClientFilesTask” task failed unexpectedly.
    System.ArgumentException: Invalid ObjRef provided to ‘Unmarshal’.
    at …….. (+ alot more)

    Is there something im missing.?

  5. #5 by Jason Rainwater on October 18, 2010 - 11:10 am

    Sorry it has taken me so long to respond. My email to notify me of comments stopped working.

    I just tried downloading the source and running in vs 2010 and it seems everything is working. The only thing I remember from before was this was done during beta and I had for some reason chose to include RIA services. Those namespaces changed so this could be the reason why. However when I just tried again downloading from this site it works.

  6. #6 by Les Prigmore on April 27, 2011 - 3:08 pm

    Jason, you have changed my approach to error detection and notification. I really appreciate the read and sample code.

  7. #7 by Jason Rainwater on April 27, 2011 - 3:11 pm

    Thanks! Validation has always of a special interest to me so ive put in a lot of time thinking about how to do certain things. I am glad you enjoyed it :)

Comments are closed.