Maarten Balliauw {blog}

ASP.NET, ASP.NET MVC, Windows Azure, PHP, ...

NAVIGATION - SEARCH

Code based ASP.NET MVC GridView

ASP.NET MVC GridViewEarlier this week a colleague of mine asked me if there was such thing as a  DataGrid or GridView or something like that in the ASP.NET MVC framework. My first answer was: "Nope!". I advised him to look for a nice foreach implementation or using ExtJS, Dojo or similar. Which made me think... Why not create a simple GridView extension method which generates a nice looking, plain-HTML grid with all required features like paging, editing, deleting, alternating rows, ...?

The idea was simple: an extension method to the HtmlHelper class would be enough. Required parameters: a header and footer template, item template, edit item template, ... But how to pass in these templates using a simple C# parameter... Luckily,  C# 3.0 introduced lambdas! Why? They are super-flexible and versatile! For instance, take the following code:

[code:c#]

// C# code:
public void RenderPerson(Person p, Action<T> renderMethod) {
    renderMethod(p);
}

// ASP.NET code:
<% RenderPerson(new Person(), person => { %>
    Hello! You are <%=person.Name%>.
<% } %>

[/code]

It translates nicely into:

[code:c#]

Response.Write("Hello! You are Maarten.");

[/code]

Creating a GridView extension method should not be that hard! And it sure isn't.

Live demo

Perhaps I should put this last in my blog posts, but there are always people who are only reading the title and downloading an example:

1. The GridView extension method

Quite short and quite easy:

[code:c#]

public static class GridViewExtensions
{
    public static void GridView<T>(
        this HtmlHelper html,
        GridViewData<T> data,
        Action<GridViewData<T>> headerTemplate,
        Action<T, string> itemTemplate,
        string cssClass,
        string cssAlternatingClass,
        Action<T> editItemTemplate,
        Action<GridViewData<T>> footerTemplate)
    {
        headerTemplate(data);

        int i = 0;
        foreach (var item in data.PagedList)
        {
            if (!item.Equals(data.EditItem))
            {
                itemTemplate(item, (i % 2 == 0 ? cssClass : cssAlternatingClass));
            }
            else
            {
                editItemTemplate(item);
            }

            i++;
        }

        footerTemplate(data);
    }
}

[/code]

2. GridViewData

Of couse, data will have to be displayed. And we'll need a property which sets the current item being edited. Here's my Model I'll be passing to the View:

[code:c#]

public class GridViewData<T>
{
    public PagedList<T> PagedList { get; set; }

    public T EditItem { get; set; }
}

[/code]

By the way, the PagedList<T> I'm using is actually a shameless copy from Rob Conery's blog a while ago.

3. The View

Of course, no rendered HTML without some sort of View. Here's a simplified version in which I pass the GridView<T> extension method the required data, header template, item template, edit item template and footer template. Also noice the alternating rows are simply alternating CSS styles (item and item-alternating).

[code:c#]

<%Html.GridView<Employee>(
    this.ViewData.Model,
    data => { %>
        <table class="grid" cellpadding="0" cellspacing="0">
    <% },
    (item, css) => { %>
        <tr class="<%=css%>">
            <td><%=Html.ActionImage<HomeController>(c => c.Edit(item.Id), "~/Content/edit.gif", "Edit", null)%></td>
            <td><%=Html.ActionImage<HomeController>(c => c.Delete(item.Id), "~/Content/delete.gif", "Delete", null)%></td>
            <td>&nbsp;</td>
            <td><%=item.Name%></td>
            <td><%=item.Email%></td>
        </tr>
    <% },
    "item",
    "item-alternating",
    item => { %>
        <%using (Html.Form<HomeController>(c => c.Save(item.Id), FormMethod.Post, new { id = "editForm" })) {%>
            <tr class="item-edit">
                <td><%=Html.SubmitImage("save", "~/Content/ok.gif", new { alt = "Update" })%></td>
                <td><%=Html.ActionImage<HomeController>(c => c.Index(), "~/Content/cancel.gif", "Cancel", null)%></td>
                <td>&nbsp;</td>
                <td><%=Html.TextBox("Name", item.Name)%></td>
                <td><%=Html.TextBox("Email", item.Email)%></td>
            </tr>
        <% } %>
    <% },
    data => { %>
        </table>
<% });%>

[/code]

4. The Controller

The Controller is perhaps the hardest part: it contains all methods that handle actions which are requested by the View. I have a Show action which simply shows the View with current data. Also, I have implemented an Edit and Save action. Make sure to check my example code download for the full example (earlier in this post).

[code:c#]

// ...

public ActionResult Show(int? page)
{
    CurrentPage = page.HasValue ? page.Value : CurrentPage;
    GridViewData<Employee> viewData = new GridViewData<Employee>
    {
        PagedList = Employees.ToPagedList<Employee>(CurrentPage, 4)
    };

    return View("Index", viewData);
}

public ActionResult Edit(int id)
{
    GridViewData<Employee> viewData = new GridViewData<Employee>
    {
        PagedList = Employees.ToPagedList<Employee>(CurrentPage, 4),
        EditItem = Employees.Where( e => e.Id == id).FirstOrDefault()
    };

    return View("Index", viewData);
}

public ActionResult Save(int id)
{
    BindingHelperExtensions.UpdateFrom(
        Employees.Where(e => e.Id == id).FirstOrDefault(),
        Request.Form
    );
    return RedirectToAction("Show");
}

// ...

[/code]

Note: based on ASP.NET MVC preview 3

kick it on DotNetKicks.com 

blog comments powered by Disqus