Manually Compressing Any ASP.NET Page, PageMethod, MVC Action, HTTPHandler,..

Compressing A Single ASP.NET Response Manually

This post is about compressing your HTTP result without using IIS Dynamic Compression. I’ll save the story to the end and give you the code first:

using System;
using System.IO.Compression;
using System.Web;
 
namespace WebCompressionSample
{
    public static class ResponseCompressor
    {
        public static void Compress(HttpContext context)
        {
            // Quite often context will be something like `HttpContext.Current`
            // which in some conditions may not be available.
            // This is likely due to mixing conerns in calling code, 
            // so, it's up to you whether to handle differently.
            if (context == null)
            {
                return;
            }
 
            // Among other things, this header tells the server 
            // whether the client can decompress compressed responses, 
            // and what compression format(s) it supports
            string acceptEncoding = context.Request.Headers["Accept-Encoding"];
 
            if (string.IsNullOrEmpty(acceptEncoding))
            {
                return;
            }
 
            // The two common compression formats in web are GZip and Deflate
 
            if (acceptEncoding.IndexOf("gzip",
                StringComparison.OrdinalIgnoreCase) > -1)
            {
                // Read the response using a GZip compressor ,
                //    and replace the output with compressed result
                context.Response.Filter = new GZipStream(
                        context.Response.Filter, CompressionMode.Compress);
                // Tell the client the ouput they got is compressed in GZip
                context.Response.AppendHeader("Content-Encoding", "gzip");
            }
            else if (acceptEncoding.IndexOf("deflate",
                StringComparison.OrdinalIgnoreCase) > -1)
            {
                // Read the response using a Deflate compressor ,
                //    and replace the output with compressed result
                context.Response.Filter = new DeflateStream(
                    context.Response.Filter, CompressionMode.Compress);
                // Tell the client the ouput they got is compressed in Deflate
                context.Response.AppendHeader("Content-Encoding", "deflate");
            }
        }
    }
}

Well, this shows how to do the compression itself. Depending on how you do ASP.NET, you probably will call it differently.

In my case, I called it manually from an ASP.NET Webforms PageMethod (more on why below), but if you are using ASP.NET MVC for example, you probably want to wrap it in an ActionFilter and apply that to the action you want to compress its output. Let me know in the comments or on twitter if you have a problem implementing it in a particular situation.

IIS Dynamic Compression

IIS 7+ has built in dynamic compression support (compressing output of server-side scripts like ASP.NET, PHP, etc.). It’s not by default because compressing dynamic content means running the compression for every request (because it doesn’t know what the server-side script will generate for every request, the point of using server-side programming is generating dynamic content!).

Static compression on the other side (caching static files like styles and scripts) is on by default because once the static resource is compressed, the compressed version is cached and served for every future request of the same file (unless the file changes of course).

General advice: I’d say if your server side scripts expect to return large text-based content (say, large data, even after paging, etc. like large reports or whatever), always turn dynamic compression on, at least for the pages that expect to return large data sets of text.

In many cases though the majority of large files will be scripts (and possibly images) will be the larger parts though, which are often taken care of (for scripts for example) by IIS static compression or ASP.NET Bundling.

Creating Absolute Urls Of Controller Actions In ASP.NET MVC

An Example Of The Need To Use Absolute URLs

I have been doing some work around twitter and ASP.NET MVC. The way twitter authentication works, is that I have a page that creates a twitter URL, redirects the user to twitter, imagethe users accepts to use the application associated with the website, and twitter redirects the user to a callback URL, the completes processing of the user credentials. In order to set a callback URL dynamically (especially in development, when the callback is likely a localhost one), we need to send the absolute URL to twitter.

Other examples might include having a “permanent URL” for some resource (product, blog post, etc..), or maybe a link to be used in emails or so. There can be many usages, so, let’s see how to do it!

How We Did It In Webforms

In webforms, the easiest way to do it was to use the Control.ResolveClientUrl() method.

Typically you pass a URL relative to the current code file (.aspx, .master or .ascx file). and it returns the corresponding absolute URL of that. Of course when the file is a control or a master page file, we don’t always want/have a path relative to this file. The work around for this was passing a relative URL that starts with “~/”. As you probably know, “~/” represents the root of the website.

Example:

Assuming your website is running in “https://localhost:4444/my-application”, calling:

var contactUsUrl = Page.ResolveClientUrl("~/About/Contact-Us.aspx");

from any page or control wiill return “https://localhost:4444/my-application/About/Contact-Us.aspx”.

Fully Qualified Urls In ASP.NET MVC

Similar to webforms, where we used a current control as a starting point, we can use the controller (or view, if we really want to [hint: we don’t]), to access the current instance of “UrlHelper” class (the “Url” property of an ASP.NET MVC Controller), which gives us access to the routing system that comes with ASP.NET in general, and gives shortcut methods specific to ASP.NET MVC, like Url.Action().

This will return us the relative URL though, to convert this to an absolute / fully-qualified URL, we use Request.Url.AbsoluteUri (Controller.Request is the current HttpRequestBase instance) to get the absolute Uri information, and the “UriBuilder” class to create the Url.

Update:

Turns out you can call any of the UrlHelper methods and get an absolute URL directly if you call the overload that accepts a “protocol” value (also called “scheme”, that’s  “http”, “https”, etc..), even if the protocol is the same protocol used in the current request.

Original Example

Going with the same assumptions in the webforms example, replacing “contact-us.aspx” page with a controller “AboutController”, and an action “ContactUs” that has ActionName set to “Contact-Us”, adding the following code inside any ASP.NET MVC action:

var contactUsUrlBuilder =
    new UriBuilder(Request.Url.AbsoluteUri)
        {
            Path = Url.Action("Contact-Us", "About")
        };

var contactUsUri = contactUsUrlBuilder.Uri;
var contactUsUriString = contactUsUrlBuilder.ToString();
// or contactUsUrlBuilder.Uri.ToString()

can be used to get “https://localhost:4444/my-application/About/Contact-Us”.

Updated Example

We can get “contactUsUriString” in the previous example in a different way, by calling:

var contactUsUriString =
    Url.RouteUrl("" /* route name, add if needed */,
                 new // route values, add more if needed
                     {
                         action = "Contact-Us",
                         controller = "About"
                     },
                 Request.Url.Scheme);

Or alternatively even more compact:

var contactUsUriString =
    Url.Action("Contact-Us", "About",
               routeValues: null /* specify if needed */,
               protocol: Request.Url.Scheme);

Of course we could change the action name and routing etc to maintain lower case, or do it from IIS or so, but doing this would be too much to the point this blog post is concerned about

Hope this was useful to you.

CodeProject

Ajax Control Toolkit New Release, HTML Editor, Combo Box, Color Picker

the AjaxControlToolkit has a new release, the release includes bug fixes and new HTMLEditor, ComboBox, ColorPicker controls/extenders.

HTML Editor

I liked the HTMLEditor control so much from first sight. Pretty lightweight and customizable. I still need to check whether it works fine inside UpdatePanel and whether it can be used with RequiredFieldValidator or so.

image  image

http://www.asp.net/AJAX/AjaxControlToolkit/Samples/HTMLEditor/HTMLEditor.aspx

 

Color Picker

The ColorPicjker is just an extender to TextBox control. This means a lot of opportunities and promises less problems with validators, etc… It’s a very nice one too.

image

http://www.asp.net/AJAX/AjaxControlToolkit/Samples/ColorPicker/ColorPicker.aspx

 

Combo Box

The ComboBox is a control that is like a DropDownList with auto complete except that the user can enter a new text that is NOT in the existing items. This is good or bad based on your sole use.

I did not like how it’s not well styled in the example page. This means it’s going to be headache to style in my sites as well, which is one of the biggest problems with AJAXToolkit in general.

image

http://www.asp.net/AJAX/AjaxControlToolkit/Samples/ComboBox/ComboBox.aspx

 

 

Prevent ASP.NET Validators from Massively Increasing Page Size

This is problematic with ASP.NET AJAX. The main Script Components are NOT sent to the client when in :Legacy” mode. This is “By design” in ASP.NET AJAX, although it is a clear limitation!!

I’m investigating the problem for other solutions and will be sending an update soon.

Thank you, Iman Halawany, for making me note this. I’ve been working on WCF services and ASP.NET MVC stuff lately than normal webforms, so, didn’t realize this obvious showstopper.

To all my readers, I owe you a BIG apology.

The Problem

ASP.NET validators and ValidationSummary controls are rendered as SPAN tags that are shown and hidden based on validation state. The properties of the validators are written normally via JavaScript calls similar to these:

<script type=“text/javascript”>

//<![CDATA[

var Page_ValidationSummaries =  new Array(document.getElementById(“vdsSiteLogin”));

var Page_Validators =  new Array(document.getElementById(“rfvEmail”), document.getElementById(“revEmail”), document.getElementById(“rfvName”));

//]]>

</script>

 

<script type=“text/javascript”>

//<![CDATA[

var rfvEmail = document.all ? document.all[“rfvEmail”] : document.getElementById(“rfvEmail”);

rfvEmail.controltovalidate = “txtEmail”;

rfvEmail.errormessage = “Email Missing”;

rfvEmail.validationGroup = “SiteLogin”;

rfvEmail.evaluationfunction = “RequiredFieldValidatorEvaluateIsValid”;

rfvEmail.initialvalue = “”;

var revEmail = document.all ? document.all[“revEmail”] : document.getElementById(“revEmail”);

revEmail.controltovalidate = “txtEmail”;

revEmail.errormessage = “Email is invalid”;

revEmail.validationGroup = “SiteLogin”;

revEmail.evaluationfunction = “RegularExpressionValidatorEvaluateIsValid”;

revEmail.validationexpression = “\w+([-+.’]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*”;

var rfvName = document.all ? document.all[“rfvName”] : document.getElementById(“rfvName”);

rfvName.controltovalidate = “txtEmail”;

rfvName.errormessage = “Password is missing”;

rfvName.validationGroup = “SiteLogin”;

rfvName.evaluationfunction = “RequiredFieldValidatorEvaluateIsValid”;

rfvName.initialvalue = “”;

var vdsSiteLogin = document.all ? document.all[“vdsSiteLogin”] : document.getElementById(“vdsSiteLogin”);

vdsSiteLogin.validationGroup = “SiteLogin”;

//]]>

 

</script>

 

Note that this is for ONLY 3 validators, 1 validation summary and in directly a page that doesn’t use a master page, not in nested user control or such!

How about a page with over 30+ validators (yeah, those forms!!), and each with ClientID like “ctl00_cphBody_ct00_fvUserLogin_rfvEmail_” and such?

If you have ever wondered why those pages take so much time loading, this code block (multiplied per number of validators you have and their properties set) is one reason.

You cannot even take the JavaScipt in separate file that can be cached, because this is dynamically created as per the visible validation controls.

The Solution (See Above Note)

The clear alternative to setting those properties via JavaScript long block with huge ClientIDs is to put the properties in the SPAN tags of the validation controls themselves.

The reason that AS.NET does not do this by default is that this is not XHTML 1.0 Transitional compliant, because the validator properties are not XHTML attributes of the SPAN tag.

ASP.NET tries to render XHTML 1.0 Transitional markup by default. But you can change that in your web.config file by adding one line under <system.web>:

<system.web>

        <xhtmlConformance mode=Legacy/>

 

This will make the properties render in the SPAN tags themselves, saving so much code in real life scenarios.

Personally I’d recommend: DO THIS IN EVERY WEBSITE YOU HAVE (See above note)

 

Thanks to Paulo Morgado for mentioning this.

ASP.NET Validators Client Side APIs: MSDN List Page

If you have ever thought that the famous if(Page_ClientValidate("validationGroup")) {/*JS Code*/}  and myValidator.ValidationGroup = "validationGroup"; are sure not enough client side capabilities in ASP.NET validators, you are right.

The list of client side API for ASP.NET Validators can be found on this MSDN page "ASP.NET Validation in Depth":

http://msdn.microsoft.com/en-us/library/aa479045.aspx#aspplusvalid_properties

Look for subtitle: "Client-Side APIs".

 

Thanks Simone Chiaretta for mentioning the topic, Mohamed Tayseer for sharing the topic on facebook, and Richard Cook for his comment on the post making me search for the complete list.

ASP.NET 3.5 Extensions, MVC, Expression Studio and Silverlight & IE 8 Preview Updates

With MIX, and meeting the announced MVC roadmap, the new versions of AS.NET Extensions are now publicly available.ASP.NET MVC Framework Preview 2 is available also as separate download, same as Silverlight 2.0 BETA 1 (and Expression studio, Silverlight VS 2008 Tools).

Download Links:

Additional Resources:

  • Announcements
  •  

    Have fun :)

    Unit Test Friendly File Upload In ASP.NET

    The Problem

    In an N-tier application, you keep your logic in a business logic tier, typically a different VS project that can be used from a website, a windows service, or desktop application, and that should be valid to writ unit tests against on its own.

    But how about if your requirements say that you need to to upload some file for the business logic to work? Think of a scanned image (signed contract maybe?) or just a comma separated value file containing some emails.  Typically the business logic tier will be the place to to handle this, but how can you send the uploaded file to it? You can get the file as “HttpPostedFile” from the “Request.Files” collection or the file upload control itself, but, to receive it in the business logic, project, the easiest way is to add reference to “System.Web” dll and accept the type “HttpPostedFile” as method argument or so, and when in need to save the file physically, you call the “SaveAs” method of “HttpPostedFile” … So simple right ?

    But how about unit testing? “HttpPostedFile” has no public constructor and is sealed class (you cannot inherit from). How will you write a unit test for a method that accepts “HttpPostedFile” as argument to work? You’d go finding a way to mock that or just forget about testing that particular method!!

    A Simple Solution

    Well, it’s more simple than you think. An “HttpPostedFile” has a property called “InputStream”, which is of type “System.IO.Stream”. Hey, that has nothing to do with “system.Web” :). You can create your own FileStream or whatever other stream in the unit test, and then only worry about other areas you want to test related to the method in your business logic.

    Because I know dealing with streams is quite ugly (at least it is to me!), I wrote an example that will walk you though a sample usage of that property. I have the complete example VS solution as attached file at the end of the example code below.

    The Business Logic

    In a real world example, the business class will have various parameters including the file, but in this example, I have made my business class (I call it “FileLogic”) just worry about the file. It has a method that treats the stream coming to it as containing just text, reads that text and returns it. Note the type of the only method parameter

    <code>/// <summary>
    /// This is the simplest method ever. Read the stream whether from web upload or whatever
    /// As the name implies, it treats the stream contents as text.
    /// </summary>
    public string ReadTextFile(Stream inputStream)
    {
        using (var reaader = new System.IO.StreamReader(inputStream))
        {
            return reaader.ReadToEnd();
        }
    }
    </code>

    Another example of a method in the business logic project is one that actually saves the file. Instead of just calling “SaveAs(filepath)”, you need to copy the stream yourself. Usually there’s more than one way to do so, the problem in most of the ways is determining the size of the stream. An “HttpPostedFile” knows the size of the file, but as I’m not getting the “HttpPostedFile” (and it wouldn’t sound great if I requested the file size as a method parameter!), I dealt as if I do not know the actual file size. See the code for this method:

    <code>/// <summary>
    /// Writes the stream context that comes to it whether from web or whatever!
    ///     and saves it to the path set by property called "SavingPath"
    /// 
    /// I meant to not make the save path a method property because 
    ///     in real situation that value would come most likely from 
    ///         configuration, DB or some other code logic
    /// </summary>
    public void WriteBinaryFile(Stream inputStream)
    {
        using (var reader = new BinaryReader(inputStream))
        {
            int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];
            int count = 0;
            int offset = 0;
    
            using (var writingStream = 
                new System.IO.FileStream(this.SavingPath, System.IO.FileMode.OpenOrCreate))
            {
                while ((count = reader.Read(buffer, offset, buffer.Length)) > 0)
                {
                    writingStream.Write(buffer, offset, count);
                }
                writingStream.Flush();
                writingStream.Close();
            }
            reader.Close();
        }
    }
    </code>

    Example: A Webforms ASP.NET Project

    Before we dig into the unit test sample, let’s see how our page code will look when dealing with this business logic class.

    Consider a page with controls like these:

    <code> <%-- 
         Note: I used the FileUpload control of .NET 2.0 and above, 
             but I could also use something like:
         <input type="file" runat="server" id="inFileTest" />
     --%>
     <asp:FileUpload runat="server" ID="upldTestFile" />
    
    
     <%-- Used a text file read operation as a simple exsample --%>
     <asp:Button runat="server" ID="btnReadFile" Text="Read text file in the page" 
         onclick="btnReadFile_Click" />
    
     <%-- Note that even text files can be treated as binary files :) --%>
     <asp:Button runat="server" ID="btnWriteFile" Text="Write Copy of the file (any type)" 
         onclick="btnWriteFile_Click" />
    
     <br />
    
     <%-- I'll show the read happened by referencing to it --%>
     <asp:Literal runat="server" ID="litReadFileResult" />
    
     <%-- I'll show the file was written by linking to it. 
             In real world, you may want to encapsulate this in an HTTPHandler 
                 not expose the file itself --%>
     <asp:HyperLink runat="server" ID="lnkWriteFileResult" 
         Text="Click here to get the saved file" />
    </code>

    The code behind for such page with our custom logic class (in a sample manner still), would look like this:

    <code>/// <summary>
    /// Created for the "ReadTextFile" method
    /// </summary>
    protected void btnReadFile_Click(object sender, EventArgs e)
    {
        if (!upldTestFile.HasFile) { /* Call whatever error msg method and, */ return; }
    
        var fileLogic = new FileLogic();
        litReadFileResult.Text =
            "The text file contents are: "
                + fileLogic.ReadTextFile(upldTestFile.PostedFile.InputStream);
        litReadFileResult.Visible = true;
    }
    
    /// <summary>
    /// Created for the "WriteBinaryFile" method
    /// </summary>
    protected void btnWriteFile_Click(object sender, EventArgs e)
    {
        if (!upldTestFile.HasFile) { /* Call whatever error msg method and, */ return; }
    
        //Save the file to "sample.text" under the website root (referenced as "~/"
        string relativeSavePath = "~/" + upldTestFile.FileName;
    
        var fileLogic = new FileLogic();
    
        // Converting path to phyical then setting it in the business logic class
        //  The physical path typically does not yet exist, as we'll create new file to it
        fileLogic.SavingPath = Server.MapPath(relativeSavePath);
    
        fileLogic.WriteBinaryFile(upldTestFile.PostedFile.InputStream);
    
        lnkWriteFileResult.NavigateUrl = relativeSavePath;
        lnkWriteFileResult.Visible = true;
    }
    </code>

    I’d say that this part is not much different than it’d be if we were passing the “HttpPostedFile” object complete.

    The Unit Test

    So, that’s what we have been hassling, complicating code, and dealing directly with streams for its sake!

    As our application will not necessarily be a file sharing application, we’ll be in need for the file as part of a bigger operation. A unit test will not be only concerned with the file upload task, but also the rest parts of the business operation our method intends to do. However, in this sample, I’ve decided to make it just test the file uploading functionality, and included really simple ways in the unit test.

    Instead of the file upload, I used a physical file that I get its path from a method called “GetTestFilename” (I could have used configuration file, static property or else for the file stream, or used any other kind of stream still).

    See how the test methods look in this example (using NUnit):

    <code>/// <summary>
    /// Checks method "ReadTextFile" to see whether it gets the same text as sent file
    /// </summary>
    [Test]
    public void TestReadTextFile()
    {
        var fileLogic = new FileLogic();
        var sampleFileInfo = new System.IO.FileInfo(GetTestFilename());
        string correctResult = sampleFileInfo.OpenText().ReadToEnd();
        string testResult = fileLogic.ReadTextFile(sampleFileInfo.OpenRead());
    
        Assert.AreEqual(correctResult, testResult);
    }
    
    /// <summary>
    /// Checks whether method "WriteBinaryFile" creates the file
    /// </summary>
    [Test]
    public void TestWriteBinaryFile_FileExists()
    {
        var fileLogic = new FileLogic();
    
        string newFilename = System.IO.Path.GetTempFileName();
        fileLogic.SavingPath = newFilename;
    
        var sampleFileInfo = new System.IO.FileInfo(GetTestFilename());
        fileLogic.WriteBinaryFile(sampleFileInfo.OpenRead());
    
        Assert.IsTrue(System.IO.File.Exists(newFilename));
    }
    
    //There should be one more method for checking the file contents are the same
    // Skipped for sample!
    </code>

    Link List: jQuery, WF, WCF, OOP and Patterns, ASP.NET, Scrum,…

    This is also from an internal mail to SilverKey Tech Egypt office dev. team. Slightly modified this time! (Yes, I know. I should stop this habit of link collection and get back to writing – hopefully soon)

    hmm, how about more links as a blog readers bonus,..

    GridView DataBinding Events

    This is a well commented example for a GridView with implementation of RowDataBound and DataBound events. I also demonstrate in it some of the important properties of GridView, like the slight differences when dealing with Grid Paging, Rows, Cells, and Columns. I prefered to heavily comment the code than to write separate paragraphs describing it as I do believe that the code is what gets the idea in a direct way.

    The example is a simple page that uses SqlDataSource to connect to a SQL Server 2005 Express database and show the results on a GridView with paging capability.

    Disclaimer:

    Many parts of the code don’t show the best practices for their situation and those are only included for demonstration purpose as they are not the main focus of the example.

    Let’s start with the code behind of the page:

    As it’s the most important part

    /******************************************************************
     * The sample is provided AS IS without any warranty
     * All rights reserved (C) 2005, Mohamed Meligy
     * https://www.gurustop.net
     * Distribution of this code without this note is prohibited.
     ******************************************************************/
    //Default "using" set of a new System.Web.UI.Page Visual Studio 2005 template:
    using System;
    using System.Data;
    using System.Configuration;
    using System.Collections;
    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;
    /// <summary>
    /// To test the GridView behavior in action,
    /// you need to have SQL Server Express installed (comes by default with VS2005),
    /// and Northwind database attached to it
    /// (attaches by default when you select "Quickstart Tutorials"
    /// from the .NET Framework 2.0 SDK Programs menu, and choose to install them).
    /// Alternatively, change the ConnectionString property of the SqlDataSource
    /// to point to another installed instance
    /// of SqlServer 2000 or SqlServer 2005 that has Northwind database attached.
    /// </summary>
    public partial class GridViewSamplePage : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            //Limiting redundancy of useless execution.
            //You do no need to check IsCallback if you
            // don't have controls with Callback Enabled.
            if (!IsPostBack & !IsCallback)
            {
                //This makes performing paging and sorting uses AJAX.
                EmployeesGridView.EnableSortingAndPagingCallbacks = true;
                //Small number so that I can see many pages out of
                // 9 rows only (The total Rows Count of Northwind's Employees DataTable).
                //Also selected it in a way that last Page Rows Count will be
                // different than other pages.
                EmployeesGridView.PageSize = 2;
                if (!IsPostBack)   //Just to prove the AJAX thing.
                {
                    PostBackStatus.Text = "Not PostBack";
                }
                else   //Never happens, as I don't require a PostBack.
                {
                    PostBackStatus.Text = "Page Is PostBack";
                }
            }
        }
        /// <summary>
        /// Executes when the databinding is complete
        /// </summary>
        protected void EmployeesGridView_DataBound(object sender, EventArgs e)
        {
            //I'll use it to show which set of Rows I'm showing in the page.
            EmployeesGridView.ShowFooter = true;
            //Similar to: EmployeesGridView.FooterRow.Visible = true
            //Stretching the first cell to fill the whole FooterRow:
            //Removing all non needed cells,
            // from the end to start
            // (so that the Cells collection is not recreated with every Removal).
            for (int cellNum = EmployeesGridView.Columns.Count - 1; cellNum > 0; cellNum--)
            {
                EmployeesGridView.FooterRow.Cells.RemoveAt(cellNum);
            }
            EmployeesGridView.FooterRow.Cells[0].ColumnSpan = EmployeesGridView.Columns.Count;
            EmployeesGridView.FooterRow.Cells[0].HorizontalAlign = HorizontalAlign.Center;
            int startIndex = EmployeesGridView.PageIndex != EmployeesGridView.PageCount - 1 ?
            //Not in last page.
            EmployeesGridView.PageSize * EmployeesGridView.PageIndex
            //EmployeesGridView Rows Count is Count of the Rows of the current page, so,
                // unless Paging is Disabled, this is not total Rows Count.
                //gridViewTotalCount is declared and assigned at the end of this code.
            : gridViewTotalCount - EmployeesGridView.PageSize + EmployeesGridView.Rows.Count;
            EmployeesGridView.FooterRow.Cells[0].Text =
                string.Format
                    ("Showing Employees {0} to {1} of {2}",
                    startIndex + 1,
                    startIndex + EmployeesGridView.Rows.Count, gridViewTotalCount);
        }
        TableCell myCell = null;
        protected void EmployeesGridView_RowDataBound(object sender, GridViewRowEventArgs e)
        {
            //I'll add that to every Row bound in the GridView.
            myCell = new TableCell();
            //Note that for the header, footer, and separator rows, DataItemIndex is -1
            myCell.Text = e.Row.DataItemIndex.ToString();
            //Note that when you add the cell,
            // this does NOT increase the GridView Columns Count.
            e.Row.Cells.Add(myCell);
            //The past lines will affect also HeaderRow, FooterRow, and even Pager,
            // you'll see when you run that this is not a desired behavior.
            //You can limit your code on condition. This is the recommended way.
            if (e.Row.RowType == DataControlRowType.DataRow)
            {
                //Do your UI logic now for data binding,
                // much similar to DataGrid ItemDataBound.
                //Example: changing color of text
                // for normal and alternative rows ONLY by code.
                if (e.Row.RowIndex % 2 == 0)
                {
                    e.Row.Style["color"] = "#0000ff";
                }
                else
                {
                    e.Row.Style["color"] = "#00bb00";
                }
                //This is the EmployeeID Cell
                e.Row.Cells[0].Text =
                    "I'm alternative employee with code: " + e.Row.Cells[0].Text;
                //Another sample change making use of the DataItem:
                // This is specific to our case where the GridView is bound to
                // first DataTable in the DataSet returnd by the EmployeesSqlSource.
                // This is when SqlDataSource DataSourceMode = DataSet (Default)
                DataRowView rowView = (DataRowView)e.Row.DataItem;
                e.Row.Cells[EmployeesGridView.Columns.Count - 1].Text =
                    int.Parse(rowView["Extension"] as string).ToString("###-##");
            }
        }
        int gridViewTotalCount;
        protected void EmployeesSqlSource_Selected
            (object sender, SqlDataSourceStatusEventArgs e)
        {
            //GridView has no way of telling total Rows Count when paging is enabled.
            gridViewTotalCount = e.AffectedRows;
        }
    }

    Now to the page XHTML source:

    <%@ Page Language="C#" AutoEventWireup="true" 
        CodeBehind="GridViewSamplePage.aspx.cs"
     Inherits="GridViewSamplePage"
        %>
    <!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="PageHeader" runat="server">
        <title>GridView Sample</title>
    </head>
    <body>
        <form id="ServerForm" runat="server">
            <div>
            Postback Status:
                <asp:Label ID="PostBackStatus" runat="server"></asp:Label>
                <asp:GridView ID="EmployeesGridView" runat="server"
                    AutoGenerateColumns="False" AllowPaging="True"
                    DataSourceID="EmployeesSqlSource" DataKeyNames="EmployeeID"
                    OnDataBound="EmployeesGridView_DataBound"
                    OnRowDataBound="EmployeesGridView_RowDataBound">
                    <Columns>
                        <asp:BoundField DataField="EmployeeID"
                            HeaderText="EmployeeID" InsertVisible="False"
                            ReadOnly="True" SortExpression="EmployeeID" />
                        <asp:BoundField DataField="LastName"
                            HeaderText="LastName" SortExpression="LastName" />
                        <asp:BoundField DataField="FirstName"
                            HeaderText="FirstName" SortExpression="FirstName" />
                        <asp:BoundField DataField="Title"
                            HeaderText="Title" SortExpression="Title" />
                        <asp:BoundField DataField="BirthDate"
                            HeaderText="BirthDate" SortExpression="BirthDate" />
                        <asp:BoundField DataField="PostalCode"
                            HeaderText="PostalCode" SortExpression="PostalCode" />
                        <asp:BoundField DataField="Extension"
                            HeaderText="Extension" SortExpression="Extension" />
                    </Columns>
                    <EmptyDataTemplate>
                        <div style="text-align: center">
                            No Data Available.
                        </div>
                    </EmptyDataTemplate>
                </asp:GridView>
                <asp:SqlDataSource ID="EmployeesSqlSource" runat="server"
                    ConnectionString=
    "Data Source=MELIGYSQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True"
                    ProviderName="System.Data.SqlClient"
                    SelectCommand=
    "SELECT [EmployeeID], [LastName], [FirstName], [Title], [BirthDate], [PostalCode], [Extension] FROM [Employees]"
                    OnSelected="EmployeesSqlSource_Selected"></asp:SqlDataSource>
            </div>
        </form>
    </body>
    </html>

    Samples of the output of the code:

    Hope that you like it.