Blogs

Paul Kimmel's Blog

Validating Using Regular Expressions or an IExtenderProvider

     

I don’t do the full-on tourism thing that much. If I am out and about and end up at a popular tourist attraction then it’s okay, but rushing place to place to snap photos of things just never appealed to me that much. I have been coming to LA since 1999 and had never been on Hollywood Blvd. I was enjoying a sunset and a beer years ago and saw Venice transmogrify from street carnies to husky-voiced (ahem!) women in a couple of minutes, but it was an accident.

Last Friday I decided to play tourist and went to the Hollywood Bowl and Hollywood Blvd snapping pictures of everything. I was taking pictures of “Stars” until I saw that Max Factor had a star—I thought this was makeup. Disenchanted I put the iPhone away and ended up at Loteria—the restaurant—for a bite to eat. Loteria—the game—as it goes is a game played with cards that look like Tarot cards and is played like bingo. I dubbed it Tarot-bingo (after a couple of El Milagros I pronounced it “terror bingo”.) The food at Loteria was pretty good, it seemed less fancy than authentic. I watched women shuck Pablano peppers, roll out dough for tortillas, and even swapped a quick holy mole recipe with the open-kitchen help. What struck me about  the restaurant was the place was cool, it was packed full of patrons, but the food presentation wasn’t fancy; in fact as presentation goes it was pretty simple. I had a Mole Con Pollo burrito and that’s what I got—a chicken burrito with Mole sauce and a few chips. No one took any time make yin and yang decorations with the Mole sauce, there wasn’t really any garnish either. The food just tasted fresh, good, and I got to watch them make it. To boot the prices of the entrees were reasonable too.

Solutions to programming problems can be fancy, full of frills, unique or a solutions can be straight forward, simple, and satisfying. When writing for an audience I might jazz up the code with some garnish, try something clever, or twist and torque the code like test driving a Porsche on the autobahn. In practice straight and simple code-fare may be the way to go more often than not.

A customer asked something along the lines of “how do I create a custom editor—like one of DevExpress’ XtraEditors—to use with a repository”. So the response in the form of a blog was a custom editor (see http://community.devexpress.com/blogs/paulk/archive/2010/03/19/creating-a-custom-repository-editor-with-validation.aspx). Here is the rub: is it necessary to write a custom editor to provide custom editing? The answer is no. You could write custom validation in an event handler and be done with it. You could use the regular expression language and alternation constructs—or conditions—and use more than one expression. The general rule I use is keep it simple if I need something once—which translates into using an event handler or a composite regular expression—and create a new something if I need to reuse a solution in a lot of places—which translates into a custom editor or some other kind of reusable chunk of code.

Knowing how to implement an event handler is a general skill that everyone should know. Implementing a custom XtraEditor is a more specific skill that applies to working with XtraEditors and DevExpress controls. Using Regular Expressions is another good, general skill that is useful for all programmers to know. Implementing an IExtenderProvider is a skill more specific to .NET Framework programming that is useful to know especially if you are interested in extending existing controls, whether DevExpress controls or standard WinForms controls. The first example in this blog demonstrates how to use more than one Regular Expression with our XtraEditors controls to screen for data input in a specific format, and the second half demonstrates how to implement an IExtenderProvider to extend controls in general—but in this instance XtraEditor controls derived from BaseEdit.

Validating a TextEdit Control Using Compound Regular Expressions

XtraEditor controls like CalcEdit, DateEdit, and TextEdit are controls that DevExpress literally provides to customers at no charge (see http://www.devexpress.com/Products/Free/WebRegistration60/). Edit controls can be used to design a form and they appear in container controls like the XtraGrid and XtraTreeList. Derived from the BaseEdit control is the TextEdit control and its descendants. Introduced at the TextEdit level is the Mask property and sub-properties. The Mask property supports entering a variety of edit mask types and masks for preliminary validation. Using Figure 1 as a visual guide you can select the mask type, enter an edit mask or select from a predefined list of masks, chose an auto-complete mode, and experiment with input test data—all at design time.

In the scenario mentioned at the beginning of this blog, the customer asked about entering time values in hh:mm, ##.##, or ## formats to represent elapsed time. The first format was a short format. The second format was hours and fractional hours, and the final format was total elapsed minutes. The solution referenced in the blog above demonstrated a custom editor. By using an alternation construct—an or condition—in the regular expression edit mask you can roughly achieve the same result provided by the custom editor. The following composite regular expression matches all three input types:

(\d*\.\d+|\d+:[0-5][0-9]|\d+)

The first sub-expression matches 0 or more digits, a decimal, and one or more digits. The pipe (|) separates expressions and is treated as an either/or condition. The next sub-expression matches one or more digits, a colon (:), and two digits with the first of the paired digits being between 0 and 5 and the second between 0 and 9. The third expression matches one or more digits. Consequently, unless there is a bug in my expressions this single compound regular expression satisfies the requirements of validating a variety of time inputs.

image
Figure 1: Choose mask type like RegEx, define or select from existing masks, and provide test input all at design time. 

The solution in this part is a little like food at the Loteria—satisfactory, wholesome and a fairly simple presentation. If you use the regular expression mask then you will get the pre-defined behavior provided by the editor when the mask condition isn’t met. If you want custom behavior—for example additional masks—or code that you can package up and share with co-workers then you can create an IExtenderProvider. (Of course, you could email a regular expression too.)

Because regular expressions are represented by a grammar and things like alternation constructs are supported (either/or conditions) you can effectively have multiple expressions for the TextEdit Mask property. However, if you use one of the other mask types like Simple then you will be limited to a single mask. By implementing an IExtenderProvider you can provide new properties for an existing control without inheritance.

IExtenderProvider is an implementation of the Decorator design pattern. IExtenderProvider (Decorator) permits you to add behaviors to an object dynamically. By implementing System.ComponentMode.IExtenderProvider and adorning your provider with a few choice attributes you can effectively extend a category of controls without inheriting from each (or any of those controls). This is how the WinForms ToolTip works—it is an extender.

The way IExtenderProvider works is that you define a class, inherit from Component and IExtenderProvider. This control will be placed in the components tray of your WinForms application and everything the extender extends will dynamically be giving the additional properties as defined by the provider. Because one extender/provider will be providing the additional features for potentially any number of controls the extender/provider will need a way to track the value of the extended properties and the control those values are provided to. The ideal way to do this is a Hashtable. The next challenge is that a specific control’s extended properties will be set one at a time; this means that the control and property values will need to be sent back and forth to the extender/provider. Clearly, if there are multiple arguments then the actual property construct won’t work; methods are needed. The general solution then becomes implement IExtenderProvider, add a Hashtable to store per-control property values, and define methods for passing in the control and its associated extended properties. The final piece is to add metadata that describes what the provided property is and the type of the control being extended.

For the sample solution I elected to define an Expressions property to be associated with BaseEdit controls. Listing 1 contains the solution for the extender/provider followed by an analysis of the code and some notes that may be generally useful if you plan on implementing extender/providers in the future.

Listing 1: My IExtenderProvider provides a string collection as an Expressions property; these strings are treated as regular expressions used for testing the Text property of the editor.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using DevExpress.XtraEditors;
using System.Collections;
using System.Collections.Specialized;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Text.RegularExpressions;

//PROPS to : http://mastersact.blogspot.com/2007/06/string-collection-editor-for-property.html
namespace EditorExtenderProvider
{
  [ProvideProperty("Expressions", typeof(BaseEdit))]
  [Category("Design")]
  public class EditExtender : Component, IExtenderProvider
  {
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    private Hashtable expressions = null;
    public EditExtender()
    {
      expressions = new Hashtable();
    }

    public EditExtender(IContainer parent) : this()
    {
      parent.Add(this);
    }

    [Editor(@"System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
      typeof(System.Drawing.Design.UITypeEditor))]
    public StringCollection GetExpressions(BaseEdit edit)
    {
      StringCollection list = (StringCollection)expressions[edit];
      if(list == null)
      {
        list = new StringCollection();
      }

      return list;
    }

    [Editor(@"System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
      typeof(System.Drawing.Design.UITypeEditor))]
    public void SetExpressions(BaseEdit edit, StringCollection list)
    {
      if (list == null)
        list = new StringCollection();

      expressions[edit] = list;

      edit.Validating += new CancelEventHandler(edit_Validating);
    }

    void edit_Validating(object sender, CancelEventArgs e)
    {
      e.Cancel = false;

      if (sender is BaseEdit == false)
        return;

      // no text nothing to do
      BaseEdit edit = (BaseEdit)sender;
      if (edit.Text == "") return;
      // get the expressions
      StringCollection list = (StringCollection)expressions[sender];
      // no regular expressions for this control
      if (list.Count == 0) return;
      // as soon as it passes one expression bail
      foreach (string pattern in list)
      {
        if (Regex.IsMatch(edit.Text, pattern))
          return;
      }

      e.Cancel = true;
    }

    #region IExtenderProvider Members

    public bool CanExtend(object extendee)
    {
      return extendee is BaseEdit;
    }

    #endregion

  }
}

The ProvidePropertyAttribute is used to name the provided properties and the type of object the properties are provided to. The convention is to use a property name, like Expressions, and then define two methods with a Get and Set prefix to support the provided property. Thus, if the provided property is Expressions then you need the methods GetExpressions and SetExpressions. The Get method accepts an argument of the provided type and returns the property values for that object. The Set method accepts an argument of the provided type and sets the values for that type. The CategoryAttribute can be used to define the category in the Properties window used to organize the provided properties. The DesignerSerializationVisibilityAttribute specifies the type of persistence to use when serializing. DesignerSerializationVisibility.Content instructs the designer to serialize the contents of the adorned item. (In the example, the DesignerSerializationVisibilityAttribute isn’t doing anything other than demonstrating how to apply it. By default properties are Visible unless the DesignerSerializationVisibilityAttribute specifies otherwise.)

The Hashtable is used to pair specific expressions with controls. The control object plays the role of indexer and the string expressions play the role of value. The default constructor creates an instance of the Hashtable. The constructor that takes an IContainer adds the extender to the IContainer and calls the default constructor—: this().

Here is a sticky part. If you define a StringCollection (or something like List<string>) for one of the provided properties the default editor will be the String Collection Editor shown in Figure 2. This editor will not permit you to add strings to it. In fact, if you try the IDE will raise a “Constructor on type ‘System.String’ not found” exception—see Figure 3. The String Collection Editor you want is the one shown in Figure 4. Unfortunately, this editor is tucked away inside the .NET Framework and is only accessible by using the fully qualified assembly name. The application of the EditorAttribute on the GetExpressions and SetExpressions methods means that the String Collection Editor in Figure 4 will be used to edit expression values.

image
Figure 2: The default String Collection Editor will not let you edit extended string collection properties correctly.

image
Figure 3: If you try to use the String Collection Editor in Figure 2 for StringCollection or List<string> objects you will get an IDE exception.

image
Figure 4: This is the String Collection Editor you want, but it is protected in the .NET Framework so you will have to use a fully qualified assembly name to load it with your extender.

GetExpressions will be called when you invoke the Expressions property editor in the IDE. The Hashtable is indexed using the argument control. The Hashtable value is that control’s StringCollection. If the StringCollection is null a new empty one is returned. The SetExpressions method indexes the Hashtable and assigns the argument StringCollection to that hash position and binds an event handler to the edit control. The Validating event handler provides the validation behavior. The sender argument is the editor. The editor is used to get the StringCollection from the Hashtable and each item in the collection is treated as a regular expression, comparing the BaseEdit control’s Text property against the StringCollection expression. As long as one of the expressions passes—see the foreach loop—the input value is deemed to pass validation and the method exits. If no expressions exist then validation passes. If there are some expressions but none pass then validation fails. Finally, the IExtenderProvider.CanExtend method indicates the class of the type being extended. Figure 5 shows how the extender provider will appear in an edit control’s Properties window. The extender provider itself will be a component in the Toolbox you add to the component tray.

image
Figure 5: Extender/providers show up as properties on the extended controls; one extender provider control can provide its extended properties to every control instance of the type it extends.

Over the last few blog posts we took a pretty good look at DevExpress’ XtraEditors. XtraEditors can be used by themselves and are used as editors in container controls like the XtraGrid and XtraTreeList. You can define XtraEditors in internal repositories or external repositories, defining an editor in one place, setting its properties one time, and then using that editor in many places. You can define custom extra editors, use events for validation, use masks for validation, or implement an IExtenderProvider for full, custom control. This is a whole lot of flexibility in what on the surface looks like a tiny little control—a TextEdit control for example—and the XtraEditors Suite contains 35 of the 60 free controls that DevExpress gives away.

Published Mar 30 2010, 10:37 PM by Paul Kimmel (DevExpress)
Bookmark and Share

Comments

 

Twitter Trackbacks for Validating Using Regular Expressions or an IExtenderProvider - Paul Kimmel's Blog [devexpress.com] on Topsy.com said:

Pingback from  Twitter Trackbacks for                 Validating Using Regular Expressions or an IExtenderProvider - Paul Kimmel's Blog         [devexpress.com]        on Topsy.com

March 30, 2010 5:57 PM
 

Crono said:

I use IExtenderProvider a lot, it became an important part of my design time strategy.

I've made a few to extend some of DX's controls. One of them adds OptionsDragDrop and OptionsFooter properties to a GridView and any of it's descendants (namely BandedGridView, AdvBandedGridView, etc...).

I also have a PersistentRepositoryExtender component that allows any standalone XtraEditor control to be bound to a RepositoryItem object stocked in a PersistentRepository component.

I'd be curious to hear about other DX-related extenders people may have done. :)

March 31, 2010 1:52 PM
 

Paul Kimmel (DevExpress) said:

Crono:

Awesome. You should drop by the booth next conference and share your ideas on extenders. Have you blogged somewhere about your extenders?

Paul

March 31, 2010 2:15 PM
 

Crono said:

I live in Quebec, Canada. It usually is too far away from your conferences. :)

I did made a post on DX forum a few months ago, for the RepositoryExtender:

community.devexpress.com/.../83873.aspx

As for the grid, I'm afraid I can't share the code as easily, it has dependencies to some other stuff. It's cool, though! :D Options can be set to allow dragging a row around to reorder it or move it from a group to another. It also scrolls the grid automatically when the mouse gets near its edges.

Footer options kinda morph the footer bar into a status bar, displaying text on the grid's full width. I have a few more options to easily integrate the number of rows showing and number of rows selected.

I wish I could share it. Most likely I could benefit from having a few advices... I'll try to split it from it's dependencies and post it on the forum, if I ever have some spare time (sigh...).

March 31, 2010 5:48 PM
 

Validating Using Regular Expressions or an IExtenderProvider … Mobile said:

Pingback from  Validating Using Regular Expressions or an IExtenderProvider … Mobile

April 1, 2010 1:28 AM
More from DevExpress
Live Chat
Have a pre-sales question?
Need assistance with your evaluation?
We are here to help.
Chat is one of the many ways you can contact members of the DevExpress Team. We are available Monday-Friday between 8:30am and 5:00pm Pacific Time.
If you need additional product information, require pre-sales assistance, or want help with your order, write to us at info@devexpress.com or call us at
+1 (818) 844-3383.