Wednesday 12 October 2011

Validation? We don't need no validation, part 2

Following on from my last post, I decided to give validation in WPF a go, with the added limitation that I could only use .NET 3.0 as we have a customer that has an n-1 policy and thus we are stuck with .NET 3.0 with this customer, but I digress. The application has a few textboxes and their content needs to be validated. There are two types of validation needed:
  • Time
  • Integer
For the first one, a Regular Expression will be used and for the second one, type checking will be used, in other words, any non-integer value will trigger a validation error. A class called, Parameters, that implements the INotifyPropertyChanged, is used to hold all the textbox values. This is the class definition:

   1 using System;
   2 using System.Collections.Generic;
   3 using System.Text;
   4 using System.ComponentModel;
   5 using System.Windows.Controls;
   6 using System.Globalization;
   7 using System.Text.RegularExpressions;
   8 
   9 namespace ConfigurationTool
  10 {
  11     public class Parameters : INotifyPropertyChanged 
  12     {
  13         public event PropertyChangedEventHandler PropertyChanged;
  14 
  15         string starttime;
  16         int retryinterval;
  17         int callretention;
  18         int nofservers;
  19         int threadsperserver;
  20 
  21         public Parameters() { }
  22 
  23         public string startTime          
  24         {
  25             get { return starttime; }
  26             set
  27             {
  28                starttime = value;
  29                 OnPropertyChanged("startTime");
  30             }
  31         }
  32 
  33         public int retryInterval
  34         {
  35             get { return retryinterval; }
  36             set
  37             {
  38                 retryinterval = value;
  39                 OnPropertyChanged("retryInterval");
  40             }
  41         }
  42 
  43         public int callRetention
  44         {
  45             get { return callretention; }
  46             set
  47             {
  48                 callretention= value;
  49                 OnPropertyChanged("callRetention");
  50             }
  51         }
  52 
  53         public int nofServers
  54         {
  55             get { return nofservers; }
  56             set
  57             {
  58                 nofservers = value;
  59                 OnPropertyChanged("nofServers");
  60             }
  61         }
  62 
  63         public int threadsperServers
  64         {
  65             get { return threadsperserver; }
  66             set
  67             {
  68                 threadsperserver = value;
  69                 OnPropertyChanged("threadsperServers");
  70             }
  71         }
  72       
  73 
  74         protected void OnPropertyChanged(string name)
  75         {
  76 
  77             PropertyChangedEventHandler handler = PropertyChanged;
  78 
  79             if (handler != null)
  80             {
  81 
  82                 handler(this, new PropertyChangedEventArgs(name));
  83 
  84             }
  85 
  86         }      
  87 
  88 
  89     }
  90 
  91 }

Note that starttime is a string, I guess TimeSpan object could have used to perhaps used type checking but I've just thought about it now, while writing this up.

In order to do the regex validation, the following class is used (thanks to stackoverflow).

   1 using System;
   2 using System.Collections.Generic;
   3 using System.Text;
   4 using System.Windows.Controls;
   5 using System.Globalization;
   6 using System.Text.RegularExpressions;
   7 
   8 namespace ConfigurationTool
   9 {
  10     class RegexValidator : ValidationRule
  11     {
  12          string pattern;
  13          string errormessage;
  14          Regex regex;
  15          
  16         public string Pattern
  17         {
  18             get { return pattern; }
  19             set
  20             {
  21                 pattern = value;
  22                 regex = new Regex(pattern, RegexOptions.IgnoreCase);
  23             }
  24         }
  25 
  26         public string errorMessage
  27         {
  28             get { return errormessage; }
  29             set
  30             {
  31                 errormessage= value;                
  32             }
  33         }
  34 
  35         public RegexValidator()
  36         {
  37 
  38         }
  39 
  40         public override ValidationResult Validate(object value, CultureInfo cultureInfo)
  41         {           
  42             try
  43             {
  44                 if (regex != null)
  45                 {
  46                     if (!regex.IsMatch(value.ToString()))
  47                     {
  48                         throw new FormatException();
  49                     }
  50                     else
  51                     {
  52                         return ValidationResult.ValidResult;
  53                     } 
  54                 }
  55 
  56                 return new ValidationResult(false, errorMessage);
  57             }
  58             catch (FormatException)
  59             {
  60                 return new ValidationResult(false, errorMessage);
  61             }
  62 
  63         }
  64     }
  65 }

Using this class any regular expression can be validated. All that is required is to hook it up in the xaml to a property and it will be validated.  At any rate, below is the (edited) xaml for this application, with an example of using regular expression validation:

   1 <Window x:Class="ConfigurationTool.MainWindow"
   2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4         xmlns:sys="clr-namespace:System;assembly=mscorlib"
   5         xmlns:u="clr-namespace:ConfigurationTool"
   6         Title="Configuration Tool" Height="465" Width="565" Icon="/ConfigurationTool;component/Images/maint.ico">
   7 
   8  <Window.Resources>
   9      <u:Parameters x:Key="Pams"/>
  10  </Window.Resources>
  11  
  12  <Grid>
  13     <TextBox Height="23" HorizontalAlignment="Left" Margin="371,72,0,0" Name="RunningTimetextBox" VerticalAlignment="Top" Width="144">
  14         <TextBox.Text>
  15             <Binding Path="startTime" UpdateSourceTrigger="PropertyChanged" Source="{StaticResource Pams}">
  16                 <Binding.ValidationRules>
  17                     <u:RegexValidator Pattern="^([0-1][0-9]|2[0-3]):[0-5][0-9]$" errorMessage="Enter a Valid Time"/>
  18                 </Binding.ValidationRules>
  19             </Binding>
  20         </TextBox.Text>
  21     </TextBox>
  22     <Label Content="Retry Interval (in minutes)" Height="28" HorizontalAlignment="Left" Margin="26,102,0,0" Name="label4" VerticalAlignment="Top" />
  23     <TextBox Height="23" HorizontalAlignment="Left" Margin="371,101,0,0" Name="RetryIntervaltextBox" VerticalAlignment="Top" Width="144" >
  24         <TextBox.Text>
  25             <Binding Path="retryInterval" UpdateSourceTrigger="PropertyChanged" Source="{StaticResource Pams}">
  26                 <Binding.ValidationRules>
  27                     <ExceptionValidationRule/>
  28                 </Binding.ValidationRules>
  29             </Binding>
  30         </TextBox.Text>            
  31     </TextBox>
  32   </Grid>
  33 </Window>

Things to note with the xaml are:
  • Windows resources defines the parameters class
  • This class is hooked up to all textboxes via Source attribute
  • The regex validation class is also hooked up to any textbox as needed.
We can compare the above with a sample code used to validate a textbox in a winforms application:

   1 private void RunningTimetextBox_Validating(object sender, CancelEventArgs e)
   2 {
   3     try
   4     {    //Ensure that the Start time is valid
   5         if (!RunningTimetextBox.Text.Trim().Equals(string.Empty))
   6         {
   7             if (!Regex.IsMatch(RunningTimetextBox.Text.Trim(), Constants.timeRegex))
   8             {
   9                 e.Cancel = true;
  10 
  11                 MessageBox.Show(Constants.errValMsg, "Error", MessageBoxButtons.OK);
  12             } 
  13         }
  14 
  15     }
  16     catch (Exception ex)
  17     {
  18         MessageBox.Show(string.Format(Constants.errMsg, ex.ToString()), "Error", MessageBoxButtons.OK);
  19     }
  20 
  21 }
   
The code is from a similar application and while not perfect it takes about one minute to write, yes it might not be as pretty as WPF, but prettification is hardly at the top of the list for most enterprise applications. It's a nice to have not a killer feature.

No comments:

Post a Comment