How to integrate Peter Blum's validation controls into the ASP.NET wizard.

by Paul Smith 16. October 2008 12:38

I came across an interesting problem recently when trying to take advantage of Peter Blum's excellent validation controls. Since the standard ASP.NET Wizard control uses standard button controls none of Peters Validation controls fire, unless you use one of his button controls or use his NativeControlExtender.

One option available is to define navigation templates for your Wizard but this can be a pain if you have several Wizards across your site. I wanted my own control that did all the validation and I wanted to add a validation group at the same time. So I created a control that inherited the standard wizard control

   1: using System;
   2: using System.Web.UI;
   3: using System.Web.UI.WebControls;
   4: using System.ComponentModel;
   5:  
   6: namespace Lib
   7: {
   8:     [ToolboxData("<{0}:CustomWizard runat=server></{0}:CustomWizard>")]
   9:     public class CustomWizard : Wizard
  10:     {
  11:     }
  12: }

First step was to add a property to hold the validation group

   1: [Description("The validation group of the next/previous buttons")]
   2: public string ValidationGroup
   3: {
   4:     get
   5:     {
   6:         object o = ViewState["ValidationGroup"];
   7:         return (o == null) ? string.Empty : (string)o;
   8:     }
   9:     set
  10:     {
  11:         ViewState["ValidationGroup"] = value;
  12:     }
  13: }

Then I need to find the buttons within the Wizard add a NativeControlExtender and assign the button to it, then set the validation group on each. Finding the buttons was a bit of pain and it's not the prettiest code (I'll tidy it up when I get the chance). Note the lines of code that make the appropriate button visible, this is important as the client side code will not render unless the control is visible.

   1: protected override void OnLoad(EventArgs e)
   2: {
   3:     Table table = (Table)this.Controls[0];
   4:     int x = 0;
   5:     
   6:     // The navigation buttons are in the last table row...
   7:     foreach (Control control in table.Rows[table.Rows.Count - 1].Cells[0].Controls)
   8:     {
   9:         // Each of the navigation containers is a table
  10:         // There'll be a prettier way of doing this.
  11:         if (control.Controls[0] is Table)
  12:         {
  13:             Table t = (Table)control.Controls[0];
  14:  
  15:             // The buttons exist in the table cells
  16:             foreach (TableCell cell in t.Rows[0].Cells)
  17:             {
  18:                 // There is a button of each type LinkButton/Button/ImageButton
  19:                 foreach (IButtonControl ctrl in cell.Controls)
  20:                 {
  21:                     // Add a validation group to the button
  22:                     ctrl.ValidationGroup = this.ValidationGroup;
  23:  
  24:                     if (ctrl is LinkButton)
  25:                     {
  26:                         // The button you want to use must be visible at this stage for the client side
  27:                         // script to get rendered. The wizard itself doesn't make them visible until the render method.
  28:                         ((Control)ctrl).Visible = true;
  29:  
  30:                         // Create a native control extender and assign the button as the control to extend
  31:                         PeterBlum.DES.NativeControlExtender nce = new PeterBlum.DES.NativeControlExtender();
  32:                         nce.ID = this.ID + "_" + (ctrl as Control).ID + x.ToString() + "_NCE";
  33:                         nce.ControlToExtend = (Control)ctrl;
  34:                         nce.Group = this.ValidationGroup;
  35:  
  36:                         // Add the NativeControlExtender to the Wizard, can't use this.Conrols.Add() as the
  37:                         // wizard does not allow it's controls collection to be modified. So I just added them 
  38:                         // in the first row as they don't render any HTML
  39:                         table.Rows[0].Cells[0].Controls.Add(nce);
  40:  
  41:                         x++;
  42:                     }
  43:                 }
  44:             }
  45:         }
  46:     }
  47: }

That handles the client side validation the final step was to add code to handle server side validation. That was a pretty straight forward affair.

   1: protected override void OnNextButtonClick(WizardNavigationEventArgs e)
   2: {
   3:     this.Page.Validate(this.ValidationGroup);
   4:     PeterBlum.DES.Globals.Page.Validate(this.ValidationGroup);
   5:  
   6:     if (!this.Page.IsValid && !PeterBlum.DES.Globals.Page.IsValid)
   7:     {
   8:         e.Cancel = true;
   9:         return;
  10:     }
  11:  
  12:     base.OnNextButtonClick(e);
  13: }
  14:  
  15: protected override void OnFinishButtonClick(WizardNavigationEventArgs e)
  16: {
  17:     this.Page.Validate(this.ValidationGroup);
  18:     PeterBlum.DES.Globals.Page.Validate(this.ValidationGroup);
  19:  
  20:     if (!Page.IsValid && !PeterBlum.DES.Globals.Page.IsValid)
  21:     {
  22:         e.Cancel = true;
  23:         return;
  24:     }
  25:  
  26:     base.OnFinishButtonClick(e);
  27: }

Putting it all together you have a custom Wizard that will handle validation groups and Peter Blum's validation control is a relatively tidy manner.

Thanks to Peter Blum for his excellent support.

Download the code for this post: CustomWizard.zip (596.00 bytes)

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

ASP.NET

Quick Data Binding to an Enumeration

by Paul Smith 2. October 2008 12:05

I recently got tired of writing the same code (below) to bind enumeration values to a control in ASP.NET.

   1: foreach (int value in Enum.GetValues(typeof(EnumName)))
   2: {
   3:     string name = Enum.GetName(typeof(EnumName), value);
   4:     li = new ListItem();
   5:     li.Text = name;
   6:     li.Value = value.ToString();
   7:     ListControl.Items.Add(li);
   8: }

So I decided to write a quick helper class to deal with sort of operation. Returning it as a DataTable made the most sense since it we easy to modify the headings for two-way data binding. Anyway I've posted the code below

   1: public class EnumUtilities<T> where T : struct
   2:     {
   3:         static EnumUtilities()
   4:         {
   5:             if (!typeof(T).IsEnum)
   6:             {
   7:                 throw new ArgumentException("Parameter T must be of type Enum");
   8:             }
   9:         }
  10:  
  11:         /// <summary>
  12:         /// Returns the Enumeration as a DataTable
  13:         /// </summary>
  14:         /// <param name="valueHeader">The name of the columns that contains the enumeration value</param>
  15:         /// <param name="textHeader">The name of the columns that contains the enumeration text</param>
  16:         public static DataTable GetAsDataTable(string valueHeader, string textHeader)
  17:         {
  18:             DataTable dt = new DataTable();
  19:  
  20:             dt.Columns.Add(valueHeader, typeof(int));
  21:             dt.Columns.Add(textHeader, typeof(string));
  22:  
  23:             foreach (int val in Enum.GetValues(typeof(T))) 
  24:             {
  25:                 dt.Rows.Add(new object[] { val, GetName(val) });
  26:             }
  27:  
  28:             return dt;
  29:         }
  30:  
  31:         /// <summary>
  32:         /// Parses a string to it's enumeration equivalent
  33:         /// </summary>
  34:         /// <param name="memberName"></param>
  36:         public static T Parse(string memberName)
  37:         {
  38:             return (T)Enum.Parse(typeof(T), memberName);
  39:         }
  40:  
  41:         /// <summary>
  42:         /// Gets the text value of an enumeration value from it's integer value
  43:         /// </summary>
  44:         /// <param name="value"></param>
  45:         /// <returns></returns>
  46:         public static string GetName(int value)
  47:         {
  48:             return Enum.GetName(typeof(T), value);
  49:         }
  50:     }

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , ,

ASP.NET | C#

Where is the Enum Generic Constraint??

by Paul Smith 1. October 2008 15:16

A frustration about the Generics framework in .NET is that it's not possible to specify a generic constraint to be any enumeration.

   1: public class SomeClassName<T> where : Enum
   2: {
   3:     // ...
   4: }

A workaround I use to enforce the constraint, although not pretty is below. Unfortunately it will not raise a build error if anyone has any better ideas please let me know.

   1: public class SomeClassName<T> where T : struct
   2: {
   3:     public SomeClassName()
   4:     {
   5:         if (!typeof(T).IsEnum)
   6:         {
   7:             throw new ArgumentException("Parameter T must be of type Enum");
   8:         }
   9:     }
  10: }

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

C#

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen