asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search





Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Building an Enhanced ASP.NET GridView Control
By Rachael Schoenbaum
Rating: 3.5 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article



  • download source code
  • The GridView is a great control that allows you to display paginated results. But what happens if you want to change the underlying display for all of your projects? This article shows you how to extend the GridView so that it displays a summary row and allows pagination and sorting in that summary row.

    The Premise

    The original request was to create a result set that looked like this:

    Enhanced Grid View

    At first blush, a GridView or DataGrid was going to provide 95% of the functionality. The only thing it wouldn't do was create the summary row in between the pagination display. I decided to use the GridView as my starting point and build in the summary row from there.

    Creating the Control

    The first step is to create the control. I created a Web Control Library project and changed the name of the default class to EnhancedGridView.cs. I changed the default class declaration to:

    public class EnhancedGridView : GridView

    which indicates that the control should inherit its functionality from the GridView object.

    Providing Stylesheet Options

    The first thing to do was to provide CssClass options for the various components so that they could be styled outside of the control. To do that, the following properties were created, with values stored in the ViewState so that the information is maintained across post backs if the values are set programmatically:

    public string CustomPagerCssClass
    {
        get { return (ViewState["CustomPagerCssClass"] == null) ? String.Empty : (string)ViewState["CustomPagerCssClass"]; }
        set { ViewState["CustomPagerCssClass"] = value; }
    }

    public string CustomHeaderCssClass
    {
        get { return (ViewState["CustomHeaderCssClass"] == null) ? String.Empty : (string)ViewState["CustomHeaderCssClass"]; }
        set { ViewState["CustomHeaderCssClass"] = value; }
    }

    public string CustomShowingCssClass
    {
        get { return (ViewState["CustomShowingCssClass"] == null) ? String.Empty : (string)ViewState["CustomShowingCssClass"]; }
        set { ViewState["CustomShowingCssClass"] = value; }
    }

    public string CustomResultsPerPageCssClass
    {
        get { return (ViewState["CustomResultsPerPageCssClass"] == null) ? String.Empty : (string)ViewState["CustomResultsPerPageCssClass"]; }
        set { ViewState["CustomResultsPerPageCssClass"] = value; }
    }

    public string CustomSortingCssClass
    {
        get { return (ViewState["CustomSortingCssClass"] == null) ? String.Empty : (string)ViewState["CustomSortingCssClass"]; }
        set { ViewState["CustomSortingCssClass"] = value; }
    }

    Creating DropDownLists and Maintaining State

    Because of the .NET page life cycle, any controls that need to maintain state must be created on every page load (that includes postbacks!) and must be created prior to ViewState being loaded. Therefore we overload the OnItit function with the DropDownLists for the sorting and results per page.

    private string _DefaultSortExpression = string.Empty;
    private DropDownList ddlSort = new DropDownList();
    private DropDownList ddlNumberOfPages = new DropDownList();

    public string DefaultSortExpression
    {
        get { return _DefaultSortExpression; }
    }

    protected override void OnInit(EventArgs e)
    {
        ddlNumberOfPages.AutoPostBack = true;
        ddlSort.AutoPostBack = true;

        ddlSort.SelectedIndexChanged += new EventHandler(ddlSort_SelectedIndexChanged);
        ddlNumberOfPages.SelectedIndexChanged +=new EventHandler(ddlNumberOfPages_SelectedIndexChanged);

        ddlNumberOfPages.ID = "ddlNumberOfPages";
        ddlNumberOfPages.Items.Add("10");
        ddlNumberOfPages.Items.Add("20");
        ddlNumberOfPages.Items.Add("50");
        ddlNumberOfPages.Items.Add("100");

        this.PageSize = Convert.ToInt32(ddlNumberOfPages.SelectedItem.Value);

        ddlSort.ID = "ddlSort";
        if (ddlSort.Items.Count == 0 && this.AllowSorting)
        {
            for (int i = 0; i < base.Columns.Count; i++)
            {
                if (base.Columns[i].SortExpression != string.Empty && ddlSort.Items.FindByValue(base.Columns[i].SortExpression) == null)
                {
                    ddlSort.Items.Add(new ListItem(base.Columns[i].HeaderText, base.Columns[i].SortExpression));
                }
            }
        }

        if (ddlSort.Items.Count > 0)
        {
            _DefaultSortExpression = ddlSort.Items[0].Text;
        }

        base.OnInit(e);
    }

    Handling the DropDownList PostBack Events

    Next we work with the postback events for the DropDownLists. The sort functionality is pretty straightforward:

    protected void ddlSort_SelectedIndexChanged(object sender, System.EventArgs e)
    {
        DropDownList ddlSort = (DropDownList)sender;
        base.Sort(ddlSort.SelectedValue, SortDirection.Ascending);
    }

    More difficult is changing the results per page, which works like the OnPageIndexChanging event. We check first to see if the event is handled somewhere else. If so, we raise the event using the number of pages selected from the dropdown. If the event isn't handled elsewhere, we manually change the page size and rebind the data.

    public event PageSizeChangeHandler PageSizeChanging;
    public delegate void PageSizeChangeHandler(object s, PageSizeChangeEventArgs e);

    protected void ddlNumberOfPages_SelectedIndexChanged(object sender, System.EventArgs e)
    {
        if (this.PageSizeChanging != null)
        {
            int iPageSize = Convert.ToInt32(ddlNumberOfPages.SelectedItem.Value);
            PageIndex = 0;
            this.PageSizeChanging(this, new PageSizeChangeEventArgs(iPageSize));
        }
        else
        {
            DropDownList ddlNumberOfPages = (DropDownList)sender;
            this.PageIndex = 0;
            this.PageSize = int.Parse(ddlNumberOfPages.SelectedValue);
            this.DataBind();
        }
    }

    The first two lines define the event and the event handler. The method defines what happens after the event is raised. The last thing we need is a definition for the PageSizeChangeEventArgs class, which should be defined outside of the EnhancedGridView class definition.

    public class PageSizeChangeEventArgs : System.EventArgs
    {
        public int NewPageSize;

        public PageSizeChangeEventArgs(int pNewPageSize)
        {
            NewPageSize = pNewPageSize;
        }
    }

    Displaying the Summary Row

    To display the summary row, we have to override the OnRowCreated and the PerformDataBinding methods. The PerformDataBinding method allows us to access the total row count for the data source provided. The OnRowCreated replaces the existing header with a summary of the results, including the DropDownLists for results per page and sorting. Note how we make sure if there are any custom CSS classes defined, we add them.

    private int intTotal = 0;

    protected override void PerformDataBinding(IEnumerable data)
    {
        if (data is IListSource)
        {
            IListSource oList = (IListSource)data;
            intTotal = oList.GetList().Count;
        }
        else if (data is ICollection)
        {
            ICollection oCollection = (ICollection)data;
            intTotal = oCollection.Count;
        }

        base.PerformDataBinding(data);
    }


    protected override void OnRowCreated(GridViewRowEventArgs e)
    {
        if (e.Row.RowType == DataControlRowType.Header)
        {
            int intColumnCount = e.Row.Cells.Count;

            e.Row.Cells.Clear();

            Panel oPnlInner;
            Literal oLit;

            Panel oPnl = new Panel();
            if (this.CustomHeaderCssClass != string.Empty) { oPnl.CssClass = this.CustomHeaderCssClass; }

            // Showing X-Y of Z
            int iUpto = 0;
            int iStart = PageIndex * PageSize + 1;

            if ((PageIndex + 1) == PageCount)
                iUpto = intTotal;
            else
                iUpto = iStart + PageSize - 1;

            oPnlInner = new Panel();
            if (CustomShowingCssClass != String.Empty) { oPnlInner.CssClass = CustomShowingCssClass; }

            oLit = new Literal();
            oLit.Text = string.Format("Showing {0}-{1} of {2}", iStart, iUpto, intTotal);
            oPnlInner.Controls.Add(oLit);
            oPnl.Controls.Add(oPnlInner);

            // Results per page
            oPnlInner = new Panel();
            if (CustomResultsPerPageCssClass != String.Empty) { oPnlInner.CssClass = CustomResultsPerPageCssClass; }

            oLit = new Literal();
            oLit.Text = "Results per page: ";
            oPnlInner.Controls.Add(oLit);
            oPnlInner.Controls.Add(ddlNumberOfPages);
            oPnl.Controls.Add(oPnlInner);

            // Sorting
            if (this.AllowSorting == true)
            {
                oPnlInner = new Panel();
                if (CustomSortingCssClass != String.Empty) { oPnlInner.CssClass = CustomSortingCssClass; }

                oLit = new Literal();
                oLit.Text = "Sort: ";
                oPnlInner.Controls.Add(oLit);
                oPnlInner.Controls.Add(ddlSort);
                oPnl.Controls.Add(oPnlInner);
            }

            // Add div to clear out styles
            oPnlInner = new Panel();
            oPnlInner.Style.Add("clear", "both");
            oPnl.Controls.Add(oPnlInner);

            e.Row.Cells.Add(new TableCell());
            e.Row.Cells[0].ColumnSpan = intColumnCount;
            e.Row.Cells[0].Controls.Add(oPnl);
        }
        else if (e.Row.RowType == DataControlRowType.Pager)
        {
            Panel oPnl = new Panel();
            if (CustomPagerCssClass != String.Empty) { oPnl.CssClass = CustomPagerCssClass; }

            Table oTab = (Table)e.Row.Cells[0].Controls[0];
            oTab.Rows[0].Cells.AddAt(0, new TableCell());
            oTab.Rows[0].Cells[0].Text = "<strong>Page:</strong>";

            oPnl.Controls.Add(oTab);

            e.Row.Cells[0].Controls.Clear();
            e.Row.Cells[0].Controls.Add(oPnl);
        }
    }

    Column Headers

    To display the column headers, we override the OnRowDataBound method to create a brand new row with the column headers. We can use the e.Row.Parent property to access the table that contains the output for the GridView. The logic for adding the row is required because the index of the row where we want the column headers to appear is different if the PagerSettings.Position is PagerPosition.Top or PagerPosition.TopAndBottom.

    To maintain similar functionality as the GridView, we also provide a ShowColumnHeader property and store the value in ViewState so that it is maintained correctly across post backs. This property allows the ASPX/codebehind control over whether the column row appears since the ShowHeader property now controls whether the summary row appears.

    Lastly, we check the UseAccessibleHeader property to determine whether to use a td or a th in the HTML markup for the table.

    public bool ShowColumnHeader
    {
        get { return (ViewState["ShowColumnHeader"] == null) ? true : (bool)ViewState["ShowColumnHeader"]; }
        set { ViewState["ShowColumnHeader"] = value; }
    }

    protected override void OnRowDataBound(GridViewRowEventArgs e)
    {
        if (e.Row.RowType == DataControlRowType.Header)
        {
            if (ShowColumnHeader && Columns.Count > 0)
            {
                Table tbl = (Table)e.Row.Parent;
                GridViewRow row = this.CreateRow(-1, -1, DataControlRowType.Header, DataControlRowState.Normal);
                for (int i = 0; i < this.Columns.Count; i++)
                {
                    if (UseAccessibleHeader)
                    {
                        row.Cells.Add(new TableHeaderCell());
                    }
                    else
                    {
                        row.Cells.Add(new TableCell());
                    }
                    row.Cells[i].Text = this.Columns[i].HeaderText;
                }
                tbl.Rows.AddAt((PagerSettings.Position != PagerPosition.Bottom) ? 2 : 1, row);
            }
        }

        base.OnRowDataBound(e);
    }

    Using the EnhancedGridView in Your Web Project

    Now that the control is complete, you can compile it and add it as a reference in your Web project. You'll have to reference the DLL in your ASPX page in order to use it.

    Here's an example of how to use the EnhancedGridView. Note that you have to put this control inside a form with runat="server" so that the dropdownlists will work.

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="default.aspx.cs" Inherits="UI.clsDefault" Title="Home Page" %>
    <%@ Register TagPrefix="cc" Assembly="CustomControls" Namespace=" CustomControls" %>

    <html>
    <body>

    <form runat="server">

    <cc:EnhancedGridView ID="gvResults" runat="server"
        GridLines="None"
        AutoGenerateColumns="false"
        AllowPaging="true" PagerSettings-Mode="Numeric"
        PagerSettings-Position="TopAndBottom"
        AllowSorting="true" AlternatingRowStyle-CssClass="alt"
        DataSourceID="sqlSelect"
        CssClass="dotnet_table"
        HeaderStyle-CssClass="dotnet_columnhead"
        CustomPagerCssClass="dotnet_pages"
        CustomHeaderCssClass="dotnet_head"
        CustomShowingCssClass="dotnet_showing"
        CustomSortingCssClass="dotnet_sorting"
        CustomResultsPerPageCssClass="dotnet_results"
    >
        <Columns>
            <asp:BoundField DataField="Column1" HeaderText="Column1" SortExpression="Column1" />
            <asp:BoundField DataField="Column2" HeaderText="Column2" SortExpression="Column2" />
        </Columns>
    </cc:EnhancedGridView>

    <asp:SqlDataSource ID="sqlSelect" runat="server"
        ConnectionString="<%$ ConnectionStrings:Web %>"
        DataSourceMode="DataSet"
        SelectCommand="SELECT [Columns] FROM [Table]"
    />

    </form>

    </body>
    </html>

    Opportunities for Improvement

    One way to improve this control would be to allow the values for the results per page DropDownList to be supplied in the ASPX page or code behind instead of being hardcoded into the control.

    Another opportunity for improvement lies in determining what columns are available for sorting. The control relies on the columns being defined. If AutoGenerateColumns="true" the sorting dropdown isn't populated correctly.

    Conclusion

    The GridView is not only a great control for displaying data but is easily enhanced to provide custom interfaces based on your needs.

    About The Author

    Rachael Schoenbaum has been programming since 1999 and specializes in ASP, .NET, Visual Basic, SQL Server, XML, and related technologies. She currently works as a consultant for Lucidea. If you have questions or comments about this article, email Rachael at rachael_schoenbaum(at)yahoo.com.

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Other Articles
    Jul 21, 2005 - N-Tier Web Applications using ASP.NET 2.0 and SQL Server 2005 - Part 1
    While the .NET Framework made building ASP.NET applications easier then it had ever been in the past, .NET 2.0 builds on that foundation in order to take things to the next level. This article shows you to how to construct an N-Tier ASP.NET 2.0 Web application by leveraging the new features of ASP.NET 2.0 and SQL Server 2005.
    [Read This Article]  [Top]
    Apr 28, 2005 - New Files and Folders in ASP.NET 2.0
    With the release of ASP.NET 2.0, Microsoft has greatly increased the power of ASP.NET by introducing a suite of new features and functionalities. As part of this release, ASP.NET 2.0 also comes with a host of new special files and folders that are meant to be used to implement a specific functionality. This article examines these new files and folders in detail and provides examples that demonstrate how to utilize them to create ASP.NET 2.0 applications.
    [Read This Article]  [Top]
    Mar 10, 2005 - The DataSet Grows Up in ADO.NET 2.0 - Part 2, Cont'd
    Alex Homer continues his detailed look at the major changes to the DataSet class. In this part, he looks at two features that allow developers to work with data in a more structured and efficient way when using the DataSet with a SQL Server 2005 database server.
    [Read This Article]  [Top]
    Mar 9, 2005 - The DataSet Grows Up in ADO.NET 2.0 - Part 2
    Alex Homer continues his detailed look at the major changes to the DataSet class. In this part, he looks at two features that allow developers to work with data in a more structured and efficient way when using the DataSet with a SQL Server 2005 database server.
    [Read This Article]  [Top]
    Mar 3, 2005 - The DataSet Grows Up in ADO.NET 2.0 - Part 1, Cont'd
    In this article, Alex Homer looks at the changes between the version 1.x and version 2.0 DataSet and their associated classes, showing you how you can take advantage of the new features to improve your applications' capabilities and performance.
    [Read This Article]  [Top]
    Mar 2, 2005 - The DataSet Grows Up in ADO.NET 2.0 - Part 1
    In this article, Alex Homer looks at the changes between the version 1.x and version 2.0 DataSet and their associated classes, showing you how you can take advantage of the new features to improve your applications' capabilities and performance.
    [Read This Article]  [Top]
    Feb 16, 2005 - Writing a Custom Membership Provider for the Login Control in ASP.NET 2.0
    In ASP.NET 2.0 and Visual Studio 2005, you can quickly program custom authentication pages with the provided Membership Login controls. In this article, Dina Fleet Berry examines the steps involved in using the Login control with a custom SQL Server membership database.
    [Read This Article]  [Top]
    Dec 29, 2004 - ClickOnce Deployment in .NET Framework 2.0
    In this article, Thiru Thangarathinam examines .NET 2.0's new ClickOnce deployment technology that is designed to ease deployment of Windows forms applications. This new technology not only provides an easy application installation mechanism, it also eases deployment of upgrades to existing applications.
    [Read This Article]  [Top]
    Dec 15, 2004 - A Sneak Peek at ASP.NET 2.0's Administrative Tools
    With ASP.NET 2.0, Microsoft has made great strides in increasing developer productivity and has made implementing previously complex solutions relatively easy. Where this version of ASP.NET really shines, however, is in its new administrative tools that allow developers to spend less time managing the configuration of the servers and software and more time developing great code.
    [Read This Article]  [Top]
    Nov 17, 2004 - The ASP.NET 2.0 TreeView Control
    Thiru Thangarathinam introduces ASP.NET 2.0's new TreeView control which provides a seamless way to consume and display information from hierarchical data sources. The article discusses this new control in depth and explains how to use this feature rich control in your ASP.NET applications.
    [Read This Article]  [Top]
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry

    internet.commediabistro.comJusttechjobs.comGraphics.com

    Search:

    WebMediaBrands Corporate Info

    Legal Notices, Licensing, Reprints, Permissions, Privacy Policy.
    Advertise | Newsletters | Shopping | E-mail Offers | Freelance Jobs