How To: Show Header and Footer Rows in an Empty GridView

If you have worked with the ASP.NET 2.0 GridView control, I am sure you were more than a little surprised when you realized there is no decent way to display the Header or Footer rows of a GridView that is bound to an empty DataSource.  I know I was.  It seems like a bug to me and I am surprised it made it through usability testing.  Anyway, there are basically 2 ways to work around this problem:

  1. Make your DataSource's return a special empty row so the grid has something to bind to (see example of this here)
  2. Subclass GridView and add this functionality yourself

I chose to go with option 2 because because we had already subclassed the GridView for other reasons.  I cheated a little bit and started with the code snippet on the following page.  The GridView control is very complicated and I am not going to claim I understand it entirely, but from what I could tell this starting code segment contains the following bugs:

  • It doesn't work when you have AutoGenerateColumns set to true
  • It doesn't respect the EmptyDataTemplate (it only accounts for the EmptyDataText)
  • It doesn't render the Footer row

So I fixed these bugs and figured I would pass on the resulting code.

You can view a live demo of this in action here, and below is a screen shot of a page using this feature along with all of the code.  Enjoy!

Screen Shot:

Markup:

<mb:GridView 
    runat="server" DataSourceID="sqldsCustomers" 
    ShowHeader="true" ShowHeaderWhenEmpty="true" 
    EmptyDataText="No Customers were found." Width="95%" 
/>

Code:

/// <summary>
/// 
/// </summary>
/// <param name="dataSource"></param>
/// <param name="dataBinding"></param>
/// <returns></returns>
protected override int CreateChildControls(System.Collections.IEnumerable dataSource, bool dataBinding)
{
    int rows = base.CreateChildControls(dataSource, dataBinding);
    
    //  no data rows created, create empty table if enabled
    if (rows == 0 && (this.ShowFooterWhenEmpty || this.ShowHeaderWhenEmpty))
    {
        //  create the table
        Table table = this.CreateChildTable();

        DataControlField[] fields;
        if (this.AutoGenerateColumns)
        {
            PagedDataSource source = new PagedDataSource();
            source.DataSource = dataSource;

            System.Collections.ICollection autoGeneratedColumns = this.CreateColumns(source, true);
            fields = new DataControlField[autoGeneratedColumns.Count];
            autoGeneratedColumns.CopyTo(fields, 0);
        }
        else
        {
            fields = new DataControlField[this.Columns.Count];
            this.Columns.CopyTo(fields, 0);
        }

        if (this.ShowHeaderWhenEmpty)
        {
            //  create a new header row
            GridViewRow headerRow = base.CreateRow(-1, -1, DataControlRowType.Header, DataControlRowState.Normal);
            this.InitializeRow(headerRow, fields);

            //  add the header row to the table
            table.Rows.Add(headerRow);
        }

        //  create the empty row
        GridViewRow emptyRow = new GridViewRow(-1, -1, DataControlRowType.EmptyDataRow, DataControlRowState.Normal);
        TableCell cell = new TableCell();
        cell.ColumnSpan = fields.Length;
        cell.Width = Unit.Percentage(100);

        //  respect the precedence order if both EmptyDataTemplate
        //  and EmptyDataText are both supplied ...
        if (this.EmptyDataTemplate != null)
        {
            this.EmptyDataTemplate.InstantiateIn(cell);
        }
        else if (!string.IsNullOrEmpty(this.EmptyDataText))
        {
            cell.Controls.Add(new LiteralControl(EmptyDataText));
        }

        emptyRow.Cells.Add(cell);
        table.Rows.Add(emptyRow);

        if (this.ShowFooterWhenEmpty)
        {
            //  create footer row
            GridViewRow footerRow = base.CreateRow(-1, -1, DataControlRowType.Footer, DataControlRowState.Normal);
            this.InitializeRow(footerRow, fields);

            //  add the footer to the table
            table.Rows.Add(footerRow);
        }

        this.Controls.Clear();
        this.Controls.Add(table);
    }

    return rows;
}

[Category("Behavior")]
[Themeable(true)]
[Bindable(BindableSupport.No)]
public bool ShowHeaderWhenEmpty
{
    get
    {
        if (this.ViewState["ShowHeaderWhenEmpty"] == null)
        {
            this.ViewState["ShowHeaderWhenEmpty"] = false;
        }

        return (bool)this.ViewState["ShowHeaderWhenEmpty"];
    }
    set
    {
        this.ViewState["ShowHeaderWhenEmpty"] = value;
    }
}

[Category("Behavior")]
[Themeable(true)]
[Bindable(BindableSupport.No)]
public bool ShowFooterWhenEmpty
{
    get
    {
        if (this.ViewState["ShowFooterWhenEmpty"] == null)
        {
            this.ViewState["ShowFooterWhenEmpty"] = false;
        }

        return (bool)this.ViewState["ShowFooterWhenEmpty"];
    }
    set
    {
        this.ViewState["ShowFooterWhenEmpty"] = value;
    }
}

TrackBack

TrackBack URL for this entry:
http://mattberseth.com/blog-mt/mt-tb.fcgi/44

Listed below are links to weblogs that reference How To: Show Header and Footer Rows in an Empty GridView:

» How To: Show Header and Footer Rows in an Empty GridView from DotNetKicks.com
You've been kicked (a good thing) - Trackback from DotNetKicks.com [Read More]

Comments


Posted by: Tom on July 16, 2007 12:00 AM

Work like a charm! Thank you!

Posted by: Dee Browning on July 18, 2007 12:00 AM

This is awesome! You have saved me literally hours of work. Much thanks!

Posted by: Ken Cherasaro on August 8, 2007 12:00 AM

Anyway to get a binary of this class along with the appropriate "registration" line for the .aspx?

Posted by: Mike on August 31, 2007 12:00 AM

Thanks for a great post, Matt. I see your markup for putting the control on the page, but Im unsure how you registered the control, or are able to reference the class from your .aspx page? Thanks again.

Posted by: Anders on September 16, 2007 12:00 AM

The only problem is that the FooterRow object is null when referenced from the GridView, so it is impossible to use this solution for e.g. inserting a new record from the footer. Would be really great if you could solve this!

Posted by: Anders on September 16, 2007 12:00 AM

A solution to the problem that GridView.FooterRow property is null when the DS is empty is easially solved by overide the FooterRow property

protected GridViewRow _footerRow = null;
public override GridViewRow FooterRow
{
get { return _footerRow == null ? base.FooterRow : _footerRow; }
}

and replacing

GridViewRow footerRow = base.CreateRow(-1, -1, DataControlRowType.Footer, DataControlRowState.Normal);

with

_footerRow = base.CreateRow(-1, -1, DataControlRowType.Footer, DataControlRowState.Normal);


BR Anders

Posted by: sanscars on September 19, 2007 12:00 AM

I try this last solution for inserting a new record but my ADD button in FooterRow not fire the RowCommand event from GridView. I dont know why?

Posted by: Stew on September 28, 2007 12:00 AM

Hi,
This looks exactly what I need, but I second Ken Cherasaros comment - how do you do the subclassing, then access the empty gridview from the page? What does the

Thanks,Stew

Posted by: deejoe on December 4, 2007 12:00 AM

Anders comment was great, but if you use a bound listcontrol (dropdown list for instance) in the footer, you will get exception.

Posted by: deejoe on December 5, 2007 12:00 AM

Forget my previous comment. That was caused by the EVIL copypaste...

Posted by: ZaX on December 5, 2007 12:00 AM

great code, Thank you!

Posted by: Mercury on December 28, 2007 12:00 AM

Its very useful code!
But you forgot
base.OnRowCreated(new GridViewRowEventArgs(_footerRow));

Posted by: Mercury on December 28, 2007 12:00 AM

For some reason code:
rows = base.CreateChildControls(dataSource, dataBinding);
throws an exception if the datasource have not records.
I propose:
int rows = 0; System.Collections.IEnumerator en = dataSource.GetEnumerator();
en.Reset();
if (en.MoveNext())
rows = base.CreateChildControls(dataSource, dataBinding);

Posted by: Bhushan Bharambe on January 2, 2008 12:00 AM

Before seeing this article i dont have any clue of this problem , Its Really Helpfull to me

Posted by: eok on January 15, 2008 12:00 AM

very helpfull, thank you!

Thank you this worked beautifully!

It worked very well for me. Thanks.

Posted by: Pete on April 17, 2008 12:00 AM

Thanks a lot for this, it works well. I have a small issue that I cant seem to get around though.

Using this control, when there is no data, the DataBound event does not fire-- I need the DataBound event to fire, but Im not sure about this controls lifecycle. Obviously there is a call not being made because there is no data, but how can I force DataBound to fire? Ive tried manually calling the event during the EnsureDataBound method, which sort of works, but none of the rows are created at that point in the lifecycle, so that wont work. I need the event to fire at the same time it would if there were data, or after that time, but not before.

Can anyone help with that?

Posted by: Bataken on May 13, 2008 12:00 AM

Really very useful, but if u have for instance in FooterTemplate control like ImageButton with CausesValidation property set to false and when click nothing happened, just interesting why

Posted by: Jonx on June 11, 2008 12:00 AM

It would have been nice to be able to download a working project but nevertheless, its the best solution of that kind to my knowledge. Thank you...

I really like your code but would love to be able to see all of the code. The link you provided when viewing the actual GridView to download the code does not work, can you please fix this so we can see the code. Thanks.

If you want the empty row to have the same CSS class as the normal rows, you might consider this slight change:

// respect the precedence order if both EmptyDataTemplate
// and EmptyDataText are both supplied ...
if (this.EmptyDataTemplate != null)
{
this.EmptyDataTemplate.InstantiateIn(cell);
}
else if (!string.IsNullOrEmpty(this.EmptyDataText))
{
cell.Controls.Add(new LiteralControl(EmptyDataText));
}

emptyRow.Cells.Add(cell);
emptyRow.CssClass = this.RowStyle.CssClass; // table.Rows.Add(emptyRow);

Posted by: e-Fendi on August 11, 2008 03:31 AM

The question is when do we call this method: CreateChildControls(System.Collections.IEnumerable dataSource, bool dataBinding) ?

Posted by: Davide on August 26, 2008 05:04 AM

Hi, i'm trying to use this code but i've problems with it: when i change in Visual Studio the property "ShowHeaderWhenEmpy" or "ShowFooterWhenEmpy" i obtain a rendering error on the gridView.
That code only works if i never change the value of these 2 properties.

Can you help me to solve this issue?
Thanks a lot.

Best Regards.

Posted by: Jane on August 26, 2008 01:48 PM

Your code is working great, except that the skins I had previsouly applied are not working now. Is this supposed to work with skin files?

Posted by: Rappido on September 8, 2008 02:31 PM

Please can you tell me how to set the FooterStyle of the GridView to the EmptyFooterRow?

example:
<egv:extGridView ID="extGridView1" runat="server"
EmptyDataText="No records found"
OnRowCreated="extGridView1_RowCreated"
ShowFooterWhenEmpty="True"
ShowHeaderWhenEmpty="True">

<egv:extGridView>

In extGridView1_RowCreated I create a button and add it to the e.Row.Cell.
This works but now I also want to set the RowStyle to the FooterStyle

Please can you help me?

Posted by: Jon on September 11, 2008 09:35 AM

Any idea how to pull this off with a listview without having to duplicate html in the emptydatatemplate?

Posted by: Andrey Dudarev on October 29, 2008 11:08 AM

Nice code. I have used it in my custom GridView with small addition:

// add the header row to the table
table.Rows.Add (headerRow);
this.OnRowDataBound (new GridViewRowEventArgs(headerRow));

it will fire OnDataRowBound event for header, same can be done for footer.
Thanks

Posted by: Vladi on November 5, 2008 03:56 PM

Has anyone discovered how to enable skin support with this extended control? Thanks!

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Consulting Services

Yep - I also offer consulting services. And heck, I'll do just about anything. If you enjoy my blog just drop me an email describing the work you need done.

Recent Comments

  • Vladi wrote: Has anyone discovered how to enable skin support with this extended control? Thanks!...
  • Andrey Dudarev wrote: Nice code. I have used it in my custom GridView with small addition: // add the header row to the ...
  • Jon wrote: Any idea how to pull this off with a listview without having to duplicate html in the emptydatatempl...
  • Rappido wrote: Please can you tell me how to set the FooterStyle of the GridView to the EmptyFooterRow? example: &...
  • Jane wrote: Your code is working great, except that the skins I had previsouly applied are not working now. Is ...
  • Davide wrote: Hi, i'm trying to use this code but i've problems with it: when i change in Visual Studio the proper...
  • e-Fendi wrote: The question is when do we call this method: CreateChildControls(System.Collections.IEnumerable data...
  • Kenneth Scott wrote: If you want the empty row to have the same CSS class as the normal rows, you might consider this sli...