Blog

posts tagged with web

Paging in ASP.NET Core MVC and EntityFramework Core

5 Comments
By Fons Sonnemans, 29-3-2017

Paging, sorting and filtering are common features in websites. Microsoft has written a tutorial how to implement these features in ASP.NET Core MVC with Entity Framework Core. The described solution works but I found it a bit too primitive. It triggered me to create a more powerful solution. Before you can start using my solution you should first read this tutorial, it explains how you can add Entity Framework Core to an ASP.NET Core MVC application.

Project Setup

My WebApplication is an ASP.NET Core Web Application (version 1.1) which uses EntityFramework Core 1.1. I use a commonly used Northwind SQL Server sample database which I created using the following tutorial. I have scaffold the Northwind database and created the controllers for the Suppliers and Products. Read this tutorial to learn how. I have added some extra navigation hyperlinks in the _Layout.cshtml file for the SuppliersController and ProductsController (Action Index).

The Index.cshtml file from the Views/Suppliers folder looks like this. I made some small adjustments like changing the h2 to an h1 and renaming the text 'Index' to 'Suppliers'. The most obvious change is that I removed some of the columns from the table to make it less wide.

@model IEnumerable<WebApplication8.Models.Database.Suppliers>

@{
    ViewData["Title"] = "Suppliers";
}

<h1>Suppliers</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table table-striped">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.CompanyName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ContactName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Address)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.City)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model) {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.CompanyName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.ContactName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Address)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.City)
                </td>
                <td>
                    <a asp-action="Edit" asp-route-id="@item.SupplierId">Edit</a> |
                    <a asp-action="Details" asp-route-id="@item.SupplierId">Details</a> |
                    <a asp-action="Delete" asp-route-id="@item.SupplierId">Delete</a>
                </td>
            </tr>
        }
    </tbody>
</table>

When you browse for the Suppliers Index page (http://localhost:2309/Suppliers) you get the following output. It shows you all suppliers and that list can be very long. Let's add Paging so we can limit the number of suppliers in the list.

Adding Paging

In the earlier mentioned tutorial a PaginatedList<T> class was used. My solution uses a similar approach but I named it PagingList<T>. This class and some other helper classes are packed in a NuGet package named ReflectionIT.Mvc.Paging. You can install the package using the NuGet browser in Visual Studio or by running the 'Install-Package ReflectionIT.Mvc.Paging' command from the Package Manager Console.

After installation you can add the paging services in the ConfigureServices() method of the Startup class. Add the services.AddPaging() call as shown below. This call will register an EmbeddedFileProvider which is used for rendering the View for the Pager ViewComponent. The View will be loaded from the ReflectionIT.Mvc.Paging assembly if it can not be found in your WebApplication.

public void ConfigureServices(IServiceCollection services)
{
    services.AddEntityFramework()
        .AddEntityFrameworkSqlServer()
        .AddDbContext<Models.Database.NorthwindContext>(options =>
            options.UseSqlServer(Configuration["Data:Northwind:ConnectionString"]));

    // Add framework services.
    services.AddMvc();

    services.AddPaging();
}

Next I modified the Index() method from the SuppliersController class. I added an optional 'page' parameter with the default value 1. I defined a query in which I retrieve the Suppliers from the context (NorthwindContext) with the AsNoTracking() method and an OrderBy(). The AsNoTracking() turns off change tracking which improves the performance. The OrderBy() is required to support Paging (Skip() & Take()) in EF. This query is used to create the PagingList. The page size is set to 10. This is the model which is passed to the View. The PagingList will fetch the data from the database asynchronously.

// GET: Suppliers
//public async Task<IActionResult> Index() {
//    return View(await _context.Suppliers.ToListAsync());
//}

public async Task<IActionResult> Index(int page = 1) {
    var qry = _context.Suppliers.AsNoTracking().OrderBy(p => p.CompanyName);
    var model = await PagingList<Suppliers>.CreateAsync(qry, 10, page);
    return View(model);
}

The Index.cshtml view must also be modified. I changed the model type (line 1), added a using (line 2) and an addTagHelper (line 3). Lines 2 and 3 could also be moved to the _ViewImports.cshtml file which would add these lines to every view. I also added a Pager above the table. This is done by invoking the Pager View Component and passing the Model as the pagingList. It is placed inside a <nav> element with an aria-label which is used by screen readers. I have done the same below the <table> but there I used a new feature of ASP.NET Core 1.1. The View Component is invoked as a Tag Helper. It uses the <vc /> element and the class and parameters are translated to lower kebab case.  For more info read this article.

@model ReflectionIT.Mvc.Paging.PagingList<WebApplication8.Models.Database.Suppliers>
@using ReflectionIT.Mvc.Paging
@addTagHelper *, ReflectionIT.Mvc.Paging

@{
    ViewData["Title"] = "Suppliers";
}

<h2>Suppliers</h2>

<nav aria-label="Suppliers navigation example">
    @await this.Component.InvokeAsync("Pager", new { pagingList = this.Model })
</nav>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table table-striped">
   ...
</table>

<nav aria-label="Suppliers navigation example">
    <vc:pager paging-list="@Model" />
</nav>

This results in the following view in which you see two pagers which use bootstrap markup.

 

Adding Sorting

For sorting I added an extra sortExpression parameter to the Index() method of the SuppliersController class. The parameter is optional and uses the CompanyName as the default sort expression in case none is supplied. The OrderBy() method is removed from the query because the PagingList will take care of the ordering. The sortExpression and the default sort expression are now required to create the PageList model.

public async Task<IActionResult> Index(int page = 1, 
                                       string sortExpression = "CompanyName") {

    var qry = _context.Suppliers.AsNoTracking();
    var model = await PagingList<Suppliers>.CreateAsync(
                            qry, 10, page, sortExpression, "CompanyName");
    return View(model);
}

In the View I modified the header of the table. I replaced the DisplayNameFor() calls by SortableHeaderFor() calls. I pass the Model as an extra parameter. This is not required but if you do you will get nice Up and Down indicators (glyphs) in the headers when you sort ascending or descending. You can also specify the SortColumn if you want a different one as the model property. In this example I used this for the City column. There I added the CompanyName as a second column to sort on.

<table class="table table-striped">
    <thead>
        <tr>
            <th>
                @Html.SortableHeaderFor(model => model.CompanyName, this.Model)
            </th>
            <th>
                @Html.SortableHeaderFor(model => model.ContactName, this.Model)
            </th>
            <th>
                @Html.SortableHeaderFor(model => model.Address, this.Model)
            </th>
            <th>
                @Html.SortableHeaderFor(model => model.City, "City, CompanyName", this.Model)
            </th>
            <th></th>
        </tr>
    </thead>

This results in the following view in which you can sort the table by clicking the headers of the columns. An up or down indicator shows you weather you are sorting ascending or descending.

Adding Filtering

To show filtering I will switch to the Products table, it has more records. The Index() method of the ProductsController has a filter parameter (besides page and sortExpression). The query is defined with an extra AsQueryable() method call. This method returns the the type IQueryable<Products>. This allowed me to append an extra Where "clause" to it when the filter is not empty.  In this example I used a Contains() which translates to a like '%...%' condition.

You have to set the RouteValue property of the model with all conditions used in the filter. This is used to build the correct URL in the pager and table headers.

public async Task<IActionResult> Index(string filter, int page = 1, 
                                       string sortExpression = "ProductName") {

    var qry = _context.Products.AsNoTracking()
        .Include(p => p.Category)
        .Include(p => p.Supplier)
        .AsQueryable();

    if (!string.IsNullOrWhiteSpace(filter)) {
        qry = qry.Where(p => p.ProductName.Contains(filter));
    }

    var model = await PagingList<Products>.CreateAsync(
                                 qry, 10, page, sortExpression, "ProductName");

    model.RouteValue = new RouteValueDictionary {
        { "filter", filter}
    };

    return View(model);
}

I added a form in the Index.cshtml with an input named filter and a submit button which has the method set to GET. This will execute the Index action of the controller.

@model ReflectionIT.Mvc.Paging.PagingList<WebApplication8.Models.Database.Products>
@using ReflectionIT.Mvc.Paging
@addTagHelper *, ReflectionIT.Mvc.Paging

@{
    ViewData["Title"] = "Products";
}

<h2>Products</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>

<form method="get" class="form-inline">
    <input name="filter" class="form-control" placeholder="filter" 
           value="@Model.RouteValue["Filter"]" />
    <button type="submit" class="btn btn-info">
        <span class="glyphicon glyphicon-search" aria-hidden="true"></span> Search
    </button>
</form>

<nav aria-label="Products navigation example">
    <vc:pager paging-list="@Model" />
</nav>

<table class="table table-striped">
    <thead>
        <tr>
            <th>
                @Html.SortableHeaderFor(model => model.ProductName, this.Model)
            </th>
            <th>
                @Html.SortableHeaderFor(model => model.Supplier.CompanyName, 
                               "Supplier.CompanyName, ProductName", this.Model)
            </th>
            <th>
                @Html.SortableHeaderFor(model => model.Category.CategoryName, 
                              "Category.CategoryName, ProductName", this.Model)
            </th>
            <th>
                @Html.SortableHeaderFor(model => model.UnitPrice, this.Model)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>

This results in the following view in which you can filter on ProductName and you have paging and sorting.

Customize

You can customize this solutions in a few ways. You can for instance call the AddPaging method which you can use to set the PagingOptions. This allows you to specify which View is used for the Pager ViewComponent. By default a pager based on Bootstrap3 is used. But there is also already a Bootstrap4 view available. Bootstrap4 doesn't support glyphs any more so if you switch to it you also have to specify alternatives for the Up/Down indicators used in the sortable headers of the table.

services.AddPaging(options => {
    options.ViewName = "Bootstrap4";
    options.HtmlIndicatorDown = " <span>&darr;</span>";
    options.HtmlIndicatorUp = " <span>&uarr;</span>";
});

You can also create your own Pager view if you want to. You store it in the folder Views\Shared\Components\Pager. The easiest way to create a custom view is by coping the default Bootstrap3.cshtml from Github. You can remove the Bootstrap classes and add your own or you can generate your own html buttons.

You can also render the Pager using the Html.Partial() Html Helper instead of using the Pager ViewComponent. The following snippet shows the 'SmallPager.cshtml' which I stored in the Views/Shared folder. When you store it in this folder you can access it from all views.

<nav aria-label="Products navigation example">
    @Html.Partial("SmallPager", this.Model)
</nav>

Closure

You can download my sample app using the Download button below. I have published the code of the Pager on GitHub. It also contains a sample webapp which uses Bootstrap4. It's open source. So if you create a better pager please do a pull-request.

Fons

 

Windows 8 Javascript Tips - TypeScript support

0 Comments
By Fons Sonnemans, 9-10-2012

Microsoft announced TypeScript last week. TypeScript is a superset of JavaScript that combines type checking and static analysis, explicit interfaces, and best practices into a single language and compiler. It is an open source programming language developed by Microsoft. If you haven't investigated yet make sure you do. The related links below might help you.

What Microsoft didn't do (yet) is add TypeScript support to JavaScript Windows Store apps. In this blog I will explain how to add this support to Visual Studio so you can use TypeScript in all your Windows 8 apps.

Step 1 - Install TypeScript

You can install TypeScript for Visual Studio 2012 using an MSI setup which you can download here. Make sure you first close all Visual Studio instances to avoid installation problems.

Step 2 - Install Web Essentials 2012

The latest version of Web Essentials 2012 extension adds support for TypeScript preview and compilation. When you save a TypeScript (.ts) file it will be compiled into a JavaScript (.js) file. So make sure you install it using the Visual Studio menu Tools, Extensions and Updates. If you already installed it make sure you update it to the latest version.

Step 3 - Add TypeScript ItemTemplate

If you try to add a TypeScript item to the 'js' folder you will see that there is no TypeScript template available. The setup from Microsoft did not (yet) add this template to Visual Studio. My solution for this problem was quite easy. I copied the following file 'C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\Extensions\msfz1qy5.oca\~IC\IT\CSharp\1033\f.zip' to my '\My Documents\Visual Studio 2012\Templates\ItemTemplates\JavaScript' folder. I also renamed the file to TypeScript.zip. Next I opened the zip file and edited the 'f.vstemplate' file. In the file I changed the ProjectType element from CSharp to JavaScript.

To make life easy you can download my TypeScript.zip file so you don't have to repeat my steps. Make sure you place it in the right ItemTemplate folder.

Step 4 - Create a JavaScript Project

This step is really easy. Just create a Project in Visual studio using one of the JavaScript Windows Store templates.

Step 5 - Add TypeScript item

When you add a new Item to the 'js' folder you can now select the TypeScript template.

The preview feature of Web Essentials 2012 shows you how your TypeScript file is compiled into JavaScript.

You should also change the Package Action of the Demo.ts file to 'None'. Otherwise the file will also be included in the APPX file which will make it larger. Only the Demo.js file is required.

TypeScript options

Web Essentials 2012 also supports some options which you might want to change. I only want to compile my TypeScripts on Build and not on every Save. You can also turn preview off but that didn't work on my computer. I hope they will fix it.

Closure

Now it is up to you to write your own TypeScript files. I love the ability to write clean OO code using: classes, interfaces, modules (namespaces), type annotations, compile time type checking and arrow functions (similar to Lambda Expression in C#). I hope this blog will help you write great Windows Store apps.

Cheers,

Fons

Related Links

Fix the ASP.NET Themes with IE8’s X-UA-Compatible header problem

0 Comments
By Fons Sonnemans, 26-9-2008

If you’ve got a page that doesn’t render correctly in IE8’s new standards mode, you can add a meta tag to the page which requests that IE8 render it in IE7 mode. IE8 only recognizes the X-UA-Compatible header if it’s the first META tag, appearing immediately after the tag.

<!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 id="Head1" runat="server"> <meta http-equiv="X-UA-Compatible" content="IE=7" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link rel="shortcut icon" href="favicon.ico" /> <title>Test</title> </head> <body>[...]</body></html>

The Problem

The ASP.NET Theme system writes out the theme CSS reference immediately after the <HEAD> tag. This makes it impossible to set the X-UA-Compatible header or to use Stylesheets in your theme folder.

<!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 id="Head1"> <link href="App_Themes/Default/Stylesheet.css" type="text/css" rel="stylesheet" /> <meta http-equiv="X-UA-Compatible" content="IE=7" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link rel="shortcut icon" href="favicon.ico" /> <title>Test</title> </head> <body>[...]</body></html>

The Solution

To fix this problem I have added an implementation for the Application_PreRequestHandlerExecute event in the Global.asax. This method will sort the controls inside the header. It places the X-UA-Compatible metatag on top. I use an anonymous method which is subscribed to the PreRenderComplete event of page. This method uses LINQ to find the metatag. Then removes it from the header and add it again on top.

void Application_PreRequestHandlerExecute(object src, EventArgs e) {
    Page p = this.Context.Handler as Page;
    if (p != null) {
        p.PreRenderComplete += (sender, ee) => {
            // I can't use p, it has no Header set (strange), use sender instead
            Page page = sender as Page; 
            if (page.Header != null) {
                // Find the Compatible Meta tag
                var ctrl = page.Header.Controls.Cast<Control>().FirstOrDefault(
                    c => c is HtmlMeta && ((HtmlMeta)c).HttpEquiv == "X-UA-Compatible");

                // If found remove it and add it on top
                if (ctrl != null) {
                    page.Header.Controls.Remove(ctrl);
                    page.Header.Controls.AddAt(0, ctrl);
                }
            }
        };
    }
}

This results in the correct HTML.

<!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 id="Head1"> <meta http-equiv="X-UA-Compatible" content="IE=7" /> <link href="App_Themes/Default/Stylesheet.css" type="text/css" rel="stylesheet" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link rel="shortcut icon" href="favicon.ico" /> <title>Test</title> </head> <body>[...]</body></html>
Tags: Web, ASP.NET

ASP.NET ViewStateParameter

0 Comments
By Fons Sonnemans, 30-5-2007

I like the ASP.NET 2.0 DataSource controls. They are very flexible and easy to use. It has some great parameters like SessionParameter and ControlParameter. It doesn't have a ViewStateParameter. I have written it myself to fill this gap.

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

namespace ClassLibrary1 {

    public class ViewStateParameter : Parameter {

        private String _key;

        public String Key {
            get {
                return _key;
            }
            set {
                _key = value;
            }
        }

        protected override object Evaluate(HttpContext context, Control control) {
            Type t = control.Page.GetType();
            System.Reflection.PropertyInfo pi = t.GetProperty("ViewState", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
            StateBag pageViewState = pi.GetValue(control.Page, null) as StateBag;
            return pageViewState[Key];
        }
    }
}

My solution uses Reflection to retrieve the ViewState out of the Page. You will need FullTrust to use the code, bummer.

Download the source + sample website here.
Tags: Web, ASP.NET

Objectmap.nl

0 Comments
By Fons Sonnemans, 5-11-2006

Today we have published a new website called Objectmap. It is for now in Dutch only. It uses Google Maps to display objects on a map. Registered users can add the objects to the system. A script is generated for each object which can be used to display the map. When you copy&paste this script into your own webpage you can display the map in a pop-up window or IFrame.


Click here for a pop-up window demo.

A complete explanation how to add objects and how to make them visible on your own site can be found here. The website is still in BETA. You can send a mail to info@objectmap.nl to request a beta account.

Tags: Web, ASP.NET

Playing with Google Maps

0 Comments
By Fons Sonnemans, 22-9-2006

I have developed a small website which uses Google Maps to prepare myself for a new project. Have a look here.

I have used GoogleMaps.Subgurim.NET to simplify things. This is an ASP.NET control which encapsulates the Google Maps API. Their site is in Spanish but with Babel Fish you can translate it to English.

The full C# source of the website can be downloaded from here.

Tags: Web, ASP.NET

Playing with Ajax (Atlas)

0 Comments
By Fons Sonnemans, 27-3-2006

I have finally found some time to play with Ajax. I have built some simple Grid/Detail pages which are place inside an UpdatePanel. This server centric model is really easy and powerful. I have never really liked the user experience of web applications. Atlas will changes this and will make the WEB even more popular.

Have a look at the result demo site. I use the ObjectDataSource to bind to Buffy.NET BusinessObjects. It's so easy.

Tags: Web, ASP.NET

Move to ASP.NET 2.0

0 Comments
By Fons Sonnemans, 2-3-2006

I have finally rewritten this website to ASP.NET 2.0. It was really a good learning experience. You only learn it by doing it. I have tried to use the new features like:

It wasn't really difficult to do. Microsoft really kept their promise that 70% of the code could disappear. ASP.NET 2.0 rocks. I'm planning to add some extra features soon, like: WebParts, Health Monitoring, other new controls, comments on blog posts, a new stylesheet and a new logo.

Update 2006-sept-25: Added Google Sitemaps, Control Adapters and UrlRewritingNet.

Tags: Web, ASP.NET

Websites using my CMS are online

0 Comments
By Fons Sonnemans, 20-10-2005

I have written a small content management system using ASP.NET. Sjef de Corte has implemented 3 websites with it. Well done Sjef!

Tags: Web, ASP.NET

InitialFocus on a ASP.NET WebForm

0 Comments
By Fons Sonnemans, 28-4-2004

Download InitialFocusDemo.zip

Introduction

The PageUtil class has a static method SetInitialFocus(control) which can be used to generate a JavaScript for an ASP.NET page (WebForm), which sets the focus on a (given) control.

        private voidPage_Load(objectsender, System.EventArgs e)
        {
            // Set the InitialFocus on TextBox1
            ReflectionIT.Web.PageUtil.SetInitialFocus(TextBox1);
        }

TextBox1: 

PageUtil Class

using System;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace ReflectionIT.Web {
    /// <summary>
    /// Utility class for a ASP.NET page
    ///
    /// Be sure to notice that this code is provided as a technology sample,
    /// 'as is' and no warranties are made by the author.
    ///
    /// For questions and comments: Fons.Sonnemans@reflectionit.nl
    ///
    /// </summary>
    public class PageUtil {

        /// <summary>
        /// Set the InitialFocus to the given control. Only works when JavaScript is supported.
        /// </summary>
        /// <param name="control">Control to set the InitialFocus on.</param>
        publicstaticvoid SetInitialFocus(Control control){
            // Postpone setting the InitialFocus to the PreRender event of the control.
            // The control might not yet have a Page property, in PreRender it always has!
            control.PreRender += new EventHandler(InitialFocusControl_PreRender);
        }

        /// <summary>
        /// Set the InitialFocus to the control that notified this EventHandler (sender)
        /// </summary>
        /// <param name="sender">The source of the event. </param>
        /// <param name="e">An EventArgs that contains the event data. </param>
        privatestaticvoid InitialFocusControl_PreRender(object sender, EventArgs e){
            Control control=senderas Control;

            if(control.Page == null){
                thrownew ArgumentException(
                    "The Control must be added to a Page before you can set the IntialFocus to it.");
            }
            if(control.Page.Request.Browser.JavaScript ==true) {
                // Create JavaScript
                StringBuilder s =new StringBuilder();
                s.Append("\n<SCRIPT LANGUAGE='JavaScript'>\n");
                s.Append("<!--\n");
                s.Append("function SetInitialFocus()\n");
                s.Append("{\n");
                s.Append(" document.");

                // Find the Form
                Control p =control.Parent;
                while(!(p is System.Web.UI.HtmlControls.HtmlForm))
                    p = p.Parent;
                s.Append(p.ClientID);

                s.Append("['");
                s.Append(control.UniqueID);

                // Set Focus on the selected item of a RadioButtonList
                RadioButtonList rbl= controlas RadioButtonList;
                if(rbl!= null){
                    stringsuffix ="_0";
                    int t = 0;
                    foreach(ListItem liinrbl.Items){
                        if(li.Selected) {
                            suffix= "_"+ t.ToString();
                            break;
                        }
                        t++;
                    }
                    s.Append(suffix);
                }

                // Set Focus on the first item of a CheckBoxList
                if(controlis CheckBoxList){
                    s.Append("_0");
                }

                s.Append("'].focus();\n");
                s.Append("}\n");

                if(control.Page.SmartNavigation)
                    s.Append("window.setTimeout(SetInitialFocus, 500);\n");
                else
                    s.Append("window.onload = SetInitialFocus;\n");

                s.Append("// -->\n");
                s.Append("</SCRIPT>");

                // Register Client Script
                control.Page.RegisterClientScriptBlock("InitialFocus", s.ToString());
            }
        }
    }
}

Example

Textbox1 has the focus after the page is loaded.

 WebForm1.aspx

<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="InitialFocusDemo.WebForm1"smartNavigation="False"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
    <HEAD>
        <metacontent="Microsoft Visual Studio 7.0"name="GENERATOR">
        <metacontent="C#"name="CODE_LANGUAGE">
        <metacontent="JavaScript (ECMAScript)"name="vs_defaultClientScript">
        <metacontent="http://schemas.microsoft.com/intellisense/ie5"name="vs_targetSchema">
    </HEAD>
    <body>
        <formid="Form1"method="post"runat="server">
            <table>
                <tr>
                    <td>
                        <asp:labelid="Label1"runat="server">Label</asp:label>
                    </td>
                    <td>
                        <asp:textboxid="TextBox1"runat="server"></asp:textbox>
                    </td>
                </tr>
                <tr>
                    <td>
                        <asp:labelid="Label2"runat="server">Label</asp:label>
                    </td>
                    <td>
                        <asp:textboxid="TextBox2"runat="server"></asp:textbox>
                    </td>
                </tr>
                <tr>
                    <td>
                    </td>
                    <td>
                        <asp:Button id="Button1"runat="server" Text="Button"></asp:Button>
                    </td>
                </tr>
            </table
        </form>
    </body>
</HTML>

 WebForm1.aspx.cs

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace InitialFocusDemo
{
    /// <summary>
    /// Summary description for WebForm1.
    /// </summary>
    public class WebForm1: System.Web.UI.Page
    {
        protected System.Web.UI.WebControls.Label Label1;
        protected System.Web.UI.WebControls.Label Label2;
        protected System.Web.UI.WebControls.TextBox TextBox1;
        protected System.Web.UI.WebControls.Button Button1; 
        protected System.Web.UI.WebControls.TextBox TextBox2;
    
        public WebForm1()
        {
            Page.Init +=new System.EventHandler(Page_Init);
        }

        privatevoid Page_Load(objectsender, System.EventArgs e)
        {
            // Set the InitialFocus on TextBox1
            ReflectionIT.Web.PageUtil.SetInitialFocus(TextBox1);
        }

        privatevoid Page_Init(objectsender, EventArgs e)
        {
            //
            // CODEGEN: This call is required by the ASP.NET Web Form Designer.
            //
            InitializeComponent();
        }

        #region Web Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        privatevoid InitializeComponent()
        {    
            this.Load +=new System.EventHandler(this.Page_Load);

        }
        #endregion

    }
}

Thanks to:

  • Philip Quirke who reported a bug in an earlier version. I used the controls 'UniqueID' property instead of 'ClientID' in the SetInitialFocus() method.
  • S Patil who reported that it didn't work with Netscape 4.7x browser. I have therfore changed the JavaScript.
  • Scott Dinwiddie who reported the SmartNavigation problem.
  • Waylon Campbell who reported the RadioButtonList problem.

Any suggestions and feedback for improving this article is most welcome. Send your suggestions and feedback to Fons.Sonnemans@reflectionit.nl

Tags: Web, ASP.NET
12>

All postings/content on this blog are provided "AS IS" with no warranties, and confer no rights. All entries in this blog are my opinion and don't necessarily reflect the opinion of my employer or sponsors. The content on this site is licensed under a Creative Commons Attribution By license.