Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

ASP.NET GridView Sort Indicator Component

4.43/5 (13 votes)
6 Aug 2007Apache5 min read 1   628  
This article describes a component that displays a sort indicator in the header of a column of a GridView by which the data is sorted.

Class Diagram

Introduction

This article presents a component that is used to add a sort indicator image to GridView columns.

The Internet provides a lot of sites that show how you can add some sort of sort indicator to a GridView, but all of them do this for a single GridView. Our application has quite a lot of GridViews that need this feature, and therefore I decided that a component would be the right way to go. A component has the advantage that I can code the behaviour just once and not for every single GridView over and over again.

The rest of this article shows you how you can use the GridViewSortExtender (that's the name of the component) and how it works on the inside.

Just use it

To use the GridViewSortExtender, simply put one on your pages along with the GridView you want to extend:

Designer

Then, set the properties for the images, and specify which GridView should be extended on the Property pane:

Designer Properties

That's it! Run the application. It will look something like this:

Browser

On the inside

The GirdViewSortExtender is quite a simple component, but there are still some interesting things about it that I'll discuss in this section.

How to add the indicator to the sort column

I use the OnPreRender method of the GridViewSortExtender to modify the extended GridView. This works in my projects because I normally do not modify the page content in the OnPreRender method of the page anymore. Note: the OnPreRender on the page is called after the OnPreRender method of the contained components.

Note: The image is always added because otherwise it will be lost on postbacks that do not bind the GridView (see the message section below for more information).

C#
public class GridViewSortExtender : Control
{
    /// ... other code ...

    /// <summary>
    /// Adds an event handler to the DataBound event of the extended GridView.
    /// </summary>
    /// <param name="e"></param>

    protected override void OnPreRender(EventArgs e)
    {
        base.OnInit(e);

        GridView extendee = 
            this.NamingContainer.FindControl(this.ExtendeeID) as GridView;
        if (extendee != null && 
            extendee.AllowSorting && 
            extendee.HeaderRow != null && 
            !String.IsNullOrEmpty(extendee.SortExpression))
        {
            int field = GetSortField(extendee);
            if (field >= 0)
            {
                Image img = new Image();
                img.ImageUrl = 
                    extendee.SortDirection == SortDirection.Ascending ? 
                        this.AscendingImageUrl : this.DescendingImageUrl;
                img.ImageAlign = ImageAlign.TextTop;

                extendee.HeaderRow.Cells[field].Controls.Add(img);
            }
        }
    }

    /// <summary>
    /// Returns the index of the sort-column.
    /// </summary>
    /// <param name="extendee"></param>
    /// <returns></returns>

    private int GetSortField(GridView extendee)
    {
        int i = 0;
        foreach (DataControlField field in extendee.Columns)
        {
            if (field.SortExpression == extendee.SortExpression)
            {
                return i;
            }
            i++;
        }
        return -1;
    }
}

The first thing is to get the extended GridView. This is accomplished by calling the FindControl method on the NamingContainer of the GridViewSortExtender. The NamingContainer is the container where the GridViewSortExtender is defined in. Therefore, the GridViewSortExtender has to be in the same container as the GridView it extends.

If the corresponding GridView is found, then it is checked whether this GridView allows sorting and has a header row - only then is it reasonable to add the sort indicator.

Then, depending on the SortDirection of the extended GridView, an image is instantiated.

The tricky part is how to find the correct column where the image is added. I loop through all columns of the extended GridView, and check its SortExpression against the SortExpression of the GridView - if it's the same, then I have found the correct column.

Finally, I add the image to the header cell of the column.

A drawback of using the SortExpression is that the GridViewSortExtender works only on BoundField columns. This is especially a problem when you have a GridView with dynamically generated columns.

A side note: normally, I override the event invoker methods (like OnPreRender) and don't register events (like PreRender), because I don't like it that an instance registers events of itself. I consider this bad OO design :-)

How to make it designer ready

I use this component a lot of times in my applications, and therefore I wanted a component that has a nice behaviour in the designer.

Reference images

The GridViewSortExtender has two references to images. I use the Editor attribute to get a nice selection dialog in the designer. The URL of the images is then stored within the ViewState.

C#
/// <summary>
/// Image that is displayed when SortDirection is ascending.
/// </summary>

[DefaultValue("")]
[Editor(typeof(ImageUrlEditor), typeof(UITypeEditor))]
[Description("Image that is displayed when SortDirection is ascending.")]
public string AscendingImageUrl
{
    get { return this.ViewState["AscendingImageUrl"] != null ?
              (string)this.ViewState["AscendingImageUrl"] : ""; }
    set { this.ViewState["AscendingImageUrl"] = value; }
}

Reference a GridView

To get a similar behaviour for the selection of the GridView to extend, I choose the IDReferenceProperty on the ExtendeeID property. Unfortunately, the result was not stable: sometimes the designer provided me all the GridViews on the page in the selection list in the property list, sometimes not. Therefore, I added the TypeConverter attribute and a custom TypeConverter (GridViewIDConverter). Now, the designer provides a selection of all DataGrids on the page reliably.

C#
/// <summary>
/// The GridView that is extended.
/// </summary>

[DefaultValue("")]
[IDReferenceProperty(typeof(GridView))]
[TypeConverter(typeof(GridViewIDConverter))]
[Description("The GridView that is extended.")]
public string ExtendeeID
{
    get { return this.ViewState["Extendee"] != null ?
              (string)this.ViewState["Extendee"] : ""; }
    set { this.ViewState["Extendee"] = value; }
}

The GridViewIDConverter is derived from the generic ControlIDConverter that does most of the work for us. The only thing we still have to do is to define which controls should be visible in the list that the designer provides to the programmer. In this case, I simply define that the control has to be a GridView.

C#
public class GridViewIDConverter : ControlIDConverter
{
    // Methods

    protected override bool FilterControl(Control control)
    {
        return control is GridView;
    }
}

Display the GirdViewSortExtender in the designer

I added a custom control designer to get a nice look of the component in design mode. The Designer attribute tells that I want to use the GridViewSortExtenderDesigner as a designer for my component. GridViewSortExtenderDesigner is derived from CodeDesigner, and overrides the GetDesignTimeHtml method to return the HTML that is used to display the component in design mode.

Note: You can see how it looks in the image further above.

C#
[Designer(typeof(GridViewSortExtenderDesigner))]
public class GridViewSortExtender : Control
{
    ... some code ...
}
    
/// <summary>
/// Designer for <see cref="GridViewSortExtender"/>.
/// </summary>

public class GridViewSortExtenderDesigner : ControlDesigner
{
    public override string GetDesignTimeHtml()
    {
        return 
            "<div style=\"background-color: #C8C8C8; " + 
            "border: groove 2 Gray;\"><b>GridViewSortExtender</b> - " + 
            this.Component.Site.Name + "</div>";
    }
}

Possible Extensions

  • Add spacer image when column is not sorted (prevent column widths from changing).

Conclusion

The GridViewSortExtenderDesigner is a simple component to add a sort indicator to a GridView, and has a nice design time behaviour to ease the usage in a project with several sortable GridViews.

Source of the Code

The sample code is extracted from a web application that I wrote for bbv Software Services AG.

Thanks for letting me write about it.

History

  • 2007-07-23 - Initial version.
  • 2007-08-06 - Uses DataBound event on GridView. Thanks to Michael Tucker for his idea.
  • 2007-08-07 - Changed back to PreRender approach because otherwise the sort indicator is lost on postbacks that do not bind the GridView. Thanks to Kelly Herald for the feedback.

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0