October 21, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Dynamic Template Columns in the ASP.NET 2.0 GridView Control

  • May 31, 2006
  • By Mike Gunderloy
  • Send Email »
  • More Articles »

One of the nice things about ASP.NET is its depth: the sheer number of tools and techniques built into this Web application framework can be quite staggering. Recently I was involved in a project where we needed to present the results of a database query as part of an ASP.NET application, but we needed extensive control over the on-screen formatting, down to the level of controls used to present individual columns of data, CSS classes used, and more. To make matters even trickier, we didn't know until runtime what the query would be. After some discussion and experimentation among the design team, though, we decided that there was no need to buy a third-party control to handle these demands. The built-in GridView could handle all of our requirements. The key lay in understanding and using the little-known ability to add columns to the GridView dynamically using templates at runtime.

A GridView template is a class that implements the ITemplate interface. It defines the controls that will be displayed on the GridView in a column, how they will bind to data, and can have special-case code to handle headers and footers. In this article I'll show you a simplified example of building a GridView up from scratch using a template to respond to a dynamic query; the technique can be extended to cover much more complex situations.

The Template Class

Let's start with the Template class itself. This is the class that holds the code that will do the actual heavy lifting of putting controls in the DataGrid, as well as formatting them and binding them to data. It starts off with some private member variables and a constructor to set them:


// dynamically added label column
public class GridViewLabelTemplate : ITemplate
{

    private DataControlRowType templateType;
    private string columnName;
    private string dataType;

    public GridViewLabelTemplate(DataControlRowType type, 
        string colname, string DataType)
    {
        templateType = type;
        columnName = colname;
        dataType = DataType;
    }

The next block of code gets called whenever an instance of this template is instantiated. If you think of a template as corresponding to a column in the GridView, this happens every time a header, cell, or footer of the GridView is created for that column. You can inspect the templateType member to figure out which of these is the case. Here, you want to create whatever control or controls you need to display the data. You're not limited to a single control, though for this article I'm only using one label for display. You can also do whatever you need to format the control to your liking. I'm going to grab the container for the control (which ends up being the wrapping table cell) and set its CSS style so that I can right-justify numeric columns. This method also sets up for data-binding by registering an event handler.


public void InstantiateIn(System.Web.UI.Control container)
{
    DataControlFieldCell hc = null;

    switch (templateType)
    {
        case DataControlRowType.Header:
            // build the header for this column
            Literal lc = new Literal();
            lc.Text = "<b>" + BreakCamelCase(columnName) + "</b>"; 
            container.Controls.Add(lc);
            break;
        case DataControlRowType.DataRow:
            // build one row in this column
            Label l = new Label();
            switch (dataType)
            {
                case "DateTime":
                    l.CssClass = "ReportNoWrap";
                    break;
                case "Double":
                    hc = (DataControlFieldCell)container;
                    hc.CssClass = l.CssClass = "ReportNoWrapRightJustify";
                    break;
                case "Int16":
                case "Int32":
                    hc = (DataControlFieldCell)container;
                    hc.CssClass = l.CssClass = "ReportNoWrapRightJustify";
                    break;
                case "String":
                    l.CssClass = "ReportNoWrap";
                    break;
            }
            // register an event handler to perform the data binding
            l.DataBinding += new EventHandler(this.l_DataBinding);
            container.Controls.Add(l);
            break;
        default:
            break;
    }
}

As you'd expect, the event handler you set up for databinding gets called when data is bound to the GridView. In this case, I'm going to use this event handler to do some formatting of the bound data:


private void l_DataBinding(Object sender, EventArgs e)
{
    // get the control that raised this event
    Label l = (Label)sender;
    // get the containing row
    GridViewRow row = (GridViewRow)l.NamingContainer;
    // get the raw data value and make it pretty
    string RawValue = 
        DataBinder.Eval(row.DataItem, columnName).ToString();
    switch (dataType)
    {
        case "DateTime":
            l.Text = String.Format("{0:d}", DateTime.Parse(RawValue));
            break;
        case "Double":
            l.Text = String.Format("{0:###,###,##0.00}", 
                Double.Parse(RawValue));
            break;
        case "Int16":
        case "Int32":
            l.Text = RawValue;
            break;
        case "String":
            l.Text = RawValue;
            break;
    }
}

The last thing in my template class is a little helper method that's used in displaying column headers. Here I'm making an assumption about naming conventions in my database - that column names are all CamelCase, and that I'd prefer to display these on the GridView interface as individual words broken at the obvious points.


// helper method to convert CamelCaseString to Camel Case String
// by inserting spaces
private string BreakCamelCase(string CamelString)
{
    string output = string.Empty;
    bool SpaceAdded = true;

    for (int i = 0; i < CamelString.Length; i++)
    {
        if (CamelString.Substring(i, 1) == 
            CamelString.Substring(i, 1).ToLower())
        {
            output += CamelString.Substring(i, 1);
            SpaceAdded = false;
        }
        else
        {
            if (!SpaceAdded)
            {
                output += " ";
                output += CamelString.Substring(i, 1);
                SpaceAdded = true;
            }
            else
                output += CamelString.Substring(i, 1);
        }
    }

    return output;
}




Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel