Groups > Asp .Net > ASP.NET Tips and tricks > User controls validation under Asp.Net 2.0




User controls validation under Asp.Net 2.0

User controls validation under Asp.Net 2.0
Thu, 7 Feb 2008 17:21:59 +0000
[This sample was written a while ago as a follow up to the following thread:
http://forums.asp.net/t/989327.aspx. Since I keep receiving (once a month on the
average) requests for the code, I've thought better make it public in this Tips
& Tricks forum.]

This is a full working sample that shows an implementation of client-side as
well as server-side validation for an user control against the built-in Asp.Net
2.0 validation system.

The user control embeds two combo boxes (drop-downs) in cascade. The reason why
I chose cascading combos is that this requires the page to post back in order to
populate the child combo, so introducing an extra complexity that is not trivial
to handle in conjunction with the validation system.

Below you can find the source code for the various components of the sample. For
your convenience, the whole package can be download from my personal site at
http://julio.diegidio.name/Share/UCValidation.zip.

Few technical notes: this code was born as a work-around to the lack of support
to client-side user controls validation under Asp.Net 2.0. Despite the code is a
bit convoluted, as of today I am not aware of any better way to approach the
problem. Please also note that the few comments interspersed in the JavaScript
code are no way enough to understand the details of what's going on, but they
should be enough to point you in the right direction. To get a deeper
understanding, you'll need to dig into the Asp.Net 2.0 built-in JavaScript
validation code which can be easily obtained by inspecting the generated HTML
code (view source from the browser) of any validation enabled web page
(including of course the web page that comes with the sample) where it appears
as a script tag referring to an embedded resource. Copy the url and paste it
into your browser's location box, hit enter and you should be allowed to
download it to your local drive.

This said, should you have any suggestions for improvements of any kind, please
feel free to post a comment here and I'll be glad to check, test and update the
whole thing and give you here and elsewhere full credit for the enhancements.
Should you instead have technical questions, please consider that I won't answer
general questions about the Asp.Net 2.0 built-in validation system, but I'll
provide clarifications where possible about aspects of the sample code that
could reveal hard to understand.

Hope this helps and enjoy! -LV



===== ValidableUserControls.js =======================================

1    /* *** Common support logic for validable user controls *** */
2    3    function vucHookupValidation(hookAnchor, validator, focusCallback)
4    {
5    	hookAnchor.focus = focusCallback;
6    7    	hookAnchor.change = vucValidationChange;
8    	hookAnchor.blur = vucValidationBlur;
9    10   	// Populate for Asp.Net built-in handling
11   	hookAnchor.Validators = new Array();
12   	hookAnchor.Validators[hookAnchor.Validators.length] = validator;
13   }
14   15   function vucValidationChange(hookAnchor)
16   {
17   	var event = new Object();
18   	event.target = hookAnchor;
19   20   	// Call Asp.Net built-in handling
21   	ValidatorOnChange(event);
22   }
23   24   function vucValidationBlur(hookAnchor)
25   {
26   	var event = new Object();
27   	event.target = hookAnchor;
28   29   	// Call Asp.Net built-in handling
30   	ValidatedControlOnBlur(event);
31   }


=============================================================

===== CascadingCombos.ascx.cs =====================================

1    using System;
2    using System.Data;
3    using System.Web.UI;
4    5    [ValidationPropertyAttribute("IsValid")]
6    public partial class CascadingCombos : UserControl
7    {
8    9    public const string VALID_VALUE = "VALID";
10   public const string INVALID_VALUE = "INVALID";
11   12   private int _parentID;
13   private int _childID;
14   private bool _isValid;
15   16   public int ParentID { get { return _parentID; } }
17   public int ChildID { get { return _childID; } }
18   public string IsValid { get { return _isValid ? VALID_VALUE :
INVALID_VALUE; } }
19   20   21   protected void Page_Load(object sender, EventArgs e)
22   	{
23   if(!IsPostBack) loadCombos();
24   else reloadCombos();
25   26   		_parentID = int.Parse(cmbParent.Value);
27   		_childID = int.Parse(cmbChild.Value);
28   29   		_isValid = _childID > 0;
30   	}
31   32   33   private void loadCombos()
34   	{
35   		cmbParent.DataSource = getParentTable();
36   		cmbParent.DataValueField = "ID";
37   		cmbParent.DataTextField = "Description";
38   		cmbParent.DataBind();
39   40   int parentID = int.Parse(cmbParent.Value);
41   		ViewState["ParentID"] = parentID;
42   43   		cmbChild.DataSource = getChildTable(parentID);
44   		cmbChild.DataValueField = "ID";
45   		cmbChild.DataTextField = "Description";
46   		cmbChild.DataBind();
47   	}
48   49   private void reloadCombos()
50   	{
51   int savedParentID = (int)ViewState["ParentID"];
52   int parentID = int.Parse(cmbParent.Value);
53   if(savedParentID != parentID)
54   		{
55   			ViewState["ParentID"] = parentID;
56   57   			cmbChild.DataSource = getChildTable(parentID);
58   			cmbChild.DataValueField = "ID";
59   			cmbChild.DataTextField = "Description";
60   			cmbChild.DataBind();
61   		}
62   	}
63   64   65   /* *** Data layer *** */66   67   private DataTable
getParentTable()
68   	{
69   const int ROW_COUNT = 8;
70   71   		DataTable table = new DataTable();
72   		table.Columns.Add("ID", typeof(int));
73   		table.Columns.Add("Description", typeof(string));
74   75   		DataRow row = table.NewRow();
76   		row["ID"] = -1;
77   		row["Description"] = "---- Select parent ----";
78   		table.Rows.Add(row);
79   80   for(int id = 1, i = 0; i < ROW_COUNT; id++, i++)
81   		{
82   			row = table.NewRow();
83   			row["ID"] = id;
84   			row["Description"] = "Parent_" + id;
85   			table.Rows.Add(row);
86   		}
87   88   return table;
89   	}
90   91   private DataTable getChildTable(int parentID)
92   	{
93   const int ROW_COUNT = 8;
94   95   		DataTable table = new DataTable();
96   		table.Columns.Add("ID", typeof(int));
97   		table.Columns.Add("Description", typeof(string));
98   99   		DataRow row = table.NewRow();
100  		row["ID"] = -1;
101  		row["Description"] = "---- Select child ----";
102  		table.Rows.Add(row);
103  104  if(parentID > 0)
105  		{
106  for(int id = 1, i = 0; i < ROW_COUNT; id++, i++)
107  			{
108  				row = table.NewRow();
109  				row["ID"] = id;
110  				row["Description"] = "Child_" + parentID +
"_" + id;
111  				table.Rows.Add(row);
112  			}
113  		}
114  115  return table;
116  	}
117  118  }


============================================================

===== CascadingCombos.ascx ======================================

1    <%@ Control Language="C#"
CodeFile="CascadingCombos.ascx.cs"
Inherits="CascadingCombos" %>2    3    <script
type="text/javascript"
src="ValidableUserControls.js"></script>
4    5    <script type="text/javascript">
6    /* <![CDATA[ */
7    8    /* *** Hookup logic *** */
9    10   function ccHookupValidation(validator)
11   {
12   	var hookAnchor = document.getElementById("<%= this.ClientID
%>");
13   14   	hookAnchor.value ="<%= this.IsValid %>";
15   16   	vucHookupValidation(hookAnchor, validator, ccFocusCallback);
17   }
18   19   function ccFocusCallback()
20   {
21   	var cmbParent = document.getElementById("<%= cmbParent.ClientID
%>");
22   	var parentID = parseInt(cmbParent.value, 10);
23   	if(parentID <= 0) { cmbParent.focus(); return; }
24   25   	var cmbChild = document.getElementById("<%= cmbChild.ClientID
%>");
26   	cmbChild.focus();
27   }
28   29   /* ]]> */
30   </script>
31   32   <script type="text/javascript">
33   /* <![CDATA[ */
34   35   /* *** Combos logic *** */
36   37   function ccParentChange(oCmb)
38   {
39   	oCmb.form.submit();
40   }
41   42   function ccChildChange(oCmb)
43   {
44   	if(Page_ValidationActive != true) return;
45   46   	var hookAnchor = document.getElementById("<%= this.ClientID
%>");
47   48   	var childID = parseInt(oCmb.value, 10);
49   	var isValid = childID > 0;
50   51   	hookAnchor.value = isValid ?
52   		"<%= CascadingCombos.VALID_VALUE %>" :
53   		"<%= CascadingCombos.INVALID_VALUE %>";
54   55   	hookAnchor.change(hookAnchor);
56   }
57   58   function ccBlur()
59   {
60   	if(Page_ValidationActive != true) return;
61   62   	var hookAnchor = document.getElementById("<%= this.ClientID
%>");
63   64   	hookAnchor.blur(hookAnchor);
65   }
66   67   /* ]]> */
68   </script>
69   70   <div>
71   <!-- Hook anchor -->72   	<a id="<%= this.ClientID
%>"></a>
73   </div>
74   75   <table>
76   	<tr>
77   		<td>Parent:</td>
78   		<td><select id="cmbParent" runat="server"79 
 				onchange="ccParentChange(this)"80  
				onkeyup="ccParentChange(this)"81   				onblur="ccBlur()"
/></td>
82   	</tr>
83   	<tr>
84   		<td>Child:</td>
85   		<td><select id="cmbChild" runat="server"86  
				onchange="ccChildChange(this)"87  
				onkeyup="ccChildChange(this)"88   				onblur="ccBlur()"
/></td>
89   	</tr>
90   </table>


============================================================

===== UCValidationSample.aspx ====================================

1    <%@ Page Language="C#" EnableViewState="true" %>2
   3    <%@ Register TagPrefix="vuc"
TagName="CascadingCombos" Src="CascadingCombos.ascx" %>4 
  5    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6    7    <script runat="server">
8    9    	protected void Page_Load(object sender, EventArgs e)
10   	{
11   		if(!IsPostBack) DataBind();
12   	}
13   14   </script>
15   16   <html xmlns="http://www.w3.org/1999/xhtml" >
17   	<head runat="server">
18   		<title>User controls validation sample</title>
19   		<meta http-equiv="Content-Type" content="text/html;
charset=utf-8" />
20   	</head>
21   	<body>
22   		<form id="form1" runat="server">
23   24   <div><vuc:CascadingCombos ID="myCascadingCombos"
runat="server" /></div>
25   26   <div><asp:RequiredFieldValidator
ID="reqMyCascadingCombos" runat="server"27  
	ControlToValidate="myCascadingCombos"28   	InitialValue="<%#
CascadingCombos.INVALID_VALUE %>"29   	Text="VALIDATION
ERROR"30   	SetFocusOnError="true" /></div>
31   32   <script type="text/javascript">
33   /* <![CDATA[ */
34   35   var myCcValidator = document.getElementById("<%=
reqMyCascadingCombos.ClientID %>");
36   37   ccHookupValidation(myCcValidator);
38   39   /* ]]> */
40   </script>
41   42   <div><input type="submit" value="Submit"
runat="server" /></div>
43   44   <pre>
45   =================================================================
46   Debug info:
47   -----------------------------------------------------------------
48   ParentID = <%= myCascadingCombos.ParentID %>49   ChildID  = <%=
myCascadingCombos.ChildID %>50   IsValid  = <%= myCascadingCombos.IsValid
%>51   =================================================================
52   =================================================================
53   Test steps to assert the validation system is working:
54   -----------------------------------------------------------------
55   56   1) Load this page from scratch
57          <i>Note: You could also reload the page (F5 or similar),
58          but make sure all fields are clear and no focus is set</i>
59   60   2) Click the Submit button and check outcomes:
61   62       2.A. The validation error label should show; this proves
63            the validation system has correctly hooked up
64   65       2.B. Focus should go to the parent combo; this proves
66            the validation system is correctly setting focus
67   68   3) Press the TAB key and check outcomes:
69   70       3.A. Focus should go to the child combo and then automatically
71            switch back to the parent combo; this proves the
72            validation system is correctly handling the blur event
73   74   =================================================================
75   </pre>
76   77   		</form>
78   	</body>
79   </html>


=====================================================
Post Reply
about | contact