We Love .NET 4 - Clean Web Control IDs with ClientIDMode Property to Static and Predictable

In this blog post, we will see how ClientIDMode property of Web Controls makes our lives easier. Also, we will demonstrate couple of scenarios on how it works...
2011-03-24 19:58
Tugberk Ugurlu


If you want, you could directly download the source code which is used in this blog post as example and skip the reading Smile. Source code is on ;

http://cid-0ee89cb310fe3603.office.live.com/self.aspx/Programming/WeLoveNet4^_1.rar

One of the things that every JavaScript programmers hate about ASP.NET is Web Control IDs. When we use Master Page or any of Data Controls for displaying data (there are other things which fit in this category but for the sake of less talking, I skipped them), the id of the web controls’ values are generated by concatenating the ID values of each parent naming container with the ID value of the control. This thing screws up the id of the control. As you can see on below source view;

image_thumb[5]

This also makes it hard to write JavaScript (nowadays, JavaScript is also known as JQuery which is kind of bizarre because JQuery is a JavaScript library Smile) codes when we want to bind the code to a control. Of course, it is not impossible to accomplish that on ASP.NET. There are a few ways of doing that on ASP.NET;

  1. One of the ways is to create a JS function and bind the control to that function inside the DOM. For instance, let’s say we have System.Web.UI.Controls.Button control on our web form and we would like to fire a JS function on click of this button. User creates the JS function and bind the control to the function by putting the function name on OnClientClick property of the Button control. I do not recommend this way. Binding the JS code to DOM elements directly is sloppy. 
  2. Other way is to bind the JS code to a Web Control by getting the name of the control with code blocks (often called Code Nuggets by members of the Visual Web Developer team). We’ll get that option on our following example latter.

With .NET Framework v4, we have been introduced with a new property of Web Controls called ClientIDMode. This little thing is now making our lives easier. ClientIDMode gets or sets the algorithm that is used to generate the value of the ClientID property.

ClientIDMode’s value type is enum and it has 4 values. The default value of ClientIDMode for a page is Predictable. The default value of ClientIDMode for a control is Inherit.

You can get more information on Control.ClientIDMode property from offical MSDN document :

http://msdn.microsoft.com/en-us/library/system.web.ui.control.clientidmode.aspx

I think better way of understanding this is by demonstrating a scenario. On the following example, our goal is to fire a JS function with on click event of a button control. Let’s see how this little feature fits in our lives.

OldFashionWay.aspx

We are assuming that we are working on .NET 3.5 here. So, we do not have the ability to use ClientIDMode feature. What do we do then? Following OldFashionWay.aspx web form code shows us how we can accomplish that;

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">

    <title>We Love .NET 4</title>

    <script src="Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>

    <script type="text/javascript">

        $(document).ready(function () {
            $("#<%= button1.ClientID  %>").click(function () {
                alert('textbox1.Text : ' + $("#<%= textbox1.ClientID  %>").val());
            });
        });

    </script>

</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">

        <div>
        
            <asp:TextBox runat="server" ID="textbox1" />  <asp:Button Text="Submit" ID="button1" runat="server" />

        </div>

</asp:Content>

As you can see inside the script tag, we use Code Nuggets to determine the id of the web controls. Here is the result;

image_thumb[7]

image_thumb[12]image_thumb[10]

This way works just fine but only inside the .aspx files. When we want to get the JavaScript code out of here, put it on a separate .js file and reference it inside the .aspx page, you will certainly fail! This is because of the fact that ASP.NET framework do not apply any special rendering on.js files as it does on .aspx, .ashx or any other ASP.NET specific file types.

ClientIDMode.aspx

As we mentioned earlier, we have been introduced with ClientIDMode property for Web Controls on .NET Framework v4. Let’s see how we accomplish the above scenario on .NET Framework v4. Here is ClientIDMode.aspx file code;

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">

    <title>We Love .NET 4</title>

    <script src="Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>

    <script type="text/javascript">

        $(document).ready(function () {

            $("#button1").click(function () {
                alert('textbox1.Text : ' + $("#textbox1").val());
            });

        });

    </script>

</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">

    <div>
        
        <asp:TextBox runat="server" ID="textbox1" ClientIDMode="Static" />  
        <asp:Button Text="Submit" ID="button1" runat="server" ClientIDMode="Static" />

    </div>

</asp:Content>

As you can notice here, we have set ClientIDMode property of TextBox and Button control to Static. What does that mean? When we set the ClientIDMode to Static, the ClientID value is set to the value of the ID property. If the control is a naming container, the control is used as the top of the hierarchy of naming containers for any controls that it contains. Let’s view the rendered html source code;

image_thumb[14]

The id of the controls are the same as we determined. Perfect!

ClientIDMode_SeperateJSFile.aspx

Let’s make our html file unobtrusive. ClientIDMode_SeperateJSFile.aspx file’s source code;

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">

    <title>We Love .NET 4</title>
    <script src="Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
    <script src="Scripts/ClientIDMode.js" type="text/javascript"></script>

</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">

    <div>
        
        <asp:TextBox runat="server" ID="textbox1" ClientIDMode="Static" />  
        <asp:Button Text="Submit" ID="button1" runat="server" ClientIDMode="Static" />

    </div>

</asp:Content>

What we do here is to get the JS code out of the .aspx file and put it inside a separate .js file. This way we could use the same file for other pages and we will accomplish to make ourselves DRY (Don’t Repeat Yourself). We are unable to do that on .NET 3.5 as we mentioned above. ClientIDMode.js is no different than the JS code we used before. Here is the content of ClientIDMode.js;

$(document).ready(function () {

    $("#button1").click(function () {
        alert('textbox1.Text : ' + $("#textbox1").val());
    });

});

Pretty awesome Smile

ClientIDMode_ForEntirePage.aspx

Assuming that we have a large page content and it will make it hard to set ClientIDMode for every Web Control. ASP.NET team also have thought that. We can set the ClientIDMode value for a page in the @ Page directive. Only we need to do is to set ClientIDMode property on <%@ Page … %> section;

<%@ Page 
        Title="" 
        Language="C#" 
        ClientIDMode="Static" 
        MasterPageFile="~/Site1.Master" 
        AutoEventWireup="true" 
        CodeBehind="ClientIDMode_ForEntirePage.aspx.cs" 
        Inherits="WeLoveNet4_1.WebForm22" %>

After this configuration, you do not need to set ClientIDMode value for every control.

Also, we can set the ClientIDMode value for all pages in a Web site by setting the pages element in the site's Web.config file. Example code for Web.config;

<configuration>
  
    <system.web>
        <pages clientIDMode="Static"></pages>
        <compilation debug="true" targetFramework="4.0" />
    </system.web>

</configuration>

ClientIDMode_InsideData-Bound_Controls.aspx

On data-bound scenarios, there is another property that we can make use of for ListView control : ClientIDRowSuffix. ClientIDRowSuffix gets or sets the name of the data field whose value is used to uniquely identify each data row of a ListView control when the ClientIDMode property is set to Predictable.

Let’s see our example. here is the ClientIDMode_InsideData-Bound_Controls.aspx file’s code;

<html xmlns="http://www.w3.org/1999/xhtml">

<head id="Head1" runat="server"></head>

<body>

    <asp:ListView ID="ListView1" ClientIDMode="Predictable" ClientIDRowSuffix="LanguageID"  runat="server" >

        <ItemTemplate>
            <li>
                <asp:Label Text='<%# Eval("Name") %>' ID="namelabel" runat="server" /> : 
                <asp:Label Text='<%# Eval("Rating") %>' ID="ratinglabel" runat="server" />
            </li>
        </ItemTemplate>

        <LayoutTemplate>
            
            <div ID="itemPlaceholderContainer" runat="server">
                <ul ID="itemPlaceholder" runat="server" />
            </div>
            
        </LayoutTemplate>

    </asp:ListView>

</body></html>

As it is above, we have set ClientIDMode of ListView control to Predictable and ClientIDRowSuffix of ListView control to LanguageID which is the ID field of our datasource. Some of you might wonder where the datasource is. I made a List of ProgrammingLanguages and bound it to ListView1 as you can see on the below code;

    public partial class ClientIDMode_InsideData_Bound_Controls : System.Web.UI.Page {

        public class ProgrammingLanguages {

            public int LanguageID { get; set; }
            public string Name { get; set; }
            public int Rating { get; set; }

        }

        protected void Page_Load(object sender, EventArgs e) {

            List<ProgrammingLanguages> _list = new List<ProgrammingLanguages>();

            _list.Add(new ProgrammingLanguages {LanguageID = 10, Name="C#", Rating=10 });
            _list.Add(new ProgrammingLanguages {LanguageID = 20, Name = "Visual Basic", Rating = 6 });
            _list.Add(new ProgrammingLanguages {LanguageID = 30, Name = "F#", Rating = 7 });
            _list.Add(new ProgrammingLanguages {LanguageID = 45, Name = "Java", Rating = 8 });

            ListView1.DataSource = _list;
            ListView1.DataBind();

        }
    }

When we view the source code of the rendered page, we will see that id of the controls are predictable.

image_thumb[16]

I hope that this article gives you an idea on how you can make your application better with this new feature of ASP.NET.

Happy coding Smile



Comments

Mehmet
by Mehmet on Saturday, Dec 17 2011 16:41:28 +02:00

Hi. it is Static on default I see in my website. I did not apply any setting. But  I have seen that there was no prefix like 'ctl_' :)

 

Am I right ???

Rama
by Rama on Wednesday, Dec 21 2011 21:31:47 +02:00

Hello,

    I don't know who the author of this article is, but whoever it is I thank them from the bottom of my heart.

God bless you.

Rob Leather
by Rob Leather on Wednesday, Mar 14 2012 15:50:25 +02:00

Of course, you could always set the Name value to whatver you wanted, and then used jQuery's excellent selection criteria to pick it.

eg.

$('input[name="MyControl"]')

newbie
by newbie on Wednesday, Apr 17 2013 03:14:52 +03:00

I've been trying to solve this remdom prefix due to master page problem for three days. I was going insane. 

Thank you for sharing this. It's saved my  sanity.

 

New Comment