:::: MENU ::::

Wednesday, October 29, 2008

If you are a web developer you would definately have worked with the querystring. Most of the time you are just getting values from the querystring or adding querystring values to url's, but in some cases the querystring can really become a hassle to work with. A simple example of this can be seen in the following scenario :

I have a control that contains a list of data displayed in a table. The current URL has no querystring by default, and in that case the first item in the grid is selected. Each row in the table contains a link to the same page, but a querystring is used to determine the selected row. On the itemdatabound event of the repeater I was setting the URL of a hyperlink on each row. Now I know this is a really simple example and using the itemdatabound event is overkill, but it's only an example ;) Now when setting the URL, I have to worry about if the same querystring value already exists in the current URL and if so replace it, else add it. (I hope you see where I'm going here.)

Introducing the Querystring builder class. The class can be easily used to build up querystrings (from scratch, the existing URL or any string in fact). I also made it chainable (like the stringbuilder class). You can also encrypt and decrypt the querystring with ease (it uses my Cryptography classes). Here are a few examples of it's use:

//create a querystring from the current URL, add 'id','user' and 'sessionId' values and remove an 'action' value
//output : "?id=123&user=brad&sessionId=ABC"
string strQuery = QueryString.Current.Add("id", "123").Add("user", "brad").Add("sessionId", "ABC").Remove("action").ToString();

//take an existing string and replace the 'id' value if it exists (which it does)
//output : "?id=5678&user=tony"
strQuery = new QueryString("id=1234&user=tony").Add("id", "5678", true).ToString();

//create a querystring from the current URL, add an 'id' value and encrypt the result
//output : "?DhSbRo10vxUjYC5ChMXO5Q%3d%3d=dkxaLXpSg6aeM71fhHJ4ZQ%3d%3d"
strQuery = QueryString.Current.Add("id", "123").Encrypt("my key").ToString();

//takes a previous querystring value, decrypts it using the same key and gets the 'id' value
//output : "123"
strQuery = new QueryString(strQuery).Decrypt("my key")["id"];

Now obviously you cannot edit the current querystring, but the class is useful when manipulating the current querystring for other links or URL's. Here is the full code :

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Collections.Specialized;

namespace Utils.Web
{
    /// <summary>
    /// A chainable query string helper class.
    /// Example usage :
    /// string strQuery = QueryString.Current.Add("id", "179").ToString();
    /// string strQuery = new QueryString().Add("id", "179").ToString();
    /// </summary>
    public class QueryString : NameValueCollection
    {
        public QueryString() { }

        public QueryString(string queryString)
        {
            FillFromString(queryString);
        }

        public static QueryString Current
        {
            get
            {
                return new QueryString().FromCurrent();
            }
        }

        /// <summary>
        /// extracts a querystring from a full URL
        /// </summary>
        /// <param name="s">the string to extract the querystring from</param>
        /// <returns>a string representing only the querystring</returns>
        public string ExtractQuerystring(string s)
        {
            if (!string.IsNullOrEmpty(s))
            {
                if (s.Contains("?"))
                    return s.Substring(s.IndexOf("?") + 1);
            }
            return s;
        }

        /// <summary>
        /// returns a querystring object based on a string
        /// </summary>
        /// <param name="s">the string to parse</param>
        /// <returns>the QueryString object </returns>
        public QueryString FillFromString(string s)
        {
            base.Clear();
            if (string.IsNullOrEmpty(s)) return this;
            foreach (string keyValuePair in ExtractQuerystring(s).Split('&'))
            {
                if (string.IsNullOrEmpty(keyValuePair)) continue;
                string[] split = keyValuePair.Split('=');
                base.Add(split[0],
                    split.Length == 2 ? split[1] : "");
            }
            return this;
        }

        /// <summary>
        /// returns a QueryString object based on the current querystring of the request
        /// </summary>
        /// <returns>the QueryString object </returns>
        public QueryString FromCurrent()
        {
            if (HttpContext.Current != null)
            {
                return FillFromString(HttpContext.Current.Request.QueryString.ToString());
            }
            base.Clear();
            return this;
        }

        /// <summary>
        /// add a name value pair to the collection
        /// </summary>
        /// <param name="name">the name</param>
        /// <param name="value">the value associated to the name</param>
        /// <returns>the QueryString object </returns>
        public new QueryString Add(string name, string value)
        {
            return Add(name, value, false);
        }

        /// <summary>
        /// adds a name value pair to the collection
        /// </summary>
        /// <param name="name">the name</param>
        /// <param name="value">the value associated to the name</param>
        /// <param name="isUnique">true if the name is unique within the querystring. This allows us to override existing values</param>
        /// <returns>the QueryString object </returns>
        public QueryString Add(string name, string value, bool isUnique)
        {
            string existingValue = base[name];
            if (string.IsNullOrEmpty(existingValue))
                base.Add(name, HttpUtility.UrlEncodeUnicode(value));
            else if (isUnique)
                base[name] = HttpUtility.UrlEncodeUnicode(value);
            else
                base[name] += "," + HttpUtility.UrlEncodeUnicode(value);
            return this;
        }

        /// <summary>
        /// removes a name value pair from the querystring collection
        /// </summary>
        /// <param name="name">name of the querystring value to remove</param>
        /// <returns>the QueryString object</returns>
        public new QueryString Remove(string name)
        {
            string existingValue = base[name];
            if (!string.IsNullOrEmpty(existingValue))
                base.Remove(name);
            return this;
        }

        /// <summary>
        /// clears the collection
        /// </summary>
        /// <returns>the QueryString object </returns>
        public QueryString Reset()
        {
            base.Clear();
            return this;
        }

        /// <summary>
        /// Encrypts the keys and values of the entire querystring acc. to a key you specify
        /// </summary>
        /// <param name="key">the key to use in the encryption</param>
        /// <returns>an encrypted querystring object</returns>
        public QueryString Encrypt(string key)
        {
            QueryString qs = new QueryString();
            Utils.Cryptography.Encryption enc = new Utils.Cryptography.Encryption();
            enc.Password = key;
            for (var i = 0; i < base.Keys.Count; i++)
            {
                if (!string.IsNullOrEmpty(base.Keys[i]))
                {
                    foreach (string val in base[base.Keys[i]].Split(','))
                        qs.Add(enc.Encrypt(base.Keys[i]), enc.Encrypt(HttpUtility.UrlDecode(val)));
                }
            }
            return qs;
        }

        /// <summary>
        /// Decrypts the keys and values of the entire querystring acc. to a key you specify
        /// </summary>
        /// <param name="key">the key to use in the decryption</param>
        /// <returns>a decrypted querystring object</returns>
        public QueryString Decrypt(string key)
        {
            QueryString qs = new QueryString();
            Utils.Cryptography.Encryption enc = new Utils.Cryptography.Encryption();
            enc.Password = key;
            for (var i = 0; i < base.Keys.Count; i++)
            {
                if (!string.IsNullOrEmpty(base.Keys[i]))
                {
                    foreach (string val in base[base.Keys[i]].Split(','))
                        qs.Add(enc.Decrypt(HttpUtility.UrlDecode(base.Keys[i])), enc.Decrypt(HttpUtility.UrlDecode(val)));
                }
            }
            return qs;
        }

        /// <summary>
        /// overrides the default
        /// </summary>
        /// <param name="name"></param>
        /// <returns>the associated decoded value for the specified name</returns>
        public new string this[string name]
        {
            get
            {
                return HttpUtility.UrlDecode(base[name]);
            }
        }

        /// <summary>
        /// overrides the default indexer
        /// </summary>
        /// <param name="index"></param>
        /// <returns>the associated decoded value for the specified index</returns>
        public new string this[int index]
        {
            get
            {
                return HttpUtility.UrlDecode(base[index]);
            }
        }

        /// <summary>
        /// checks if a name already exists within the query string collection
        /// </summary>
        /// <param name="name">the name to check</param>
        /// <returns>a boolean if the name exists</returns>
        public bool Contains(string name)
        {
            string existingValue = base[name];
            return !string.IsNullOrEmpty(existingValue);
        }

        /// <summary>
        /// outputs the querystring object to a string
        /// </summary>
        /// <returns>the encoded querystring as it would appear in a browser</returns>
        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            for (var i = 0; i < base.Keys.Count; i++)
            {
                if (!string.IsNullOrEmpty(base.Keys[i]))
                {
                    foreach (string val in base[base.Keys[i]].Split(','))
                        builder.Append((builder.Length == 0) ? "?" : "&").Append(HttpUtility.UrlEncodeUnicode(base.Keys[i])).Append("=").Append(val);
                }
            }
            return builder.ToString();
        }
    }
}

 

More

DevExpress and Microsoft announced a partnership today to offer CodeRush Xpress for free. At the present time it’s only available for non-express versions of Visual Studio and will only work with C# (per their post). I’m sure the other support will be just around the corner. If you’re interested be sure to download Xpress here.

Tuesday, October 28, 2008

Do you know the C# Anonymous Object? If you don’t know this concept, I can suggest  this url to you http://msdn.microsoft.com/en-us/library/bb397696.aspx

In the previous Tips , we introduced how do we use the interface in LINQ. We did not use the new key word “var” on purpose. In this tips , we will use the var to show how powerful and cool the anonymous object could be.

We have one interface and one method to populate a list of the data for us.

   1:  public interface IName
   2:      {
   3:          string FirstName{get;set;}
   4:          string LastName { get; set; }
   5:          string Prefix { get; set; }
   6:      }
   7:   
   8:      public class Name : IName
   9:      {
  10:          #region IName Members
  11:   
  12:          public string FirstName
  13:          {
  14:              get;
  15:              set;
  16:          }
  17:   
  18:          public string LastName
  19:          {
  20:              get;
  21:              set;
  22:          }
  23:   
  24:          public string Prefix
  25:          {
  26:              get;
  27:              set;
  28:          }
  29:   
  30:          

At my previous employer, one of the early decisions that had huge payoffs later was the SQL naming conventions. A good naming convention is more than just a nicety. It lets you write programs that don’t need to be told about the relationships among tables and columns.

There are many ways to do this, I think. But in practice, I think I’ve seen only a few customer systems that have a completely consistent, logical naming convention. And there are levels of convenience; some systems have a couple extra rules that make a big difference.

In this post I’ll explain the components of my ideal naming conventions.

  • Consistent letter case. I prefer all lowercase for readability and type-ability. Regardless, you need the same case for both table and column names, unless your chosen programming language is case-insensitive (and if it is, you should be flogged for using it).
  • This isn’t mandatory, but word separators (underscores) are pretty nice. If you run things together, you can’t tell the difference between “API rate sheet” and “a pirate sheet.” Yeah, that’s a real example.
  • If a table has a single-column primary key, such as an auto-increment column, then that column is named the same thing as the table. For example, the user table’s primary key is called user. Naming it id or user_id or anything else may be logical and consistent too, but in my experience it leads to a lot more code.
  • If a column expresses a relationship among tables, name it the same as the related table. For example, a table of blog posts should have a column called user which is a foreign key to the user table.
  • Singular. Both table and column names are singular. Plurals add a ton of complexity, and defeat the niceness of naming columns and tables the same thing. Fooling around with conversions between plural and singular (goose/geese, moose/moose, cow/cattle) is a waste of synapses and code.

Sakila’s convention

That’s not a lot of rules, is it? Let’s see how the Sakila sample database would fare if these rules were applied to it. Two of the core tables are actor and film, with the “acted in” relationship expressed in the film_actor table. The tables look like this (simplified):

CREATE TABLE actor (
  actor_id smallint unsigned NOT NULL auto_increment,
  first_name varchar(45) NOT NULL,
  last_name varchar(45) NOT NULL,
  PRIMARY KEY  (actor_id)
);
 
CREATE TABLE film (
  film_id smallint unsigned NOT NULL auto_increment,
  title varchar(255) NOT NULL,
  description text,
  ... other columns ...
  PRIMARY KEY(film_id)
);
 
CREATE TABLE film_actor (
  actor_id smallint unsigned NOT NULL,
  film_id smallint unsigned NOT NULL,
  PRIMARY KEY  (actor_id,film_id)
);

What’s right about this

This is already a pretty nice convention. For example, tables are singular, and the columns that have the same meaning have the same name everywhere. This means you can write

select actor.first_name
from actor
   inner join film_actor using(actor_id)
   inner join film using(film_id);

The ability to use the USING keyword in a join is one way to test whether your naming convention makes sense. If you had gone with the “every primary key is named id, and foreign keys are named [table]_id” convention that’s pretty common, you’d have to write

from actor
   inner join film_actor on actor.id = film_actor.actor_id
   inner join film on film.id = film_actor.film_id;

This is not nearly as elegant. So Sakila’s naming convention is pretty nice already.

What I’d change about Sakila

If I had designed Sakila, I’d have done this:

CREATE TABLE actor (
  actor smallint unsigned NOT NULL auto_increment,
  first_name varchar(45) NOT NULL,
  last_name varchar(45) NOT NULL,
  PRIMARY KEY  (actor)
);
 
CREATE TABLE film (
  film smallint unsigned NOT NULL auto_increment,
  title varchar(255) NOT NULL,
  description text,
  ... other columns ...
  PRIMARY KEY(film)
);
 
CREATE TABLE cast (
  actor smallint unsigned NOT NULL,
  film smallint unsigned NOT NULL,
  PRIMARY KEY  (actor, film)
);

It’s not a dramatic change in this case, and it doesn’t really simplify the example queries a lot, but consider what happens when you write an ORM on top of this simplified naming convention.

As an example, suppose your database has accounts that belong to clients, each of which is managed by a single employee. Look at the following code snippet:

$acc = new Account($account_no);
$email = new Email();
$email->to($acc->client->employee->email);
$email->body("Account $acc for client $acc->client is expired");
$email->send();

If the table and column names match, such an ORM is really easy to build. If they don’t, there’s more code to write.

It’s hard to estimate the reduction in lines of code, tests, and mistakes, but I think it’s pretty significant; some five-line programs I’ve written might have needed thousands of lines of code without the naming conventions, and I’m sure a lot of code would have needed supporting meta-data tables to define the mappings between different types of data.

Summary

Here are a couple of concrete ideas. With the conventions I’ve shown, it’s easy to write a simple recursive program that can examine your entire database for data consistency, based only on naming conventions. And you can easily write a program to dump an account and all its related data (client, employee, and so on) for such purposes as migrating a client to a new shard or creating a dataset for a test suite.

There are many good ways to do this, and your favorite method probably has lots to recommend it. But after having worked with lots of such systems myself (including one company who mandated that column names had to be globally unique, which was horrible), I still haven’t seen anything better than the simple conventions I’ve described above. It’s kind of a reductionistic “let’s make this absolutely as simple as possible” philosophy, and it really pays off.

 

Monday, October 27, 2008

Introduction:
Last month Matt Berseth posted very good post about how to build Master-Detail with the GridView, DetailsView and ModalPopup Controls. Today I'm going to clone his post and build the same feature using jQuery with jQuery Plugins; one for popup windows jqModal& and the other is for Color Animation. You can view a demo of this sample here [View Demo].

Prerequisites:
Because I'm using some design tips and styles posted in Matt's posts, I recommend to return to his original posts regarding styling and UI enhancement. I used the styles and design shown on his post Building a VS2008 Styled Grid with the GridView Control.

In my sample I'm using UpdatePanel, and used a client side technique to update the UpdatePanel. To read and review more about this technique please read Dave's post Easily refresh an UpdatePanel, using JavaScript.

It is important also to review documentation of jqModalas I'm not going to explain its APIs.

Implementation:
To make a long story short, I just modified Matt's sample and replaced ModalPopup control of AJAX Control Toolkit with jqModal. Also Matt used to indicate the updated row by setting a style sheet class to the updated row for certain period of time then remove it to make it look as before. I did the same, but I used some kind of animation provided by Color Animationplugin for jQuery.

The GridView which displays Products List is placed inside an UpdatePanel. Each record will display edit button. When the edit button is clicked it will set a hidden field with ProductID then invoke client script to update another UpdatePanel that contains DetailsView control that will be used to display Product Detail for update. It is important to note that the button doesn't cause a PostBack, instead it calls __doPostBack JavaScript method with UpdatePanel as a parameter to force Async Request to be initiated. The Server will process the request and the DetailsView will bind Product Details. When the Async Request Ends, jqModal will be invoked to show a modal popup that carries the DetailsView Control to show editable Product Detail.

On the popup window, user will be able to edit the data then save or close to discard changes. If Save is Clicked, recored will be saved in the database and modal window will disappear, then the GridView will display a color fading indicator on the updated row.

 

More

 

nums are great. They provide additional type safety and make code more readable.

Converting enums to strings is trivial. Converting strings to enums is not as straightforward...so here is a code snippet:

// This enum is in the .Net framework

public enum DayOfWeek

{

    Sunday = 0,

    Monday = 1,

    Tuesday = 2,

    Wednesday = 3,

    Thursday = 4,

    Friday = 5,

    Saturday = 6,

}

 

protected void Button1_Click(object sender, EventArgs e)

{

    // converting enums to strings is easy

    String WhatDayItIs = DayOfWeek.Monday.ToString();

 

    // converting strings to enums is a bit more work

    DayOfWeek WhatDayItIsDOW;

    WhatDayItIsDOW = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), WhatDayItIs);

}

 

 

Friday, October 24, 2008

The BaseValidator class defines the basic implementation needed for all Validation controls. There are 6 Validation Controls included in the ASP.NET 2.0 and ASP.NET 3.5 framework which validate controls to prevent the users from entering wrong data. However, there are a few shortcomings in these controls. The good part is that ASP.NET provides the framework to create new validation controls. If you would like to create your own validation control, you can do so by deriving a new control from the BaseValidator abstract class. In this article, we will explore how to create a custom validation control in ASP.NET and provide both Server and Client Side Validation for the same.

 

Step 1: Create an ASP.NET Website (File > New > Website). Drag and drop a CheckBoxList, RadioButtonList and a Button control on to the page.

 

Step 2: Let us first complete the task of populating the CheckBoxList and RadioButtonList  controls in the Page_Load event as shown below:

C#

 

protected void Page_Load(object sender, EventArgs e)

{

    if (!Page.IsPostBack)

    {

        List<string> lstStr = new List<string>();

        lstStr.Add("Item 1");

        lstStr.Add("Item 2");

        lstStr.Add("Item 3");

        lstStr.Add("Item 4");

        CheckBoxList1.DataSource = lstStr;

        CheckBoxList1.DataBind();

        RadioButtonList1.DataSource = lstStr;

        RadioButtonList1.DataBind();           

    }

}

VB.NET

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        If (Not Page.IsPostBack) Then

            Dim lstStr As List(Of String) = New List(Of String)()

            lstStr.Add("Item 1")

            lstStr.Add("Item 2")

            lstStr.Add("Item 3")

            lstStr.Add("Item 4")

            CheckBoxList1.DataSource = lstStr

            CheckBoxList1.DataBind()

            RadioButtonList1.DataSource = lstStr

            RadioButtonList1.DataBind()

        End If

    End Sub

 

Step 3: Our next step would be to create the custom validation control. But before that, let us quickly take an overview of what we are trying to achieve. This sample will demonstrate that the user should select at least one checkbox and one radiobutton before submitting the form to the server. For this purpose, we will first show how to create a custom validation control to perform server-side validation and then extend the control to support client-side validation too.

Note: If you are wondering why we have chosen to create a custom validation control instead of using a CustomValidator, then I would state that the main reason for doing so is ‘reusability’. You would soon see that we will be using the same custom validation control to validate both the CheckBoxList as well as RadioButtonList. It is as simple as registering the assembly and start using it.

 

Step 4: To create our custom validation control, Right click the project > Add New Item > From the Template, choose ‘Class’ > Rename the class to ‘ListValidator’ or anything you like > Choose the Language (C# or VB) > Click on Add.

Note: Visual Studio would prompt you to add the class to the App_Code. Choose yes to do so.

 

Step 5: As already discussed, we will be deriving from the BaseValidator abstract class. You will need to implement the ‘EvaluateIsValid’ method which returns a Boolean value indicating if the field/control being validated is valid or not. The code for the server side validation will be similar to the following:

 

Server-Side Validation

C#

namespace CustomListValidator

{

///<summary>

/// Summary description for ListValidator

///</summary>

public class ListValidator : BaseValidator

{

    public ListValidator()

    {

 

    }

 

    protected override bool ControlPropertiesValid()

    {

        Control ctrl = FindControl(ControlToValidate) as ListControl;

        return (ctrl != null);

    }

 

    protected override bool EvaluateIsValid()

    {

        return this.CheckIfItemIsChecked();

    }

 

    protected bool CheckIfItemIsChecked()

    {

        ListControl listItemValidate = ((ListControl)this.FindControl(this.ControlToValidate));

        foreach (ListItem listItem in listItemValidate.Items)

        {

            if (listItem.Selected == true)

                return true;

        }

        return false;

    }

 

}

}

VB.NET

Namespace CustomListValidator

    ''' <summary>

    ''' Summary description for ListValidator

    ''' </summary>

    Public Class ListValidator

        Inherits BaseValidator

        Public Sub New()

 

        End Sub

 

        Protected Overrides Function ControlPropertiesValid() As Boolean

            Dim ctrl As Control = TryCast(FindControl(ControlToValidate), ListControl)

            Return (Not ctrl Is Nothing)

        End Function

 

        Protected Overrides Function EvaluateIsValid() As Boolean

            Return Me.CheckIfItemIsChecked()

        End Function

 

        Protected Function CheckIfItemIsChecked() As Boolean

            Dim listItemValidate As ListControl = (CType(Me.FindControl(Me.ControlToValidate), ListControl))

            For Each listItem As ListItem In listItemValidate.Items

                If listItem.Selected = True Then

                    Return True

                End If

            Next listItem

            Return False

        End Function

    End Class

End Namespace

In the code snippet shown above, we override the EvaluateIsValid() method. The ‘ControlToValidate’ is cast to a ListControl, which is an abstract base class for all list-type controls. We then loop through all the ListItems and if one of the items in both the CheckBoxList as well as RadioButtonList is checked by the user, we return true, else we return false.

In order to use this custom validation control on the CheckBoxList and RadioButtonList kept in our page, we will use the <%@ Register %> directive as shown below:

<%@ Register TagPrefix="CLV" Namespace="CustomListValidator" %>

<div>

<asp:CheckBoxList ID="CheckBoxList1" runat="server">

</asp:CheckBoxList></div>

        <CLV:ListValidator runat="server" ID="custLstVal" ControlToValidate="CheckBoxList1" ErrorMessage="At least one item in the checkboxlist should be checked" />

 

<asp:RadioButtonList ID="RadioButtonList1" runat="server">

</asp:RadioButtonList>

        <CLV:ListValidator runat="server" ID="custRadVal" ControlToValidate="RadioButtonList1" ErrorMessage="At least one item in the radiobuttonlist should be checked" />

<br />

<br />

<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" />

As you will observe, the two controls are being validated by the same custom validation control that we are building. Run the application and click on the button without checking any items in the two controls. Two error messages are displayed when the form is submitted.

 

Step 6: We saw how to perform server side validation. Let us see how we can now perform validation directly on the client side, without a postback. To perform client side validation, you would need to override OnPreRender and create the javascript function using a StringBuilder object and output it using Page.ClientScript.RegisterClientScriptBlock().

Client-Side Validation

The code with the OnPreRender and JavaScript function will look similar to the following. This code is to be added in the same class where the server-side validation code was added.

 

C#

protected override void OnPreRender(EventArgs e)

{

    // Determines whether the validation control can be rendered

    // for a newer ("uplevel") browser.

    // check if client-side validation is enabled.

    if (this.DetermineRenderUplevel() && this.EnableClientScript)

    {

        Page.ClientScript.RegisterExpandoAttribute(this.ClientID, "evaluationfunction", "CheckIfListChecked");

        this.CreateJavaScript();

    }

    base.OnPreRender(e);

}

 

protected void CreateJavaScript()

{

    StringBuilder sb = new StringBuilder();

    sb.Append(@"<script type=""text/javascript"">function CheckIfListChecked(ctrl){");

    sb.Append(@"var chkBoxList = document.getElementById(document.getElementById(ctrl.id).controltovalidate);");

    sb.Append(@"var chkBoxCount= chkBoxList.getElementsByTagName(""input"");");

    sb.Append(@"for(var i=0;i<chkBoxCount.length;i++){");

    sb.Append(@"if(chkBoxCount.item(i).checked){");

    sb.Append(@"return true; }");

    sb.Append(@"}return false; ");

    sb.Append(@"}</script>");

    Page.ClientScript.RegisterClientScriptBlock(GetType(),"JSScript", sb.ToString());

}

VB.NET

        Protected Overrides Sub OnPreRender(ByVal e As EventArgs)

            ' Determines whether the validation control can be rendered

            ' for a newer ("uplevel") browser.

            ' check if client-side validation is enabled.

            If Me.DetermineRenderUplevel() AndAlso Me.EnableClientScript Then

                Page.ClientScript.RegisterExpandoAttribute(Me.ClientID, "evaluationfunction", "CheckIfListChecked")

                Me.CreateJavaScript()

            End If

            MyBase.OnPreRender(e)

        End Sub

 

        Protected Sub CreateJavaScript()

            Dim sb As StringBuilder = New StringBuilder()

            sb.Append("<script type=""text/javascript"">function CheckIfListChecked(ctrl){")

            sb.Append("var chkBoxList = document.getElementById(document.getElementById(ctrl.id).controltovalidate);")

            sb.Append("var chkBoxCount= chkBoxList.getElementsByTagName(""input"");")

            sb.Append("for(var i=0;i<chkBoxCount.length;i++){")

            sb.Append("if(chkBoxCount.item(i).checked){")

            sb.Append("return true; }")

            sb.Append("}return false; ")

            sb.Append("}</script>")

            Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), "JSScript", sb.ToString())

        End Sub

As you observe, we override the OnPreRender method and use the RegisterExpandoAttribute to assign the function name ‘CheckIfListChecked’ an attribute called ‘evaluationfunction’. The javascript function(CheckIfListChecked) takes one parameter, the validator object and returns ‘true’ if Valid (item in both Radiobuttonlist or CheckBoxList is checked) and ‘false’ if Invalid (Item in either Radiobuttonlist or CheckBoxList is not selected).

Note: If you observe, there is a call to the base.OnPreRender() in the OnPreRender method. This is done to make sure that if the users have turned off javascript on their browsers, then this call passes the control back to the page and allows the rendering to continue by calling base.OnPreRender() .

The javascript function is created using a StringBuilder object and is output using Page.ClientScript.RegisterClientScriptBlock()

When the page is run, the javascript output to the browser will look similar to the following:

<script type="text/javascript">

function CheckIfListChecked(ctrl)

{

var chkBoxList = document.getElementById(document.getElementById(ctrl.id).controltovalidate);

var chkBoxCount= chkBoxList.getElementsByTagName("input");

for(var i=0;i<chkBoxCount.length;i++)

{

if(chkBoxCount.item(i).checked)

{

return true;

}

}return false;

}

</script>

 

Step 7: The last step would be to add the EnableClientScript="true" to our custom validation controls as shown below:

        <CLV:ListValidator runat="server" ID="custLstVal" EnableClientScript="true" ControlToValidate="CheckBoxList1" ErrorMessage="At least one item in the checkboxlist should be checked" />

 

        <CLV:ListValidator runat="server" ID="custRadVal" EnableClientScript="true" ControlToValidate="RadioButtonList1" ErrorMessage="At least one item in the radiobuttonlist should be checked" />

 

That’s pretty much it. This time when you run the application, the custom validation control handles both server-side as well as client-side validation. If JavaScript is enabled on your browsers, the validation errors are displayed before the form is posted back to the server. The entire source code of the application can be downloaded from here. I hope you liked the article and I thank you for viewing it.