Logo

Maarten Balliauw {blog}

ASP.NET, ASP.NET MVC, Azure, PHP, OpenXML, VSTS, ...

About the author

Maarten Balliauw is an MVP ASP.NET and is currently employed as .NET Software Engineer at RealDolmen. His interests are mainly web applications developed in ASP.NET (C#) or PHP.
More about me More about me
Send mail E-mail me


Microsoft Most Valuable Professional - MVP - ASP.NET

Subscribe to my RSS feed Follow me on Twitter! View Maarten Balliauw's profile on LinkedIn RealDolmen - Rock-solid passion for ICT
I'm a speaker at TechDays Belgium and TechDays Finland

Search

Latest Twitter

    Follow me on Twitter...

    Disclaimer

    The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

    © Copyright Maarten Balliauw 2010

    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:

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

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

    It translates nicely into:

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

    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:

    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);
        }
    }

    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:

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

        public T EditItem { get; set; }
    }

    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).

    <%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>
    <% });%>

    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).

    // ...

    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");
    }

    // ...

    Note: based on ASP.NET MVC preview 3

    kick it on DotNetKicks.com


    Categories: ASP.NET | C# | General | ICT | Internet | MVC | Projects | Software

    Comments

    alvinashcraft.com | Reply

    Wednesday, June 04, 2008 2:42 PM

    pingback

    Pingback from alvinashcraft.com

    Dew Drop – June 4, 2008 | Alvin Ashcraft's Morning Dew

    David United States | Reply

    Wednesday, June 04, 2008 3:22 PM

    David

    Nice. I had used the same technique to create a DataList like view (though no paging).

    You can probably abstract out the <table>, <tr>, <td> tags using Response.Write(). But yeah, good stuff. Thanks for sharing.

    Here's the code:

            public static void RenderSimpleTable<T>(
                this HtmlHelper helper,
                IEnumerable<T> items,
                int columns,
                Object tableTagAttributes,
                Object trTagAttributes,
                Object tdTagAttributes,
                Action<T> itemRenderHandler)
            {
                helper.RenderSimpleTable(
                    items,
                    columns,
                    tableTagAttributes,
                    trTagAttributes,
                    tdTagAttributes,
                    itemRenderHandler,
                    true);
            }

            public static void RenderSimpleTable<T>(
                this HtmlHelper helper,
                IEnumerable<T> items,
                int columns,
                Object tableTagAttributes,
                Object trTagAttributes,
                Object tdTagAttributes,
                Action<T> itemRenderHandler,
                bool evenlySpaceColumns)
            {
                var percent = ((int)(((float)1 / (float)(columns)) * 100)).ToString() + "%";
                var response = helper.ViewContext.HttpContext.Response;

                var tableTagAttributeString
                    = tableTagAttributes != null
                        ? " " + tableTagAttributes.ToAttributeList()
                        : String.Empty;

                var trTagAttributesString
                    = trTagAttributes != null
                        ? " " + trTagAttributes.ToAttributeList()
                        : String.Empty;

                var tdTagAttributesString
                    = tdTagAttributes != null
                        ? " " + tdTagAttributes.ToAttributeList()
                        : String.Empty;

                var evenlySpaceStyleString
                    = evenlySpaceColumns
                        ? String.Format(@" style=""width:{0}""", percent)
                        : String.Empty;

                helper.RenderTable<T>(
                    items,
                    columns,
                    () => { response.Write(String.Format(@"<table{0}>", tableTagAttributeString)); },
                    () => { response.Write(String.Format(@"<tr{0}>", trTagAttributesString)); },
                    (item) => {
                        response.Write(String.Format(@"<td{0}{1}>", evenlySpaceStyleString, tdTagAttributesString));
                        itemRenderHandler(item);
                        response.Write(@"</td>");
                    },
                    () => { response.Write(@"</tr>"); },
                    () => { response.Write(@"</table>"); },
                    () => { response.Write(String.Format(@"<td{0}{1}>&nbsp;</td>", evenlySpaceStyleString, tdTagAttributesString)); });
            }

            public static void RenderTable<T>(
                this HtmlHelper helper,
                IEnumerable<T> items,
                int columns,
                Action headRenderHandler,
                Action groupHeadRenderHandler,
                Action<T> itemRenderHandler,
                Action groupTailRenderHandler,
                Action tailRenderHandler,
                Action emptyRenderHandler)
            {
                headRenderHandler();
                int column = 0;

                foreach (var item in items)
                {
                    if (column == 0)
                        groupHeadRenderHandler();
                    itemRenderHandler(item);
                    if (column == columns - 1)
                        groupTailRenderHandler();
                    column = (column + 1) % columns;
                }

                if (column > 0)
                {
                    for (int i = column; i < columns; i++)
                        emptyRenderHandler();
                }

                tailRenderHandler();
            }

    maartenba Belgium | Reply

    Wednesday, June 04, 2008 3:30 PM

    maartenba

    I smell a very nice combination here for creating on-the-fly gridview generation from any kind of data set Smile

    Klaus Graefensteiner United States | Reply

    Wednesday, June 04, 2008 4:20 PM

    Klaus Graefensteiner

    Nice MVC example!

    Glenn United States | Reply

    Wednesday, June 04, 2008 4:31 PM

    Glenn

    Nice stuff.  What really needs to occur for Web development to move forward.  A couple things though...

    Minor - it looks like the Cancel and Delete icons are mixed up.  (Cancel shows to delete line, Delete shows to Cancel Edit.)
    Not so Minor - Sorting would be needed for what I usually use a GridView for.
    Not so Minor - It posts back with each added line or change.  Can't it do this using Ajax?  This doesn't look much better than a using ViewState Web Page Control.

    maartenba Belgium | Reply

    Wednesday, June 04, 2008 5:59 PM

    maartenba

    That's a consideration to make... I would say: use some of the advanced JavaScript + Ajax grids out there (see start of my post). If you need something similar to the old "postback" method, this would be the way to go. Usage really depends on how and what and when.

    Rob Conery United States | Reply

    Thursday, June 05, 2008 12:23 AM

    Rob Conery

    Maarten - great stuff! I've noodled with this stuff using a ComponentController (as that's what this is...) - check it out. That's why we made the ComponentController - for this very thing!

    maartenba Belgium | Reply

    Thursday, June 05, 2008 9:28 AM

    maartenba

    Actually this was the first thing that popped up in my mind. I've used the ComponentController before in the Mvc Membership provider (www.codeplex.com/MvcMembership) and it proved quite nice to work with.

    Using a ComponentController for something like a gridview would indeed be an interesting approach, as one could use 1 ComponentController base class, if needed a specific ComponentController, and multiple Views with that.

    Mohammad Javed India | Reply

    Monday, June 09, 2008 12:03 PM

    Mohammad Javed

    your are providing good code.

    code-inside.de | Reply

    Monday, June 09, 2008 9:10 PM

    pingback

    Pingback from code-inside.de

    Wöchentliche Rundablage: Silverlight 2, WPF, ASP.NET MVC, jQuery… | Code-Inside Blog

    frenchiesunderdown.wordpress.com | Reply

    Friday, June 13, 2008 11:03 AM

    pingback

    Pingback from frenchiesunderdown.wordpress.com

    MVC Framework preview 3, first contact « Frenchies Under Down

    Duckie | Reply

    Sunday, June 22, 2008 7:53 PM

    Duckie

    Hm, is there any way to convert this to vb.net, i tried and failed Smile

    sp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
        <h2>Employees</h2>
        <%Html.GridView<Employee>(
              this.ViewData.Model,
              data => { %>
                <table class="grid" cellpadding="0" cellspacing="0">
                    <tr>
                    ....

    maartenba Belgium | Reply

    Monday, June 23, 2008 8:01 AM

    maartenba

    Affraid I can't help you there. Are multi-line lambda's supported in VB.NET?

    Duckie | Reply

    Monday, June 23, 2008 9:19 AM

    Duckie

    Does not look like it :-(

    dkl Czech Republic | Reply

    Thursday, July 10, 2008 4:43 PM

    dkl

    It seems a little bit too complicated. Too many lambdas just to abstract away one foreach cycle. This is the real meat of this approach:


          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);


    Should we sacrifice readability just because one simple foreach cycle?

    Swordfish Netherlands | Reply

    Friday, July 11, 2008 11:50 PM

    Swordfish

    I second the remark of dlkl (just above). Only why not using the new 3.5 DataList? It's works perfectly with MVC (See Scott Gurthie blog) and it's not too hard to hard to turn it into a datagrid with ajax support. That's reminds me, A few weeks ago I came across a project name Ajax Data Controls from dotnetjunkies. It can do the same as the 'one-liner' function but it has proper template support, it has better maintainability (very important in larger projects where multiple developers are involved) and as dlkl said: readability is also important.



    Mani India | Reply

    Monday, July 21, 2008 3:32 PM

    Mani

    Nice & awesome thank u very much fr giving this code...

    hsidev.wordpress.com | Reply

    Friday, August 01, 2008 7:42 PM

    pingback

    Pingback from hsidev.wordpress.com

    To (ASP.NET)MVC or not to MVC (or, ASP.NET MVC Hyperlink Acupuncture) « HSI Developer Blog

    thefreakparade.com | Reply

    Thursday, September 11, 2008 10:27 PM

    pingback

    Pingback from thefreakparade.com

    To (ASP.NET)MVC or not to MVC (or, ASP.NET MVC Hyperlink Acupuncture) | The Freak Parade

    RealDolmen blogs | Reply

    Wednesday, December 31, 2008 3:43 PM

    trackback

    Trackback from RealDolmen blogs

    Top blog posts in 2008

    dotnet.org.za | Reply

    Friday, February 06, 2009 11:11 AM

    pingback

    Pingback from dotnet.org.za

    MVC Code based Gridview - Baka.Blog

    vamsi India | Reply

    Saturday, May 02, 2009 2:50 PM

    vamsi

    Hi all,

    can any one of you ppl please send me the code to bind gridview in mvc 1.0.

    i am getting the following error when i try to bind data to the gridview.

    Compiler Error Message: CS1928: 'System.Web.Mvc.HtmlHelper' does not contain a definition for 'GridView' and the best extension method overload 'MvcGridView.Extensions.GridViewExtensions.GridView<T>(System.Web.Mvc.HtmlHelper, MvcGridView.Extensions.GridViewData<T>, System.Action<MvcGridView.Extensions.GridViewData<T>>, System.Action<T,string>, string, string, System.Action<T>, System.Action<MvcGridView.Extensions.GridViewData<T>>)' has some invalid arguments


    Thanks in Advance,
    vamsi.

    GreatButNotForReleasedVersion United States | Reply

    Friday, May 15, 2009 10:20 PM

    GreatButNotForReleasedVersion

    Unfortunately this no longer works for MVC1

    This is the problem with eatrly adoption I guess, nothing works after the RC ( in Microsoft )

    And I"m too new to help fix it or I would but just a heads up it doesn't work anymore.

    maartenba Belgium | Reply

    Monday, May 25, 2009 1:49 PM

    maartenba

    I have uploaded an ASP.NET MVC 1.0 version: http://blog.maartenballiauw.be/file.axd?file=2009%2f5%2fMvcGridView-1.0.zip

    Petey United States | Reply

    Wednesday, June 17, 2009 4:29 PM

    Petey

    It's a great thing you post the code up-front!! I hate it when bloggers put the download link all the way in the bottom, or even worse, somewhere in the middle.

    answerspluto.com | Reply

    Tuesday, July 14, 2009 3:43 AM

    pingback

    Pingback from answerspluto.com

    list of urls - 5 « Answers Pluto

    Add comment




      Country flag

    biuquote
    • Comment
    • Preview
    Loading