User Input Validation in Silverlight 2.0

 

There are different ways of getting data from a Silverlight application, in this blog post I will focus on using WCF. When we use WCF we define DataContract which will be used between the client and the server. It’s relative easy to create a DataContract, here is an example of a contract:

[DataContract]
public class Customer
{
   [DataMember]
    public string CustomerID { get; set; }

    [DataMember]
     public string CompanyName { get; set; }
}

 

When we add a Service Reference from our Silverlight project we will get a generated client class from the DataContract. The generated class of the above DataContract will look like this:

[GeneratedCode("System.Runtime.Serialization", "3.0.0.0"), DebuggerStepThrough, DataContract(Name="Customer", Namespace="http://schemas.datacontract.org/2004/07/ContactClient.Web")]
public class Customer : INotifyPropertyChanged
{
    private string CompanyNameField;
    private string CustomerIDField;
    private PropertyChangedEventHandler PropertyChanged;

    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged(string propertyName);

    [DataMember]
    public string CustomerID
    {
       get { return this.CustomerIDField; }
       set
       {
          if (!object.ReferenceEquals(this.CustomerIDField, value))
          {
             this.CustomerIDField = value;
             this.RaisePropertyChanged("CustomerID");
          }
       }
    }

    [DataMember]
    public string CompanyName
    {
       get { return this.CompanyNameField; }
       set
       {
          if (!object.ReferenceEquals(this.CompanyNameField, value))
          {
             this.CompanyNameField = value;
             this.RaisePropertyChanged("CompanyName");
          }
       }
    }
}

 

The thing that I like with this generated code is that the INotifyPropertyChanged is implemented. In this case we can simply bind this client class to FrameworkElement and get a notification when a property is changed. The sad thing is that we can’t easy add validation to the set methods to validate the value. If we should “hack” the generated class, and update our Service based on changes to the Service, our code would be overwritten. So we need another way to handle validation. We can use a traditional validation where we add code to our code-behind, which will validate the User input value, and if the values are not  correct, we display a nice message for the user. Something like this:

private void saveButton_Click(object sender, RoutedEventArgs e)
{
    if (IsFieldEmpty(companyNameTextBox);)
       return;
 
    //Save
}

private bool IsFieldEmpty(TextBox validateTextBox)
{
    if (string.IsNullOrEmpty(validateTextBox.Text.Trim()))
    {
        validateTextBox.Margin = new Thickness(2);
        validateTextBox.BorderThickness = new Thickness(2);
        validateTextBox.BorderBrush = new SolidColorBrush(Color.FromArgb(255, 255, 0, 0));
        return false;
    }
    else
    {
        RestoreCustomerFormTextBoxesToOriginalStyle(validateTextBox);
        return = true;
    }
 }


Note: We could have used a Resource for the TextBox styles instead of hardcode the Margin and BorderThickness etc.


Let us assume that we have control over the DataContract DataMember and added the validation to the set mothod, like this:

[DataMember]
public string CompanyName
{
   get { return this.CompanyNameField; }
   set
   {
       if (!object.ReferenceEquals(this.CompanyNameField, value))
       {
          if(string.IsNullOrEmtpy(value))
              throw new ApplicationException("CompanyName can't be null or empty!");

          this.CompanyNameField = value;
          this.RaisePropertyChanged("CompanyName");
       }
   }
}


We could then enable the NotifyOnValidationError and ValidateOnException when we use the Binding expression:

 

<StackPanel x:Name="myStackPanel">
   <TextBox x:Name="myTextBox" Margin="20" Width="200" Height="30" 
             Text="{Binding Value, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"></TextBox> <!-- .... --> </StackPanel>

 

In this example we bind our data to the StackPanel, so we need to hook up to the BindingValidationError event to handle the exception that will be throw when our validation will fail:

void Page_Loaded(object sender, RoutedEventArgs e)
{
    myStackPanel.DataContext = new MyClass() { Value = "Test" };
    myStackPanel.BindingValidationError += myStackPanel_BindingValidationError;
}


void myStackPanel_BindingValidationError(object sender, ValidationErrorEventArgs e)
{
    var control = e.OriginalSource as Control;

    if (control != null)
    {
        if (e.Action == ValidationErrorEventAction.Added)
           control.Background = new SolidColorBrush(Color.FromArgb(255, 255, 0, 0));
        else if (e.Action == ValidationErrorEventAction.Removed)
           control.Background = new SolidColorBrush(Color.FromArgb(255, 255, 255, 255));
    }
 }

 

The BindingValidationError event handler has an argument of type ValidationErrorEventArgs. This event arg has a Action property we can use to see if the validation has failed (ValidationErrorEventAction.Added), or if we have fixed the validation “problem” (ValidationErrorEventAction.Removed).

If we want to use this Binding validation feature we can’t bind our generated DataContract to our FrameworkElements because we can’t add validation code to it in a easy way (not what I’m aware of). To solve this we could add a new class to the Silverlight project which can uses the same interface as the DataContract and where we add the validation code. We can then use this class and bind it to the FrameworkElements. The problem with this is that we need to map our generated DataContract to this new class. This can of course be fixed if we skip the use of the auto generated proxy classes for our service, and create on manually. A manually solution will be more optimized and we could easy add the validation to the client side DataContract without being worry that our code should be overwritten with a tool. But this require more coding.

When I creates applications I prefer to separate the model used for presentation from the DataContract. So I often map the DataContract class to my “presentation” model. The reason to this is because the “presentation” model can have a different interface than the DataContract, and also have a more suitable structure for using features like DataBinding and validation etc.

4 Comments

  • Nice intro to Silverlight validation. And I agree - wrapping your data objects into presentation models is a good and common pattern in WPF and Silverlight :)

  • It would be nice if you could provide the Visual Studio Solution for this.
    I think it's so much easier to read and learn from inside the code.
    Some code to Sweden would that be so hard?

    Greetings from Sweden
    Henrik S

  • I agree with Henrik S - source code is good :)

  • Mmmm...
    &nbsp;Taking into account that there's no clean solution for this problem, What about taking advantages of partial classes?
    &nbsp;Let's say our autogenerated proxy class (named "MyClass") has a property called "MyProperty", I could create a partial class and define a new property called "MyPropertyBinded" this class will call the get and set of MyProperty, but first will perform the needed validation and throw the needed exceptions just in case (then in the XAML we will bind to the MyPropertyBinded class):
    partial class MyClass
    {
    public string myPropertyBinded
    {
    get { return this.MyProperty; }
    &nbsp; set
    &nbsp; {
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if(string.IsNullOrEmtpy(value))
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;throw new ApplicationException("MyProperty can't be null or empty!");
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;this.MyProperty = value;
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;this.RaisePropertyChanged("MyPropertyBinded");
    &nbsp; &nbsp; &nbsp; }
    &nbsp; } &nbsp;
    }
    What do you think?

Comments have been disabled for this content.