Improved GridView & ListView Row Selection in ASP.NET 4.0

Previous versions of GridView and ListView selection behaviour operating in a potentially confusing way to the user. For example, if the third row was selected and then page 2 was selected, the third row on page 2 would also be selected. ASP.NET 4 fixes this by adding the EnablePersistedSelection and DataKeyNames properties to provide what MSDN calls "Persisted Selection".

To make selection work more as expected simple set EnablePersistedSelection="True" on the GridView or ListView and set the DataKeyNames property to the unique\primary key\property of the underlying databound object\row\etc.

SHARE:

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:

URL Routing in ASP.Net 4 Web Forms

ASP.Net 4 simplifies the routing experience introduced in ASP.NET 3.5 SP1. Routing allows the use of more meaningful or 'friendly' URLs (which may also benefit search engine ranking).

For example, rather than .../ShowCountryDetails.aspx?country=australia routing could be enabled so the url looked like .../countries/australia

Defining Routes

You can define routes using the MapPageRoute method of the RouteCollection class. The code below is an extract from Global.asax.cs, the method RegisterRoutes is called from application start, and it's here that a route is registered:

protected void Application_Start(object sender, EventArgs e)
{
    RegisterRoutes(RouteTable.Routes);
}



void RegisterRoutes(RouteCollection routes)
{
    /*  Create a routing which maps SayHello to ShowGreeting.aspx
        and defines 2 parameters, greeting and name. */
    routes.MapPageRoute("ShowGreetingRoute",
        "SayHello/{greeting}/{name}",
        "~/ShowGreeting.aspx");
}
 

Accessing URL Param Values In Markup

When routing is in use, RouteValue expressions can be used in markup:

<asp:Literal Text="<%$RouteValue:greeting%>" runat="server" />

Accessing URL Param Values In Code

In code we can use Page.RouteData to get the values of any routed URL parameter values; we can either check for a null before calling ToString() or as in the following example use ContainsKey to check first (in practice you wouldn't use 'magic' string literals for "greeting", rather you'd define these as constants, etc.):

string greetingText = "no greeting text specified";
       
if (Page.RouteData.Values.ContainsKey("greeting"))
        greetingText = Page.RouteData.Values["greeting"].ToString();

Creating Routed URLs in Markup

We can create routed URLs in markup by using RouteUrl expressions:

<asp:HyperLink ID="declarativeLinkToRoutedPage"
    NavigateUrl="<%$RouteUrl:greeting=bonjour, name=Bob, routename=ShowGreetingRoute%>"
    Text="Say hello to Bob in French" runat="server" />

Creating Routed URLs in Code

Instead of setting NavigateUrl using RouteUrl in markup, we could set it in code RouteValueDictionary & VirtualPathData:

/// <summary>
/// Example of setting a Hyperlink's NavigateUrl in code
/// </summary>
private void SetHyperlinkNavigateUrlProperty()
{
    RouteValueDictionary routeParamValues = new RouteValueDictionary
    {
        {"greeting", "goodnight"},
        {"name", "Fred"}
    };

    VirtualPathData vpd = RouteTable.Routes.GetVirtualPath(null, routeParamValues);

    linkSetInCodeToRoutedPage.NavigateUrl = vpd.VirtualPath;
}

Using Routed Values as Parameters in Bound Data Sources

We can define RouteParameter in a data source that is being bound to.

The following markup shows a DetailsView being bound to an ObjectDataSource. The SelectParameters collection defines a RouteParameter which will pass the value of the 'name' URL routed parameter to the GetPersonsAge method of the MockDataBase class.

<asp:ObjectDataSource ID="odsPersonsAge" runat="server"
    SelectMethod="GetPersonsAge" TypeName="ASPNET4Routing.MockDataBase">
    <SelectParameters>
        <asp:RouteParameter Name="name" RouteKey="name" Type="String" />
    </SelectParameters>
</asp:ObjectDataSource>
      
<asp:DetailsView ID="DetailsView1" runat="server" DataSourceID="odsPersonsAge"
                AutoGenerateRows="true" /> 

<asp:ObjectDataSource ID="odsPersonsAge" runat="server"
    SelectMethod="GetPersonsAge" TypeName="ASPNET4Routing.MockDataBase">
    <SelectParameters>
        <asp:RouteParameter Name="name" RouteKey="name" Type="String" />
    </SelectParameters>
</asp:ObjectDataSource>

SHARE:

Managing ViewState in ASP.Net 4

ASP.Net 4 allows us to use an opt-in viewstate approach rather than an opt-out approach in previous ASP.Net versions (EnableViewState was available in prior versions, but if you disable viewstate, all children of the page/container also inherited this with no one to override and opt-in).

To use the opt-in approach in ASP.Net 4, the new ViewStateMode property can be used. The following example disables viewstate for the whole page (all children will inherit this disabled value) but Label2 opts in to viewstate. The codebehind sets the values of the labels only on the first page load - not any subsequent postbacks.

<%@ Page Language="C#" AutoEventWireup="true"
 CodeFile="Default.aspx.cs" Inherits="_Default"
 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>
        <p><asp:Label ID="Label1" Text="not set" runat="server" /></p>
        <p><asp:Label ID="Label2" Text="not set" ViewStateMode="Enabled" runat="server" /></p>
        <p><asp:Button Text="do a postback" runat="server" /></p>
    </div>
    </form>
</body>
</html>

Code behind:

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        Label1.Text = "this text has been set from code behind only on the first load i.e. not set in any following postbacks.";
        Label2.Text = Label1.Text;
    }
}

The first time the page loads, the 2 text boxes show the text as set in the code behind:

 

 



Once a postback happens (in this case by clicking the button), no code is executed in the Page_Load event due to the if (!IsPostBack) check. This means that the Text property of the labels will be set to what's initially declared in the aspx mark-up (i.e. "not set"); however because we have set ViewStateMode="Enabled" on Label2, it's Text will be retained in viewstate between callbacks:

 

SHARE:

Setting Meta Tags in ASP.Net 4

ASP.Net 4 adds 2 new Meta tag related properties to the Page. They can be used to set meta tags for keywords and description.

You can set them in the code behind:

Page.MetaKeywords = "keyword1, keyword2, keyword3";
Page.MetaDescription = "Example of new meta tag support in ASP.Net 4";

You can also set in the @Page directive:

<%@ Page Language="C#" AutoEventWireup="true"
MetaKeywords="keyword1, keyword2, keyword3"
MetaDescription="Example of new meta tag support in ASP.Net 4"
CodeFile="Default.aspx.cs" Inherits="_Default" %>

The ouput of either of these methods renders html similar to the following:


<!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>
    ASP.NET 4 Meta Tag Support
</title><meta name="description" content="Example of new meta tag support in ASP.Net 4" /><meta name="keywords" content="keyword1, keyword2, keyword3" /></head>
<body>
 </body>
</html>
 

SHARE:

Session State Compression in ASP.NET 4

ASP.NET 4 introduces the ability to turn on compression of session data.

Compression is disabled by default, to enable it modify the web.config file and set the new compressionEnabled attribute to true:

<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
<sessionState
mode="InProc"
stateConnectionString="tcpip=127.0.0.1:42424"
stateNetworkTimeout="10"
sqlConnectionString="data source=127.0.0.1;Integrated Security=SSPI"
sqlCommandTimeout="30"
customProvider=""
compressionEnabled="true" cookieless="UseCookies" cookieName="ASP.NET_SessionId" timeout="20" allowCustomSqlDatabase="false" regenerateExpiredSessionId="true" partitionResolverType="" useHostingIdentity="true"> <providers> <clear /> </providers> </sessionState> </system.web> </configuration>

The <sessionState> element is not defined in either the root machine or web config, and according to msdn the above (minus the compressionEnabled attribute) is the default.

The choice to enable compression should only be taken if the server is not already CPU bound as compression adds an additional compression/decompression overhead.

SHARE:

Permanently Redirecting a Page in ASP.NET 4

Rather that using Response.Redirect("newpage.aspx"), ASP.NET 4 introduces the RedirectPermanent method.

To signal that the redirect is of a permanent nature (such as a url to a page changing permanently) you can now call:

Response.RedirectPermanent("newpage.aspx");

This will issue a http response HTTP 301 (Moved Permanently) rather than the normal HTTP 302 (Found). This can potentially remove a round trip to the server.

From msdn.com: "Search engines and other user agents that recognize permanent redirects will store the new URL that is associated with the content, which eliminates the unnecessary round trip made by the browser for temporary redirects."

SHARE: