Async Data Validation

MiniTutorial

This is the sixth posting in a series on Templates and DataValidation.
[ First In Series                                    Previous In Series ]

 

In this posting I’ll extend the data validation mechanism we’ve been examining to support asynchronous data validation (allowing for validation by objects on a web service). 

Silverlight is all about client-side code, and client-side validation is often exactly what you want; however there are times that you do not want to expose your validation algorithm in the client or in which you need to do a dynamic database lookup (credit ratings come to mind).  In that case, you’ll want your Silverlight application to be able to validate against a web service, but to keep working while it does so.  That’s just what we’ll do today.

To save time I’ll build on a variation of the project we’ve been looking at. There are no surprises in that code, and you can download the source code for both the  starting and the ending project.

To review, briefly, MainPage invokes EditCustomer.  The contents of EditCustomer’s input controls are filled via databinding, which includes the two attributes needed for data validation. For example, the Zip Code text box looks like this:

<TextBox
    x:Name = "Zip"
    Width = "80"
    Text = "{Binding Path=Zip, Mode=TwoWay,
       ValidatesOnExceptions=true, NotifyOnValidationError=true}"
    Style = "{StaticResource NarrowTextBoxStyle}" />

Validation Logic In the VM

All of the client side  validation logic is in the View Model.  For example, here is the logic for the State field validation:

private string state;
public string State
{
   get { return state; }
   set
   {
      var stateErrors = new List<String>();

      if (string.IsNullOrEmpty(value))
      {
         return;
      }
         
      AddLogItem("Testing State for 2 characters");
      if ( value.Length != 2)
      {
         stateErrors.Add( "State must be two letters" );
         AddLogItem("ERROR  State was not 2 characters");
         ManageErrors("State", stateErrors);
      }
      else
      {
         ManageErrors( "State", null);
         state = value;
         AddLogItem("State accepted");
         RaisePropertyChanged("State");
      }
   }
}

As discussed earlier in this series, the validation is done in the setter of the property. In this case, I’ve factored out the management of the dictionary of errors to the ManageErrors method.  ManageErrors job is either to add the property to the dictionary of errors if the entry is not valid, or to remove it from the dictionary if it is already there but the field is no longer invalid. 

private void ManageErrors(
   string property,
   List<String> errors )
{
   var raiseEventErrorsChanged = false;

   if ( property == null )
   {
      return;
   }

   if ( errors == null )
   {
      if ( currentErrors.ContainsKey( property ) )
      {
         AddLogItem( "Clearing errors for " + property );
         currentErrors.Remove( property );
         raiseEventErrorsChanged = true;
      }
      AddLogItem( "Set Status OK" );
   }
   else
   {
      AddLogItem( "Writing error for " + property );
      currentErrors.Add(
         property,
         errors );
      AddLogItem( "Set Status Error" );
      raiseEventErrorsChanged = true;
   }

   if ( raiseEventErrorsChanged && ErrorsChanged != null )
   {
      AddLogItem( "Invoking ErrorsChanged with " + property );

      ErrorsChanged(
         this,
         new DataErrorsChangedEventArgs( property ) );
   }
}

To add asynchronous support for checking against a web service we’ll add four elements to the program:

  • A (mock) web service
  • A callback method to receive the decision from the web service (valid or not valid?)
  • An enumerated set of states to indicate to the user if the field is valid or not or if we’re waiting for a response
  • A public property indicating the current status (to which the UI can bind)

 

Modifying The Validation To Call Out To The Web Service

After we’ve done all the local validation work we can, the State property will call out to the Web  Service, passing in a copy of the value that is being evaluated, and an instance of a CustomerCallBack delegate,

public delegate void CustomerCallBack( bool isValid );

Specifically, one which will cause the web service to call the method StateCallMeBack when it has validated the value.

In the demo code, I mock up the web service to delay for a random but constrainted amount of time; then it invokes the callback method through the delegate, passing as a parameter a boolean that is randomly set either true or false.

private static void TimeElapsed(
   object sender,
   EventArgs e)
{
   var t = sender as DispatcherTimer;
   if (t == null)
   {
      return;
   }

   t.Stop();
   var custCallBack = CallBacks[t];

   var isValid = RandomGenerator.Next()%2 == 0;
   custCallBack(isValid);
}

The callback method itself acts just like any other local validating method, checking the truth of its if statement and either adding or clearing the error,

public void StateCallMeBack( bool isValid )
{
    AddLogItem( "In call back from Web Service" );
    ErrorCheckingStatus = Status.Completed;
    if ( isValid )
    {
        AddLogItem( "Web service reported OK" );
        ManageErrors(
            "State",
            null );
        state = candidateNewState;
        ErrorCheckingStatus = Status.Ok;
        RaisePropertyChanged( "State" );
    }
    else
    {
        AddLogItem( "Web service reported error" );
        var callBackErrors = new List<String>();
        const string info = "Your entry does not match a US State";
        callBackErrors.Add( info );
        ErrorCheckingStatus = Status.Error;
        ManageErrors(
            "State",
            callBackErrors );
    }
    ErrorCheckingStatus = Status.Completed;
}

Asynchronous Means Never Having To Say You’re Blocked…

 

Async

 

 

While the web service is processing the validity of the data you are free to go on and check the validity of other fields.  The status will be set to In Progress and nothing will be blocked. When the web service completes the call back will be invoked and will set the state appropriately.  Notice that the log shows that the request was made to the server, and then the program went on to test the Zip Code which fails the test, and the appropriate error is displayed.

A Personal Note

On a personal note, I was very pleased to see Silverlight build on Styles and Templates to add the Visual State manager, and then build on the Visual State Manager and databinding to build data validation. This is a strong indication of a powerful an flexible architecture, and I believe it bodes well for continuing development.

In the next posting, I’ll  take a look at how you can use the Visual State Manager to change how the error message appears. 

 

Note that this week marks the launch of a new and on-going series of tutorials and videos on Windows Phone 7 Programming.  If there are topics of specific interest to you, please do leave a comment.

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in Data, Mini-Tutorial, Styles and Templates, Tools and Utilities. Bookmark the permalink.

8 Responses to Async Data Validation

Comments are closed.