Controlling HTML IDs in ASP.NET 4.0 Webforms

ASP.NET 4 introduces more control over how client-side IDs are rendered by ASP.NET controls. By default ASP.NET 4 will render client IDs in the same way as ASP.NET 3.5 and earlier.

To change the way IDs are generated, the ClientIDMode property can be set - this can either be done on individual controls or at the page level in the @Page directive.

The default ClientIDMode value for controls is Inherit and the default for the Page is AutoID - if we change ClientIDMode at the Page level, the controls on that page will inherit the setting.

The options for ClientIDMode are:

  • AutoID: (Page default) generates IDs the same way as prior versions. Use for backward compatibility or when you don't care about what IDs are generated.
  • Inherit: (Control default): uses the value from the parent container.
  • Static: always sets the client-side ID to the same as the server-side ID property. Use to guarantee the client-side ID of a control - may result in duplicate IDs on the client which may cause problems if you have JavaScript looking for the control by ID.
  • Predictable: Can be used for content inside a databound GridView or ListView to append the unique id/key of the bound object (e.g. the primary key of a databound database record).

The following snippets of HTML output shows how the different settings change client-side IDs when a Label control called "NumberInStockLabel" is used inside a databound ListView.

AutoID, No ClientIDRowSuffix Specified

<td><span id="AutoIDListView_ctrl0_NumberInStockLabel">22</span></td>

Predictable, ClientIDRowSuffix=ProductID

<td><span id="PredictableListView_NumberInStockLabel_44">22</span></td>

The 44 at the end of the ID is the ProductID property from the underlying databound item.

Predictable, No ClientIDRowSuffix Specified

<td><span id="PredictableListViewNoClientIDRowSuffix_NumberInStockLabel_0">22</span></td>

The 0 at the end of the ID is a simple sequential number generated by ASP.NET, this is because we have not specified a value for ClientIDRowSuffix; subsequent rows would end _1 _2 _3 etc.

Static, No ClientIDRowSuffixSpecified

<td><span id="NumberInStockLabel">22</span></td>

The id is exactly the same as the one we set in markup; subsequent rows would all have exactly the same id.

Complete Source Markup

<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="Products.aspx.cs"
Inherits="clientIDs.Products"
ViewStateMode="Disabled" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>


        <asp:ObjectDataSource ID="odsProducts" runat="server"
            SelectMethod="GetProducts"
            TypeName="clientIDs.MockProductDB" />


        <h3>ListView, AutoID, No ClientIDRowSuffix</h3>
        <table>
            <tr>
                <th>Product ID</th>
                <th>Description</th>
                <th>Stock</th>
            </tr>
            <asp:ListView ID="AutoIDListView" runat="server"
                DataSourceID="odsProducts"
                ClientIDMode="AutoID"
                ClientIDRowSuffix="ProductID">
                <ItemTemplate>
                    <tr>
                        <td><%# Eval("ProductID") %></td>
                        <td><%# Eval("Description")%></td>
                        <td><asp:Label ID="NumberInStockLabel" runat="server" Text='<%# Eval("NumberInStock") %>' /></td>
                    </tr>
                </ItemTemplate>
            </asp:ListView>
        </table>



        <h3>ListView, Predictable, ClientIDRowSuffix=ProductID</h3>
        <table>
            <tr>
                <th>Product ID</th>
                <th>Description</th>
                <th>Stock</th>
            </tr>
            <asp:ListView ID="PredictableListView" runat="server"
                DataSourceID="odsProducts"
                ClientIDMode="Predictable"
                ClientIDRowSuffix="ProductID">
                <ItemTemplate>
                    <tr>
                        <td><%# Eval("ProductID") %></td>
                        <td><%# Eval("Description")%></td>
                        <td><asp:Label ID="NumberInStockLabel" runat="server" Text='<%# Eval("NumberInStock") %>' /></td>
                    </tr>
                </ItemTemplate>
            </asp:ListView>
        </table>



         <h3>ListView, Predictable, No ClientIDRowSuffix set</h3>
        <table>
            <tr>
                <th>Product ID</th>
                <th>Description</th>
                <th>Stock</th>
            </tr>
            <asp:ListView ID="PredictableListViewNoClientIDRowSuffix" runat="server"
                DataSourceID="odsProducts"
                ClientIDMode="Predictable">
                <ItemTemplate>
                    <tr>
                        <td><%# Eval("ProductID") %></td>
                        <td><%# Eval("Description")%></td>
                        <td><asp:Label ID="NumberInStockLabel" runat="server" Text='<%# Eval("NumberInStock") %>' /></td>
                    </tr>
                </ItemTemplate>
            </asp:ListView>
        </table>





        <h3>ListView, Static, No ClientIDRowSuffix set</h3>
        <table>
            <tr>
                <th>Product ID</th>
                <th>Description</th>
                <th>Stock</th>
            </tr>
            <asp:ListView ID="StaticListView" runat="server"
                DataSourceID="odsProducts"
                ClientIDMode="Static">
                <ItemTemplate>
                    <tr>
                        <td><%# Eval("ProductID") %></td>
                        <td><%# Eval("Description")%></td>
                        <td><asp:Label ID="NumberInStockLabel" runat="server" Text='<%# Eval("NumberInStock") %>' /></td>
                    </tr>
                </ItemTemplate>
            </asp:ListView>
        </table>



    </div>
    </form>
</body>
</html>

 

Complete HTML Output

 
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>
 
</title></head>
<body>
    <form method="post" action="Products.aspx" id="form1">
<div class="aspNetHidden">
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTkwMjEwMjAxN2QYBAUOU3RhdGljTGlzdFZpZXcPFCsADmRkZGRkZGQ8KwAEAAIEZGRkZgL/////D2QFJlByZWRpY3RhYmxlTGlzdFZpZXdOb0NsaWVudElEUm93U3VmZml4DxQrAA5kZGRkZGRkPCsABAACBGRkZGYC/////w9kBRNQcmVkaWN0YWJsZUxpc3RWaWV3DxQrAA5kZGRkZGRkPCsABAACBGQVAQlQcm9kdWN0SUQUKwAEFCsAAQIsFCsAAQIDFCsAAQLIIhQrAAECYGYC/////w9kBQ5BdXRvSURMaXN0Vmlldw8UKwAOZGRkZGRkZDwrAAQAAgRkFQEJUHJvZHVjdElEFCsABBQrAAECLBQrAAECAxQrAAECyCIUKwABAmBmAv////8PZBoTurOO0ZN1r5PaB5cscPKdDFEGlp47JvHGSM55AYQ9" />
</div>
 
    <div>
       
 
 
 
 
        <h3>ListView, AutoID, No ClientIDRowSuffix</h3>
        <table>
            <tr>
                <th>Product ID</th>
                <th>Description</th>
                <th>Stock</th>
            </tr>
           
                    <tr>
                        <td>44</td>
                        <td>product 1</td>
                        <td><span id="AutoIDListView_ctrl0_NumberInStockLabel">22</span></td>
                    </tr>
               
                    <tr>
                        <td>3</td>
                        <td>product 2</td>
                        <td><span id="AutoIDListView_ctrl1_NumberInStockLabel">11</span></td>
                    </tr>
               
                    <tr>
                        <td>4424</td>
                        <td>product 3</td>
                        <td><span id="AutoIDListView_ctrl2_NumberInStockLabel">1</span></td>
                    </tr>
               
                    <tr>
                        <td>96</td>
                        <td>product 4</td>
                        <td><span id="AutoIDListView_ctrl3_NumberInStockLabel">0</span></td>
                    </tr>
               
        </table>
 
 
 
        <h3>ListView, Predictable, ClientIDRowSuffix=ProductID</h3>
        <table>
            <tr>
                <th>Product ID</th>
                <th>Description</th>
                <th>Stock</th>
            </tr>
           
                    <tr>
                        <td>44</td>
                        <td>product 1</td>
                        <td><span id="PredictableListView_NumberInStockLabel_44">22</span></td>
                    </tr>
               
                    <tr>
                        <td>3</td>
                        <td>product 2</td>
                        <td><span id="PredictableListView_NumberInStockLabel_3">11</span></td>
                    </tr>
               
                    <tr>
                        <td>4424</td>
                        <td>product 3</td>
                        <td><span id="PredictableListView_NumberInStockLabel_4424">1</span></td>
                    </tr>
               
                    <tr>
                        <td>96</td>
                        <td>product 4</td>
                        <td><span id="PredictableListView_NumberInStockLabel_96">0</span></td>
                    </tr>
               
        </table>
 
 
 
         <h3>ListView, Predictable, No ClientIDRowSuffix set</h3>
        <table>
            <tr>
                <th>Product ID</th>
                <th>Description</th>
                <th>Stock</th>
            </tr>
           
                    <tr>
                        <td>44</td>
                        <td>product 1</td>
                        <td><span id="PredictableListViewNoClientIDRowSuffix_NumberInStockLabel_0">22</span></td>
                    </tr>
               
                    <tr>
                        <td>3</td>
                        <td>product 2</td>
                        <td><span id="PredictableListViewNoClientIDRowSuffix_NumberInStockLabel_1">11</span></td>
                    </tr>
               
                    <tr>
                        <td>4424</td>
                        <td>product 3</td>
                        <td><span id="PredictableListViewNoClientIDRowSuffix_NumberInStockLabel_2">1</span></td>
                    </tr>
               
                    <tr>
                        <td>96</td>
                        <td>product 4</td>
                        <td><span id="PredictableListViewNoClientIDRowSuffix_NumberInStockLabel_3">0</span></td>
                    </tr>
               
        </table>
 
 
 
 
 
        <h3>ListView, Static, No ClientIDRowSuffix set</h3>
        <table>
            <tr>
                <th>Product ID</th>
                <th>Description</th>
                <th>Stock</th>
            </tr>
           
                    <tr>
                        <td>44</td>
                        <td>product 1</td>
                        <td><span id="NumberInStockLabel">22</span></td>
                    </tr>
               
                    <tr>
                        <td>3</td>
                        <td>product 2</td>
                        <td><span id="NumberInStockLabel">11</span></td>
                    </tr>
               
                    <tr>
                        <td>4424</td>
                        <td>product 3</td>
                        <td><span id="NumberInStockLabel">1</span></td>
                    </tr>
               
                    <tr>
                        <td>96</td>
                        <td>product 4</td>
                        <td><span id="NumberInStockLabel">0</span></td>
                    </tr>
               
        </table>
 
 
 
    </div>
    </form>
</body>
</html>

SHARE:

FallbackValue, TargetNullValue & StringFormat in Silverlight 4

Silverlight 4 introduces some further databinding functionality including:

FallbackValue

If there is a problem with performing the binding, for example an incorrect path to a property value, the fallback value will be used in it's place. This allows some meaningful display, rather that the default behaviour of showing nothing.

TargetNullValue

Allows a value to be used if the bound property is null.

StringFormat

Allows formatting of the bound value using standard formatting expressions.

Example

The following is the output of the XAML below:

 

 

<Grid x:Name="LayoutRoot" Background="WhiteSmoke" >
    <Grid.ColumnDefinitions>
        <ColumnDefinition></ColumnDefinition>
        <ColumnDefinition></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"></RowDefinition>
        <RowDefinition Height="auto"></RowDefinition>
        <RowDefinition Height="auto"></RowDefinition>
        <RowDefinition Height="auto"></RowDefinition>
        <RowDefinition Height="auto"></RowDefinition>
        <RowDefinition Height="auto"></RowDefinition>
    </Grid.RowDefinitions>

    <TextBlock TextWrapping="Wrap">Invalid binding path with FallbackValue set</TextBlock>
    <TextBlock Text="{Binding Path=badPath, FallbackValue='this is a fallback value'}" Grid.Column="1"></TextBlock>

    <TextBlock TextWrapping="Wrap" Grid.Row="1">Invalid binding path withOUT FallbackValue set</TextBlock>
    <TextBlock Text="{Binding Path=badPath}" Grid.Row="1" Grid.Column="1"></TextBlock>

    <TextBlock TextWrapping="Wrap" Grid.Row="2">Bound property = null with TargetNullValue set</TextBlock>
    <TextBlock Text="{Binding Path=AnIntProperty, TargetNullValue='The int is null'}" Grid.Column="1" Grid.Row="2"></TextBlock>

    <TextBlock TextWrapping="Wrap" Grid.Row="3">Bound property = null withOUT TargetNullValue set</TextBlock>
    <TextBlock Text="{Binding Path=AnIntProperty}" Grid.Column="1" Grid.Row="3"></TextBlock>

    <TextBlock TextWrapping="Wrap" Grid.Row="4">Bound property with StringFormat</TextBlock>
    <TextBlock Text="{Binding Path=AnotherIntProperty, StringFormat='Age is {0} years old.'}" Grid.Column="1" Grid.Row="4"></TextBlock>

    <TextBlock TextWrapping="Wrap" Grid.Row="5">Bound property withOUT StringFormat</TextBlock>
    <TextBlock Text="{Binding Path=AnotherIntProperty}" Grid.Column="1" Grid.Row="5"></TextBlock>

</Grid>

The DataContext is set to an instance of the following class:

public class TestData : DependencyObject
{
    public int? AnIntProperty
    {
        get { return (int?)GetValue(AnIntPropertyProperty); }
        set { SetValue(AnIntPropertyProperty, value); }
    }

    // Using a DependencyProperty as the backing store for AnIntProperty.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty AnIntPropertyProperty =
        DependencyProperty.Register("AnIntProperty", typeof(int?), typeof(TestData),
            new PropertyMetadata(0));


    public int AnotherIntProperty
    {
        get { return (int)GetValue(AnotherIntPropertyProperty); }
        set { SetValue(AnotherIntPropertyProperty, value); }
    }

    // Using a DependencyProperty as the backing store for AnotherIntProperty.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty AnotherIntPropertyProperty =
        DependencyProperty.Register("AnotherIntProperty", typeof(int), typeof(TestData), new PropertyMetadata(0));


    public TestData()
    {
        AnIntProperty = null;
        AnotherIntProperty = 44;
    }

}

SHARE:

Modify And Save an EF EntityDataSource Bound Entity From Code Behind

If you're using databound controls (e.g. FormView) and binding them to an EntityDataSource you might want to be able to modify (and save) the underlying entity from code. For example, if you have a Question entity bound to a FormView via an EntityDataSource you might want a button to allow the use to vote up or down the question (like StackOverflow for example). To be able to do this without extra trips to the database, the first thing we need to do is get hold of the EF ObjectContext that the EntityDataSource is using.

        // Context that we can use for code-behind operations
        ObjectContext questionObjectContext = null;


        protected void QuestionEntityDataSource_ContextCreated(object sender, EntityDataSourceContextCreatedEventArgs e)
        {
            questionObjectContext = e.Context;
        }

When the QuestionEntityDataSource creates it's context, we take a reference to it in our private field questionObjectContext.

Now in the button click we can up-vote the question:

 

        protected void btnVoteUp_Click(object sender, EventArgs e)
        {
            // force a databind to ensure we have a DataItem in the FormView        
            FormView1.DataBind();

            // Get the actual entity represented by the FormView
            Question questionToUpdate = FormView1.DataItem.WrappedEntity<Question>();

            // Vote-up
            questionToUpdate.VoteScore++;
           
            // using our 'copy' of the EntityDataSource ObjectContext, save the changes
            questionObjectContext.SaveChanges(true);
           
            // Force rebind of FormView to ensure newest data displayed
            FormView1.DataBind();                     
        }

This method feels like a bit of a hack, but ideally you would provide a client-side AJAX version, only if JavaScript is disabled on the client do we use this server-side up-vote.

SHARE:

Accessing Entity Framework Entities In EntityDataSource Data-Bound Controls

If using ASP.NET EntityDataSource and databound controls you may need to access the actual entity object being represented in the control, e.g. a data row in a table or a single entity in a FormView. The actual entity is not readily available however as it is automatically wrapped in a System.Web.UI.WebControls.EntityDataSourceWrapper which is not accessible. The extension method below allows you to gain access to the strongly typed, underlying entity. It is based on the article by Diego Vega which explains the wrapping behaviour in more detail. 

 /// <summary>
/// Gets the actual EF entity object that is being wrapped and databound.
/// </summary>
/// <example>
/// Advert ad = myFormView.DataItem.WrappedEntity<Advert>();
/// (where myFormView is databound to EntityDataSource returning Advert entity)
/// </example>
static class WrappedEFEntityExtensions
{
    public static TEntity WrappedEntity<TEntity>(this object dataItem) where TEntity : class
    {
        var entity = dataItem as TEntity;

        if (entity != null)
            return entity;

        var typeDescriptor = dataItem as ICustomTypeDescriptor;

        if (typeDescriptor != null)
            return (TEntity)typeDescriptor.GetPropertyOwner(null);

        return null;
    }
}

SHARE: