asp.net-mvcasp.net-mvc-2mvccontrib

MVCContrib Grid showing headers when empty?


The elegant Action Syntax in the MVCContrib Grid gives us the Empty() method. However, the default behavior of MvcContrib.UI.Grid.GridRenderer<T>.RenderHeader() is to hide the table column headers when the grid is empty. Is there a way to show headers when data is not present that does not require a major refactoring?

Now I have heard of hiding the headers by default and hard-coding something but this is not cool to me.

By the way, this is what is happening under the hood (in MvcContrib.UI.Grid.GridRenderer<T>):

protected virtual bool RenderHeader()
{
    //No items - do not render a header.
    if(! ShouldRenderHeader()) return false;

    RenderHeadStart();

    foreach(var column in VisibleColumns())
    {
        //Allow for custom header overrides.
        if(column.CustomHeaderRenderer != null)
        {
            column.CustomHeaderRenderer(new RenderingContext(Writer, Context, _engines));
        }
        else
        {
            RenderHeaderCellStart(column);
            RenderHeaderText(column);
            RenderHeaderCellEnd();
        }
    }

    RenderHeadEnd();

    return true;
}

protected virtual bool ShouldRenderHeader()
{
    return !IsDataSourceEmpty();
}

protected bool IsDataSourceEmpty()
{
    return DataSource == null || !DataSource.Any();
}

Solution

  • In your custom Grid Renderer (subclass GridRenderer<T>) use overrides like these:

    /// <summary>
    /// Renders the <c>table</c> header.
    /// </summary>
    /// <returns>
    /// Returns the negative of the results
    /// of <see cref="GridRenderer<T>.IsDataSourceEmpty"/>.
    /// </returns>
    /// <remarks>
    /// The return value of <see cref="GridRenderer<T>.IsDataSourceEmpty"/>
    /// in this override has no effect on whether the Grid header is rendered.
    /// 
    /// However, this return value is used
    /// by <see cref="GridRenderer<T>.Render"/>
    /// to run <see cref="GridRenderer<T>.RenderItems"/>
    /// or <see cref="GridRenderer<T>.RenderEmpty"/>.
    /// </remarks>
    protected override bool RenderHeader()
    {
        RenderHeadStart();
    
        foreach(var column in VisibleColumns())
        {
            //Allow for custom header overrides.
            if(column.CustomHeaderRenderer != null)
            {
                column.CustomHeaderRenderer(new RenderingContext(Writer, Context, _engines));
            }
            else
            {
                RenderHeaderCellStart(column);
                RenderHeaderText(column);
                RenderHeaderCellEnd();
            }
        }
    
        RenderHeadEnd();
    
        return !base.IsDataSourceEmpty();
    }
    

    protected override void RenderEmpty()
    {
        RenderBodyStart();
        WriteNoRecordsAvailable(base.Writer, this._numberOfTableColumns);
        RenderBodyEnd();
    }
    

    Note that WriteNoRecordsAvailable() is my custom method so it can be ignored.

    Finally:

    /// <summary>
    /// This private member is duplicated
    /// in order to override <see cref="GridRenderer<T>.RenderHeader"/>.
    /// </summary>
    readonly ViewEngineCollection _engines;
    

    /// <summary>
    /// Initializes a new instance of the <see cref="CrmHtmlTableGridRenderer&lt;T&gt;"/> class.
    /// </summary>
    /// <param name="engines">The engines.</param>
    public CrmHtmlTableGridRenderer(ViewEngineCollection engines)
        : base(engines)
    {
        _engines = engines;
    }