A Strongly Typed ASP.NET MVC WebGrid

March 12, 2012 at 9:58 PMWebsite Sorcery

I've been working on a project which requires server side paging and sorting to cope with potentially millions of records. I originally used the jQuery Tablesorter plugin with client side sorting and paging which worked well both functionally and aesthetically, but for the full bells and whistles project I stumbled upon the MSDN post Get the Most out of WebGrid in ASP.NET by Stuart Leeks.

Within in the midst of Stuart's post he creates a wrapper for the ASP.NET MVC WebGrid to allow Strong Typing. I debated the practicality of using strong typing with WebGrid which could be said to work better dynamically, but there are other benefits to strong typing which make this a decent little coding nugget.

Before getting to the code I'll give you a quick looky at how it works from the UI:

<div>
    @{
        var grid = new WebGrid<Product>(null, rowsPerPage: Model.PageSize, defaultSort: "Name");
        grid.Bind(Model.Products, rowCount: Model.TotalRows, autoSortAndPage: false);
    }
    @grid.GetHtml(
        columns: grid.Columns(
            grid.Column(
                "Name", format: @<text>@Html.ActionLink((string)item.Name,
                "Details", "Products", new { id = item.Id }, null)</text>),
            grid.Column("Description")
        )
    )
</div>

I created the WebGrid wrapper class in an Infrastructure folder in my website. It references another class called EnumerableExtensions which provides a useful SafeCast method to cast an IEnumerable. Here's the code:

Strongly Typed WebGrid Wrapper (WebGrid.cs)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Helpers;

namespace MyWebSite.Infrastructure
{
    /// <summary>
    /// Wrapper for System.Web.Helpers.WebGrid that preserves the item type from the data source
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class WebGrid<T> : WebGrid
    {
        /// <param name="source">Data source</param>
        /// <param name="columnNames">Data source column names. Auto-populated by default.</param>
        /// <param name="defaultSort">Default sort column.</param>
        /// <param name="rowsPerPage">Number of rows per page.</param>
        /// <param name="canPage">true to enable paging</param>
        /// <param name="canSort">true to enable sorting</param>
        /// <param name="ajaxUpdateContainerId">ID for the grid's container element. This enables AJAX support.</param>
        /// <param name="ajaxUpdateCallback">Callback function for the AJAX functionality once the update is complete</param>
        /// <param name="fieldNamePrefix">Prefix for query string fields to support multiple grids.</param>
        /// <param name="pageFieldName">Query string field name for page number.</param>
        /// <param name="selectionFieldName">Query string field name for selected row number.</param>
        /// <param name="sortFieldName">Query string field name for sort column.</param>
        /// <param name="sortDirectionFieldName">Query string field name for sort direction.</param>
        public WebGrid(IEnumerable<T> source = null, IEnumerable<string> columnNames = null, string defaultSort = null, int rowsPerPage = 10, bool canPage = true, bool canSort = true, string ajaxUpdateContainerId = null, string ajaxUpdateCallback = null, string fieldNamePrefix = null, string pageFieldName = null, string selectionFieldName = null, string sortFieldName = null, string sortDirectionFieldName = null)
            : base(source.SafeCast<object>(), columnNames, defaultSort, rowsPerPage, canPage, canSort, ajaxUpdateContainerId, ajaxUpdateCallback, fieldNamePrefix, pageFieldName, selectionFieldName, sortFieldName, sortDirectionFieldName)
        {
        }
        public WebGridColumn Column(string columnName = null, string header = null, Func<T, object> format = null, string style = null, bool canSort = true)
        {
            Func<dynamic, object> wrappedFormat = null;
            if (format != null)
            {
                wrappedFormat = o => format((T)o.Value);
            }
            WebGridColumn column = base.Column(columnName, header, wrappedFormat, style, canSort);
            return column;
        }
        public WebGrid<T> Bind(IEnumerable<T> source, IEnumerable<string> columnNames = null, bool autoSortAndPage = true, int rowCount = -1)
        {
            base.Bind(source.SafeCast<object>(), columnNames, autoSortAndPage, rowCount);
            return this;
        }
    }
}

Enumberable Extensions (Enumerable Extensions.cs)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace MyWebSite.Infrastructure
{
    public static class EnumerableExtensions
    {
        public static IEnumerable<TTarget> SafeCast<TTarget>(this IEnumerable source)
        {
            return source == null ? null : source.Cast<TTarget>();
        }
    }
}

To read more about the WebGrid I suggest reading Stuart Leeks' excellent post. Additionally, learn how to Style the ASP.NET MVC WebGrid.

Posted in: ASP.NET MVC

Tags: ,