Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Regex Validation in WPF

0.00/5 (No votes)
2 Nov 2006 1  
Demonstrates how to perform input validation using regular expressions, in a Windows Presentation Foundation application.

Introduction

This article demonstrates how regular expressions can be used to validate user input in a Windows Presentation Foundation (WPF) application. The technique presented herein provides two ways for a developer to validate the text of a TextBox via regular expressions: explicitly adding a ValidationRule-derived object to the ValidationRules of a Binding, or simply using attached properties of a static service provider class.

This code was compiled and tested against the June 2006 CTP of .NET Framework v3.0, but should work properly in later builds of the framework, as well.

Background

WPF provides an approach to input validation which is very different from the way that validation was performed in WinForms and ASP.NET. This article assumes that the reader is familiar with the fundamentals of WPF validation. If you are not yet familiar with validation in WPF, I highly recommend Paul Stovell�s excellent article on the subject. This page in the SDK is also very informative.

The general idea behind input validation in WPF involves the abstract ValidationRule class. Classes which derive from ValidationRule must override the Validate method to perform custom validation logic and return a value which indicates whether the input value is valid or not. Instances of the ValidationRule-derived classes can be added to a Binding�s ValidationRules collection. When it comes time for the bound value to be validated, all of the ValidationRule-derived objects will be queried to see if the input value is valid.

RegexValidationRule

My goal was to create a reusable means of validating user input via regular expressions. I implemented this by creating the RegexValidationRule class, which derives from ValidationRule, and executing a Regex in its Validate override. The RegexValidationRule is designed to validate the Text property of a TextBox.

The XAML below shows how to use this class:

<TextBox Name="txtProductCode">  
  <TextBox.Text>
    <Binding Path="ProductCode">
      <Binding.ValidationRules>
        <jas:RegexValidationRule
          RegexText="^[A-Z]{3}\.[0-9]{3}$"
          ErrorMessage="Invalid product code"
          RegexOptions="IgnoreCase"
          />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>

The Binding and RegexValidationRule created above in XAML can be created in the code-behind, as seen here:

Binding binding = new Binding();
binding.Path = new PropertyPath( "ProductCode" );

RegexValidationRule rule = new RegexValidationRule();
rule.RegexText = @"^[A-Z]{3}\.[0-9]{3}$";
rule.ErrorMessage = "Invalid product code.";
rule.RegexOptions = RegexOptions.IgnoreCase;

binding.ValidationRules.Add( rule );
txtProductCode.SetBinding( TextBox.TextProperty, binding );

The RegexValidationRule class has three public properties that can be used to adjust its behavior:

  • RegexText - stores the regular expression used during validation.
  • ErrorMessage - stores the text to be used/displayed when validation fails (such as the message in a tooltip).
  • RegexOptions - allows you to modify the RegexOptions property of the Regex object used to validate the input value.

When writing a regular expression in a XAML file, be sure to keep in mind that XAML requires certain characters to be escaped, such as ampersands (& = &amp;) and apostrophes ( ' = &apos;). Refer to this page in the SDK for more information about these necessary nuisances.

This is all well and good, but it seems like a lot of effort just to add an instance of the RegexValidationRule to the Text property�s associated Binding in XAML. It would be much more convenient if it was not necessary to create multiple nested elements, just to add a rule to the ValidationRules collection of a Binding. The next section of this article shows a much easier way to make use of the RegexValidationRule class.

RegexValidator

The RegexValidator class provides a streamlined way of using regular expressions to validate the text of a TextBox. Through the magic of attached properties, it is possible to avoid the multiple nested elements in XAML, as seen in the previous section, just to add a RegexValidationRule to a Binding�s ValidationRules.

The RegexValidator is a static class whose sole job is to create RegexValidationRule objects and add them to the appropriate Binding�s ValidationRules. It exposes its functionality via attached properties, so you never have to create an instance of RegexValidator to use the service it provides. In fact, you cannot instantiate the RegexValidator class because it is a static class.

Below is an example of the RegexValidator in use:

 <TextBox Name="txtProductCode"
  Text="{Binding Path=ProductCode}" 
  jas:RegexValidator.RegexText="^[a-zA-Z]{3}\.[0-9]{3}$"
  jas:RegexValidator.ErrorMessage="Invalid product code." 
  />

The XAML seen above could be expressed in C# like this:

// Establish the Text property's binding.

Binding binding = new Binding();
binding.Path = new PropertyPath( "ProductCode" );
this.txtProductCode.SetBinding( TextBox.TextProperty, binding );

// Assign the regular expression.

RegexValidator.SetRegexText( 
 this.txtProductCode, @"^[a-zA-Z]{3}\.[0-9]{3}$" );

// Assign the error message.

RegexValidator.SetErrorMessage(
 this.txtProductCode, "Invalid product code." );

As you can see, the RegexValidator is much easier to use than the RegexValidationRule, particularly when being used in XAML. You simply specify the values of attached properties, and the rest of the work is taken care of for you.

The astute observer will notice that this is a modified version of the example used in the previous section, but it does not set the RegexOptions property. I decided to omit the RegexOptions property from RegexValidator because I suspect that property will not be used very often (I could be wrong about that!). The benefit of omitting that property from RegexValidator is that the class�s API is kept very small and simple. If you need to set the RegexOptions property, you will need to create a RegexValidationRule explicitly, as seen in the previous section of this article.

How it works

The rest of this article explains how the RegexValidationRule and RegexValidator work. You do not need to read this section in order to use those classes in your applications.

The RegexValidationRule is very simple. Below is a stripped-down version of the class:

public class RegexValidationRule : ValidationRule
{
 // All other members were omitted for clarity.


 /// <summary>

 /// Validates the 'value' argument using the regular expression and 

 /// RegexOptions associated with this object.

 /// </summary>

 public override ValidationResult Validate( object value, 
   CultureInfo cultureInfo )
 {
 ValidationResult result = ValidationResult.ValidResult;

 // If there is no regular expression to evaluate,

 // then the data is considered to be valid.

 if( ! String.IsNullOrEmpty( this.RegexText ) )
 {
  // Cast the input value to a string (null becomes empty string).

  string text = value as string ?? String.Empty;

  // If the string does not match the regex, return a value

  // which indicates failure and provide an error mesasge.

  if( ! Regex.IsMatch( text, this.RegexText, this.RegexOptions ) )
   result = new ValidationResult( false, this.ErrorMessage );
 }

 return result; 
}

The Validate method (which is inherited from ValidationRule) simply calls the static IsMatch method of the Regex class to perform the validation. If the input value does not match the regular expression stored in the RegexText property, a ValidationResult is returned which indicates that the value is invalid and supplies an error message. The error message is retrieved from the ErrorMessage property. If the input value is valid, the ValidResult property is returned. That property returns a singleton instance of ValidationResult, so using it to indicate success allows us to avoid fragmenting the managed heap with redundant ValidationResult instances that all express the same thing (�the value is valid�).

The RegexValidator class is more complicated. First, let�s see how the class is declared:

public static class RegexValidator
{
 // There's more to come...

}

Since it is a static class, it can never be instantiated and all of its members must be static. This makes sense, because this class is a service provider and all of its functionality is exposed via attached properties. There is no reason to instantiate the class.

Next, we will examine the implementation of its attached properties:

/// <summary>

/// Identifies the RegexValidator's ErrorMessage attached property.  

/// This field is read-only.

/// </summary>

public static readonly DependencyProperty ErrorMessageProperty;

/// <summary>

/// Returns the error message used when validation fails for the

/// specified TextBox.

/// </summary>

/// <param name="textBox">The TextBox whose error message is returned.</param>

public static string GetErrorMessage( TextBox textBox )
{
 return textBox.GetValue( ErrorMessageProperty ) as string;
}

/// <summary>

/// Sets the error message used when validation fails for the 

/// specified TextBox.

/// </summary>

/// <param name="textBox">The TextBox being validated.</param>

/// <param name="value">The error message.</param>

public static void SetErrorMessage( TextBox textBox, string value )
{
 textBox.SetValue( ErrorMessageProperty, value );
}

/// <summary>

/// Identifies the RegexValidator's RegexText attached property.  

/// This field is read-only.

/// </summary>

public static readonly DependencyProperty RegexTextProperty;

/// <summary>

/// Returns the regular expression used to validate the specified TextBox.

/// </summary>

/// <param name="textBox">

/// The TextBox whose regular expression is returned.

/// </param>

public static string GetRegexText( TextBox textBox )
{
 return textBox.GetValue( RegexTextProperty ) as string;
}

/// <summary>

/// Sets the regular expression used to validate the 

/// specified TextBox.

/// </summary>

/// <param name="textBox">The TextBox being validated.</param>

/// <param name="value">The regular expression.</param>

public static void SetRegexText( TextBox textBox, string value )
{
 textBox.SetValue( RegexTextProperty, value );
}

Those attached properties are following the required convention of having public static Get/Set methods to associate a property value with a DependencyObject (in this case, it only works with the TextBox class). The attached properties are registered in a static constructor:

static RegexValidator()
{
 RegexTextProperty = DependencyProperty.RegisterAttached(
  "RegexText",
  typeof( string ),
  typeof( RegexValidator ),
  new UIPropertyMetadata( null, OnAttachedPropertyChanged ) );

 ErrorMessageProperty = DependencyProperty.RegisterAttached(
  "ErrorMessage",
  typeof( string ),
  typeof( RegexValidator ),
  new UIPropertyMetadata( null, OnAttachedPropertyChanged ) );
}

Notice that when the properties are registered, a callback method is supplied in the UIPropertyMetadata argument. That callback is invoked whenever the value of one of RegexValidator�s attached properties is modified for a TextBox. That callback method is implemented like this:

/// <summary>

/// Invoked whenever an attached property of the 

/// RegexValidator is modified for a TextBox.

/// </summary>

/// <param name="depObj">A TextBox.</param>

/// <param name="e"></param>

static void OnAttachedPropertyChanged( DependencyObject depObj, 
  DependencyPropertyChangedEventArgs e )
{
 TextBox textBox = depObj as TextBox;
 if( textBox == null )
  throw new InvalidOperationException(
   "The RegexValidator can only be used with a TextBox." );

 VerifyRegexValidationRule( textBox );
}

You might be wondering why I bothered using a callback to call the VerifyRegexValidationRule method, instead of just calling that method from within the static Set methods created for the attached properties. It might seem that this approach would work just fine, and be simpler:

// This is not how it works!

public static void SetErrorMessage( TextBox textBox, string value )
{
 textBox.SetValue( ErrorMessageProperty, value );
 VerifyRegexValidationRule( textBox );
} 

This approach would work if the value of the ErrorMessage property was always set by calling RegexValidator.SetErrorMessage for a TextBox. However, it is entirely possible to set the ErrorMessage attached property (or any other attached property) for a TextBox by directly calling SetValue on the TextBox itself, just like the SetErrorMessage method does. In that situation, the call to VerifyRegexValidationRule would not occur and the TextBox�s Text property binding would never have a RegexValidationRule added to its ValidationRules collection. By using the callback method supplied when registering the attached property, we can rest assured that whenever the attached property value is modified for a TextBox, our callback will call VerifyRegexValidationRule.

Now, it is time to examine what the VerifyRegexValidationRule method does. Remember, this method is called whenever the value of a RegexValidator attached property is modified.

/// <summary>

/// Creates or modifies the RegexValidationRule in the TextBox's 

/// Text property binding to use the current values of the attached 

/// properties exposed by this class.

/// </summary>

/// <param name="textBox">The TextBox being validated.</param>

static void VerifyRegexValidationRule( TextBox textBox )
{
 RegexValidationRule regexRule = GetRegexValidationRuleForTextBox( textBox );
 if( regexRule != null )
 {
  regexRule.RegexText = 
    textBox.GetValue( RegexValidator.RegexTextProperty ) as string;

  regexRule.ErrorMessage = 
    textBox.GetValue( RegexValidator.ErrorMessageProperty ) as string;
 }
}

This method attempts to retrieve a RegexValidationRule which is to be used for the specified TextBox�s Text property binding. If it gets a reference to one, it transfers property values to that object. Notice that the actual values for the RegexValidationRule properties are stored by the TextBox as attached properties. When working with attached properties, it is important to keep in mind that the value of those properties are stored by the object to which they are applied, not by the class/object which exposes the attached properties.

At this point, it is necessary to examine how the GetRegexValidationRuleForTextBox method works. This method is responsible for creating or retrieving a RegexValidationRule object for a given TextBox.

/// <summary>

/// Returns a RegexValidationRule to be used for validating the specified

/// TextBox.  If the TextBox is not yet initialized, this method returns null.

/// </summary>

static RegexValidationRule GetRegexValidationRuleForTextBox( TextBox textBox )
{
 if( ! textBox.IsInitialized )
 {
  // If the TextBox.Text property is bound, but the TextBox is not yet

  // initialized, the property's binding can be null.  In that situation,

  // hook its Initialized event and verify the validation rule again when 

  // that event fires.  At that point in time, the Text property's binding

  // will be non-null.

  EventHandler callback = null;
  callback = delegate
  {
    textBox.Initialized -= callback;
    VerifyRegexValidationRule( textBox );
  };
  textBox.Initialized += callback;
  return null;
 }

 // Get the binding expression associated with the TextBox's Text property.

 BindingExpression expression = 
   textBox.GetBindingExpression( TextBox.TextProperty );
 if( expression == null )
  throw new InvalidOperationException(
   "The TextBox's Text property must be bound for the RegexValidator to " + 
   "validate it." );

 // Get the binding which owns the binding expression.

 Binding binding = expression.ParentBinding;
 if( binding == null )
  throw new ApplicationException(
   "Unexpected situation: the TextBox.Text binding expression has no " + 
   "parent binding." );

 // Look for an existing instance of the RegexValidationRule class in the

 // binding.  If there is more than one instance in the ValidationRules

 // then throw an exception because we don't know which one to modify.

 RegexValidationRule regexRule = null;
 foreach( ValidationRule rule in binding.ValidationRules )
 {
  if( rule is RegexValidationRule )
  {
   if( regexRule == null )
    regexRule = rule as RegexValidationRule;
   else
    throw new InvalidOperationException(
     "There should not be more than one RegexValidationRule in a Binding's " +
     "ValidationRules." );
  }
 }

 // If the TextBox.Text property's binding does not yet have an 

 // instance of RegexValidationRule in its ValidationRules,

 // add an instance now and return it.

 if( regexRule == null )
 {
  regexRule = new RegexValidationRule();
  binding.ValidationRules.Add( regexRule );
 }

 return regexRule;
}

That method gets a reference to the BindingExpression associated with the TextBox�s Text property. It then retrieves a reference to the Binding which owns the expression, and iterates the Binding�s ValidationRules collection looking for a RegexValidationRule. If it cannot find one, it will create a new instance and add it to the ValidationRules collection. If it finds more than one RegexValidationRule, it will throw an exception, because there is no way to know which one should be returned.

Conclusion

The RegexValidationRule and RegexValidator provide support for validating a TextBox in WPF, using regular expressions.

You can create a RegexValidationRule and explicitly add it to the Text property�s Binding, or simply use the RegexValidator�s attached properties and it will take care of the work for you.

If you need to specify a single RegexOptions value for the RegexValidationRule, you can use the RegexOptions property of that class in XAML.

When creating regular expressions in a XAML file, keep in mind that XAML requires that certain characters must be escaped (such as the ampersand and apostrophe).

The complete source code and a demo application can be downloaded from the top of this article.

Revision History

  • 17 September 2006 - Created article.
  • 2 November 2006 - Originally, the article stated that bitwise operations cannot be performed in XAML (i.e., the XAML parser doesn't support them). I mentioned that "fact" in regards to combining multiple values of the RegexOptions enum. It turns out that I was wrong; flagged enum values can be combined by separating them with a comma. I removed that misinformation from the article. Here's the WPF forum post which revealed the truth about combining flagged enums in XAML: Microsoft Forums.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here