Exercise 2: Controlling Server Control ClientIDs

In this exercise, you will learn how to control the client side IDs that are generated from ASP.NET server controls by the framework. Previously the framework would modify the client side IDs to uniquely identify each control. This sometimes left you with the ID you defined in markup or sometimes left you with something that looks like this, "ctl00_MasterPageBody_ctl01_Textbox1".

The modification of the client side id property works great to ensure that each element is uniquely identified, however, to anyone that has tried to do any sort of client side scripting this becomes very frustrating. Chances are that if you have worked in ASP.NET for any time at all you have run into this issue. The problem is that until runtime you do not know what the client side ID could be, making it difficult to do any kind of client side scripting. In addition, any modification of the page, adding removing controls, can result in a different client side ID being generated.

Again, if you have worked with ASP.NET for any amount of time you know there is a work around for this issue. Each control has a property called ClientID that is a read only and supplies the unique client side ID. You could use this in a code behind when dynamically adding scripts or more commonly use inline code (old ASP style) to supply the value to and client side scripts.

JavaScript

<script type="text/javascript"> function DoSomething(){ alert('<%= Control.ClientID %>'); } </script>

ASP.NET 4 Web Forms addresses this need by providing four ClientID ‘modes’, giving the user everything from existing behavior to full control. The controls ID property is modified according to the ClientIDMode mode and then used as the client side id.

The four modes are:

  • AutoID: The default value if ClientIDMode is not set anywhere in the control hierarchy. This causes client side IDs to behave the way they did in version 2.0 (3.0 and 3.5 did not change this code path) of the framework. This mode will generate an ID similar to "ctl00_MasterPageBody_ctl01_Textbox1".
  • Inherit: This is the default behavior for every control. This looks to the controls parent to get its value for ClientIDMode. You do not need to set this on every control as it is the default, this is used only when the ClientIDMode has been changed and the new desired behavior is to inherit from the controls parent.
  • Static: This mode does exactly what you think it would; it makes the client side ID static. Meaning that what you put for the ID is what will be used for the client side ID. Warning, this means that if a static ClientIDMode is used in a repeating control the developer is responsible for ensuring client side ID uniqueness.
  • Predictable: This mode is used when the framework needs to ensure uniqueness but it needs to be done so in a predictable way. The most common use for this mode is on databound controls. The framework will traverse the control hierarchy prefixing the supplied ID with its parent control ID until it reaches a control in the hierarchy whose ClientIDMode is defined as static. In the event that the control is placed inside a databound control a suffix with a value that identifies that instance will also be added to the supplied ID. The ClientIDRowSuffix property is used to control the value that will be used as a suffix. This mode will generate an ID similar to "Gridview1_Label1_0".

For more information, see Setting Client IDs.

Note:
To verify that each step is correctly performed, it is recommended to build the solution at the end of each task.

Task 1 – Assigning Static ClientID to ASP.NET Controls

In this task, you will enable the StaticClientID mode in several ASP.NET controls within the web application. By doing this, you will be able to reference them seamlessly from client-side code, and from a CSS in future steps.

  1. Open Microsoft Visual Studio 2010 as an Administrator. Right-click on Start | All Programs | Microsoft Visual Studio 2010 | Microsoft Visual Studio 2010.and select Run as administrator.
  2. Open the solution file WebFormsSampleApp.sln located under \Ex02-ClientId\begin\ (Choosing the folder that matches the language of your preference).

    Note:
    The lab scenario consists of a single page that lists the products filtered by category from AdventureWorksLT database, allowing the user to add them to a cart and then submit them by clicking Check Out. The AdventureWorksLT.mdf file must be copied from \AspNetWebForms4\Source\Assets folder to the App_Data folder of this project.

    Figure 11

    Viewing the Web Application in Solution Explorer (C#)

    Figure 12

    Viewing the Web Application in Solution Explorer (VB)

  3. Open the ShoppingCart user control in Source mode. To do this, in Solution Explorer, right-click the ShoppingCart.ascx file under the UserControls folder, and select View Markup.

    Note:
    This is the shopping cart where the user will be placing orders. This user control when rendered will have the ability to expand and collapse by taking advantage of the ClientID property (Static mode) via client side script.

  4. Enable ClientIDStatic mode in ShopCartCollapsed ASP.NET Panel. To do this, replace the current ShopCartCollapsed asp:Panel definition with the following highlighted code.

    Note:
    This Panel will be rendered to the client as a div with the same Id as the server control, in this case, ShopCartCollapsed.

    ASP.NET

    <asp:Panel ID="ShopCartCollapsed" ClientIDMode="Static" runat="server">

  5. Do the same as in the previous step but with the ShopCartExpanded ASP.NET Panel.

    ASP.NET

    <asp:Panel ID="ShopCartExpanded" ClientIDMode="Static" runat="server">

Task 2 – Assigning Predictable ClientID to ASP.NET Controls

In this task, you will assign the PredictableClientID mode to the product list items that are retrieved from the database, setting the product id as the ClientIDRowSuffix.

Note:
Note: ASP.NET previously generated its unique IDs to prevent ID collisions, and the most common place for these types of collisions was inside databound controls. Predictable mode was principally designed to tackle this problem while working with databound controls.

Predictable mode output follows the pattern [Prefix]_[ID]_[Suffix], where each parameter represents the following:

- Prefix: Underscore-separated list of all parent controls with an explicit ID/ClientID

- ID: The repeated item server control Id

- Suffix: An optional auto-incrementing number used for repeated items (only applicable when using an IDataKeysControl). This parameter is assigned by setting the ClientIDRowSuffix property of the databound server control (not on the repeated items). If this property is not set or is not available, the row index will be used in its place.

Setting the ClientIDRowSuffix property is only supported by controls that implements a new interface called IDataKeysControl (currently implemented by GridView and ListView). This interface provides the ability to set the ClientIDRowSuffix of a child element whose value is based on the data keys of each row.

  1. Assign the ClientIDRowSuffix property to the ListView that shows the items that were placed in the shopping cart. To do this, open ShoppingCart.ascx in Source mode, locate the ShoppingCartItemsLists ListView, and replace the current control definition with the following highlighted code.

    Note:
    The ProductId is a property of the class which items will be repeated (ShoppingCartItem), and is automatically inserted into the data keys collection when the data source is bound.

    ASP.NET

    <asp:ListView ID="ShoppingCartItemsLists" runat="server" ClientIDMode="Static" ClientIDRowSuffix="ProductId">

    Note:
    There are three ways to use the Predictable mode, each one of these is defined through the ClientIDRowSuffix property that specifies the suffix for each instance.

    1- With no ClientIDRowSuffix defined. This is also the behavior for databound controls without a data keys collection (for example, Repeater Control). To construct the ClientId, ASP.NET will suffix the ID with the row index.

    2- With a ClientIDRowSuffix defined. It looks in the databound server control’s data keys collection for the value, and then suffixes the ID with that value.

    3- With a ClientIDRowSuffix defined, but using a compound value instead of just one value. Exhibits the same behavior as with one value, but it will suffix the ID with both concatenated values. (For example, ClientIDRowSuffix="ID, Name").

  2. Bound the cart items to the ShoppingCartItemLists control. To do this, open the ShoppingCart.ascx code-behind file, and add the following highlighted code at the bottom of the Page_PreRender method of the ShoppingCartControl class.

    Note:
    If you browse the ShoppingCartItem class you will view the ProductId property that is used to set the ClientIDRowSuffix property of the ListView.

    (Code Snippet – ASP.NET 4 Web Forms Lab – Page_PreRender method – C#)

    C#

    protected void Page_PreRender(object sender, EventArgs e)
    FakePre-a1fc63d70dc94dd39059d6e0466dae50-40ece616cc504319b8d41643cc8cd622FakePre-ffcbf0eaec5b4894a553566617f944a3-466d24dc90bb4981a860934d947b170aFakePre-84e2890fc202435eb04b44f3c5028384-43ed044b7bf148d79bfe08e786c21e3bFakePre-02e91a45b3554bfeaad896d2f3089429-7e14df105c6645779a4c84cbe3bb59b4FakePre-7ed782090aef409d8be6b3fb454c479f-723a6e2d12c842fc870f68bc3987f035FakePre-971f4f10bbb54a2fac91995d7a325203-f3bf2212c4b84aa18846d87b457354baFakePre-d5ca3ec3b0824290a7b5f18ecc4ad126-47d6bdb9e1884250901699a73312df20FakePre-aac356f24ae8429a870c318835e82fb6-e132528ed0df486aab22ecaa8bdb4ef1FakePre-79f45a0fbccb4dcdbc73e8cf6dba702e-503d99b48c4547349c885f689c78d6a7FakePre-45e5ec7f99a744ee8249f6ca97ed9d31-672a4c2ab3d5421286eb52de2e3995acFakePre-d6d3f49a69124a16b67a3a71bb8a0d92-8acb961cf39149b0875e08462089532d ShoppingCartItemsLists.DataSource = cart.Items; ShoppingCartItemsLists.DataBind();FakePre-c1ed3519669446f6a909373dea29a80f-375ae77e50ef4301ac6155f87ff4598e

    (Code Snippet – ASP.NET 4 Web Forms Lab – Page_PreRender method – VB)

    VB

    Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As EventArgs)
    FakePre-05257caee14c4aa4acea448eeb97c97a-a200603ba06445d1a0571f50ad5d28dfFakePre-cb7590e2a07e4f6d8c1ad81a38924410-a3c06fc78b0041b59667c49646c71198FakePre-96c6079118e043dfa61b5c710a0ecf24-c17dac704c75486eb8a0da54cd55db9fFakePre-08f6efe0542844f1a974279003b83415-5eb271df90f741c1b621776ae8440094FakePre-940bde490318454a96c5c45c65013b56-759cae9d47aa4d62bb2f26bcbae1ab22FakePre-f9e2fe60711a446a935be97d5732df84-6599a6fb86364ddfbea1868ca2a1359bFakePre-fe4ed84d0cd742f6b4e822724eceecf3-a95d8ef8676048c292248ce50dedd2ccFakePre-4f040b9061e54da89f2b1b9238227928-b58ea67db83e451a8ed362812ccc7a5cFakePre-2fd2f4d789ee4378b42b1746b366b70c-b0728e13596c494b81c050a846f1d881FakePre-931072e98b2a4eeabfd6baeede1316a1-6b2cc682fd074dc99b93cd9ba123e88c ShoppingCartItemsLists.DataSource = cart.Items ShoppingCartItemsLists.DataBind()FakePre-716f82c3e99e4fe783dbc5778857c635-a586ac3049d7498d9904b5a15e31a0a4

  3. Enable PredictableClientId mode in the child elements of the shopping cart ListView. To do this, switch back to the markup view of the ShoppingCartControl user control and replace the current Quantity and TotalPriceasp:Labels definition contained in the ShoppingCartItemLists ListView, with the following highlighted code.

    Note:
    These labels will be rendered to the client as divs, once for each item in the shopping cart. For example, the ClientId of the Quantity label will be something like "ctrl0_Quantity_12", where 12 is the ProductId and ctrl0 is the parent control id.

    ASP.NET

    <asp:ListView ID="ShoppingCartItemsLists" runat="server" ClientIDMode="Static" ClientIDRowSuffix="ProductId">
    FakePre-6d429ae0351841138edbb52ecaec50f6-1a585587d7374153a53b32582a59e8b3FakePre-fe5b144567244cebb451984f989c52b6-0fa8229ebdde44d988d73fea33d4e440FakePre-aff7948700fa4f8da2bf82fa42aea8c4-ca72d4ae239b4ee8a54bea8a74230270 <asp:Label ID="Quantity" ClientIDMode="Predictable" runat="server"> <asp:Label ID="TotalPrice" ClientIDMode="Predictable" runat="server">FakePre-8b7c9f5aeac544ce87defc766b19f143-eee0f00c3fb74365b5e44d441bbd4c38FakePre-e177bc456a424993b6663997414857b8-55bfc94a12cf40ca92e4a66d41b34f0fFakePre-28d88d8d73f8420ca0c81707daa80d33-28f34899f0504e91b622ec77f48012b3 <asp:Label ID="Quantity" ClientIDMode="Predictable" runat="server"> <asp:Label ID="TotalPrice" ClientIDMode="Predictable" runat="server">FakePre-e7f5fec0ff3b445fb692ea80bb8bfaf7-7c349241a6c647bb8f428dc98cfd3828FakePre-d27587810dfe4367814ce67fb6d53411-95fdbb708cd94128b386ad8d369a2f1fFakePre-f8166d9dc8554ad9a4445e497b4a9a65-fca7d429eb394583bbfdd5b604577c28FakePre-af63c460e29340b5a9038e0c6f48e307-289cedb15bee43fba2ba965917dc39b8FakePre-874ae331010d41b5b88d1202e8edd266-a54028d1e9e24d49bfc996712b462c52

Task 3 – Assigning Inherit ClientID to ASP.NET Controls

In this task, you will assign the InheritClientID mode to a Panel server control contained in the shopping cart. This will let the control inherit the ClientIdMode from the first parent server control that implements the INamingContainer interface (in this case the ShoppingCart User Control).

Note:
The INamingContainer interface identifies a container control that creates a new ID namespace within a Page object's control hierarchy. Any control that implements this interface creates a new namespace in which all child control ID attributes are guaranteed to be unique within an entire application. This is a marker interface only.

For more information, see INamingContainer interface.

  1. Define the ClientIDMode of the parent ShoppingCart user control that will be inherited by the child Panel server control. To do this, open the UI.Master page in Source mode, and replace the current ShoppingCart1 user control definition with the following highlighted code.

    ASP.NET

    ...
    <uc1:ShoppingCart ID="ShoppingCart1" runat="server" ClientIDMode="Static" />FakePre-12c7c844d0ec434483a27cb4296bcf7d-d59145f1e9ca404c9dc6b749c48219e3

    Note:
    If no ClientIDMode had been set, all its child control would have inherited the default ClientIDMode which is AutoID.

  2. Enable InheritClientId mode in the child Panel server control. To do this, open ShoppingCart.ascx in Source mode, and replace the current ShopCartExpandedNonEmptyasp:Panel definition with the following highlighted code.

    ASP.NET

    ...
    <asp:Panel ID="ShopCartExpandedNonEmpty" ClientIDMode="Inherit" runat="server">FakePre-556df46c287d46478c1122145262514c-23667c91ecb34a5b8129df0fe85acfe1FakePre-1540827c22b540739cc7b6238cfa2a68-b2035265b165490c9da2589f4ffeb91dFakePre-388e5cc9f4f2438fac2ef77da76fc032-c0524d2f0f6d48718ebd4339875875f3FakePre-e177c592a2ae450887aaa6a42b632bb9-078e89c70ee94615a327b2c9721f8e0b

    Note:
    Note: There are two other possible ways to set the ClientIdMode:

    - At Page level: Defines the default ClientIdMode for all controls within the current page. For example:

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" ClientIdMode="Static"%>

    - At Web.config level: It is also possible to set the ClientIdMode in the config section at either machine or application level. For example, this defines the default ClientIdMode for all controls within all pages in the application:

    <system.web>

    <pages clientIdMode="Predictable"></pages>

    </system.web>

Task 4 – Targeting Static ClientIDs via CSS and JavaScript

  1. In this task, you will take advantage of the static ClientID mode, by manipulating the rendered control with the static ClientID through JavaScript, and by applying CSS styles directly to the Id rather than creating an unnecessary CssClass.
  2. Apply styles to the controls with static ClientIDs. To do this, open the main.css file located under Styles folder, and add the following highlighted code:

    Note:
    ShopCartCollapsed and ShopCartExpanded ID attributes are being referenced applying ID selectors rather than Class selectors. The essential difference is that while class selectors apply to one or more elements on a page, ID selectors apply to exactly one element. For more information, see https://www.w3.org/TR/CSS21/selector.html#id-selectors.

    CSS

    #ShopCartCollapsed { display: none; background-color: #00FF80; padding: 10px 15px; height: 10px; background: url(../Images/shopcart_collapsed_bg.png) no-repeat top left; } #ShopCartCollapsed p.summary span { position: relative; top:-4px; } #ShopCartCollapsed a.checkoutLink { position: absolute; top: 6px; right: 8px; } #ShopCartExpanded { display: none; padding: 10px 15px; height: 90px; background: url(../Images/shopcart_bg.gif) no-repeat top left; } #ShopCartExpanded p.items { font-size: 11px; color: #666; } #ShopCartExpanded ul.items { height:60px;overflow:auto; margin: 0; padding: 0; list-style: none; } #ShopCartExpanded ul.items li { display: inline; margin: 0; padding: 0; } #ShopCartExpanded div.ShoppingCartItem { display: block; } #ShopCartExpanded div.productColumn { float: left; width: 150px; } #ShopCartExpanded div.priceColumn { float: right; width: auto; } #ShopCartExpanded p.summary span { position: relative; top:+6px; } #ShopCartExpanded p.empty { text-align: center; font-weight: bold; color: #50d48f; padding-top: 15px; } #ShopCartExpanded a.checkoutLink { position: absolute; top: 89px; right: 8px; }

  3. Implement the necessary JavaScript code to create the effects for the Shopping Cart using JQuery. To do this, open the shoppingCart.Effects.js file located under Scripts folder, and follow these steps:
    1. Add the following highlighted code to CollapseCart function:

      JS

      function CollapseCart(withAnimation) {
      if (withAnimation) { $("#ShopCartExpanded").hide(); $("#ShopCartCollapsed").show("slow"); } else { $("#ShopCartExpanded").css("display", "none"); $("#ShopCartCollapsed").css("display", "block"); } $("#ShoppingCartState").val("collapsed");FakePre-05a2c141b44d4236968bd370e2c4142a-c63547b406c246918f5d94c013afbc1bFakePre-ca39b070580e467e9c4ac815b6f52c21-bb4ca391accd46cfb482541e4e1f3d21

    2. Add the following highlighted code to ExpandCart function:

      JS

      function ExpandCart(withAnimation) {
      if (withAnimation) { $("#ShopCartCollapsed").hide(); $("#ShopCartExpanded").show("slow"); } else { $("#ShopCartCollapsed").css("display", "none"); $("#ShopCartExpanded").css("display", "block"); } $("#ShoppingCartState").val("expanded");FakePre-255bda8fa902428fa7c85a6da6969335-2153cfc8dc0444cc851792583f0071dcFakePre-9ea6ba4874184846b0c60000291a6c51-2041548548ed4085baf701863753003a

    3. Add the following highlighted code to $(document).ready function:

      JS

      $(document).ready(function() {
      FakePre-f61f59c2410f4c65bb77ef9ea324f2b6-29acd389e234483c82dec06747ea3418FakePre-f8982d34e481444dbc475b02fe37ae91-917b28306ab0412191544b53778bc2e6FakePre-aae566cc131f47e2a1d1759d0b6a2e85-354ed88d05e94a83833061f423da71a8FakePre-69cffb8276c9462c9dd20b03b855bd39-61d3abaf0c9e4a23a3609822dfba8b17 $("#ShopCartCollapsed").click(function() { ExpandCart(true) }); $("#ShopCartExpanded").click(function() { CollapseCart(true) });FakePre-3a8b11faced74e43bfd281aeb41a5acf-8a25541e7f6f4f54b1aa2aefbc1d4ce1FakePre-0790f74b48824a1494a5758e3de06b28-ebe75747c5e04e548879e6b55505e49aFakePre-f21dc40a0f424a33902f3347f7dcc2bf-924258fb5dc24e0480468a5aa1c302e0FakePre-eec94d9eba994eda9f53e6bed80fcc27-4d6d61fb36824a2bb654650c217f894bFakePre-8c9ed07c7b6343c5b6fa95f4fefe47c7-ef4ff59543214d80be0973a9b3f2234cFakePre-11fbedc8170040febe5f6c33018af162-65cb71bae5d6409d86a309ef772ce625FakePre-48194256139e4219a952b9f0740da182-ba5bda411cd84d8ab9d228f4a1694599FakePre-98dd8e0b4fc04fb183744b0240c8f025-ac64a10f99d24c24b7e0663c289d28b4

  4. Add a reference to ShoppingCart.Effects.js in Default.aspx. To do this, open Default.aspx in Source mode, and add the following highlighted code inside the first asp:Content tag.

    ASP.NET

    ...
    FakePre-1ee8c15122894e6a8cd9e7c1ce656312-3fa63d490bac43b8b8b41accb22e71aeFakePre-33773871d5f649988a1c9ab15ab481d8-e82f05db06c04aeb8a69dd9aca592ccaFakePre-53332c1a145643bfb4ac6e48728e82f4-4d29ca5c52ec4420b5f031e2a6dd67dc <script type="text/javascript" src="/Scripts/shoppingCart.Effects.js"></script>FakePre-105fa180c6d24630909c514297431586-590e460520374d71aee148bca957ffbeFakePre-161aaea1c6a9468d91f0dbd37acb5779-c98a58cbcdf54341ab4e7cac90f6caf0

Next Step

Exercise 2: Verification