Looking At The WebMatrix WebGrid

One of the most useful helpers provided by ASP.NET Web Pages via WebMatrix is likely to be the WebGrid, which is designed for displaying tabular data. Still in Beta 1, documentation is sparse, so here's a more detailed look the WebGrid Helper.

The WebGrid helper is designed for rendering data taken from a database or provided via some other means. It has built-in support for paging and sorting (although a bug in the Beta 1 prevents you sorting on null DateTime values), and also offers access to a number of other formatting and design configurations. The main constructor (method called to create a WebGrid object) takes a number of arguments (mostly optional) as follows:

IEnumerable<object> source The data that the grid will display
[Optional, Default Value(null)] IEnumerable<string> columnNames The values that you want to appear as column headers.
[Optional, Default Value(null)] string defaultSort The column that you want to have the grid sorted on by default
[Optional, Default Value(10)] int rowsPerPage The number of rows you want per page
[Optional, Default Value(true)] bool canPage If this is true (it is by default) the grid can be paged
[Optional, Default Value(true)] bool canSort If this is true (it is by default) the grid can be sorted
[Optional, Default Value(null)] string ajaxUpdateContainerId The id of the containing element for Ajax paging and sorting support
[Optional, Default Value(null)] string fieldNamePrefix A value which prefixes the default querystring fields
such as "sort" and "sortdir"
[Optional, Default Value(null)] string pageFieldName A custom value to replace the default querystring "page" field
[Optional, Default Value(null)] string selectionFieldName A custom value to replace the default querystring "row" field
[Optional, Default Value(null)] string sortFieldName A custom value to replace the default querystring "sort" field
[Optional, Default Value(null)] string sortDirectionFieldName A custom value to replace the default querystring "sortdir" field

 

The first argument, source is not optional. The grid must have some data to display. This can be any IEnumerable but most often it will be the results of a database query. All other arguments are optional, which means they have default values and do not need to be specified when creating a grid. If you want to pass anything other than the default values, you need to pass the the argument name and parameter value into the constructor in the following manner - argumentname : value and each one needs to be separated by a comma. It doesn't matter what order they are passed in, so long as source is first. For example, you can decide that you do not want paging enabled, but you want to specify the default column for sorting. defaultSort : "column1", canPage : false is just as valid as canPage : false, defaultSort : "column1".

I'm going to use the simple Books.sdf file I created for previous articles to show a basic implementation of the WebGrid using some of these arguments. To start with, this example shows a WebGrid being built using the minimum amount of code. Two things are required - the creation of a valid WebGrid object with a source of data, and a call to the WebGrid.GetHtml() method to write the resulting table to the browser:

@{
    var db = Database.Open("Books");
    var sql = "Select BookId, Title, ISBN, Description, FirstName, LastName, Category, DatePublished  " +  
               "From Books Inner Join Authors on Books.AuthorId = Authors.AuthorId " + 
               "Inner Join Categories on Books.CategoryId = Categories.CategoryId";
    var books = db.Query(sql);
    var grid = new WebGrid(books);
}

    @grid.GetHtml()

When rendered, you can see that the default behaviour is for all columns of data to be included in the grid, and for paging and sorting to be enabled with 10 rows per page:

Limiting the columns can be done through the columnNames argument. This just needs to be included in the constructor with an IEnumerable of strings (an array will do):

@{
    var db = Database.Open("Books");
    var sql = "Select BookId, Title, ISBN, Description, FirstName, LastName, Category, DatePublished  " +  
               "From Books Inner Join Authors on Books.AuthorId = Authors.AuthorId " + 
               "Inner Join Categories on Books.CategoryId = Categories.CategoryId";
    var books = db.Query(sql);
    var grid = new WebGrid(books, columnNames : new []{"BookId", "Title", "ISBN", "Category"});
}

    @grid.GetHtml()

Now it's time to look at another argument - the ajaxUpdateContainerId. This points to the container element that will provide partial updates via jQuery AJAX. To start with, we'll amend the previous code to include two items - a paragraph showing the current time, and a div for the grid:

@{
    Layout = "~/Shared/_Layout.cshtml";
    var db = Database.Open("Books");
    var sql = "Select BookId, Title, ISBN, Description, FirstName, LastName, Category, DatePublished  " +  
               "From Books Inner Join Authors on Books.AuthorId = Authors.AuthorId " + 
               "Inner Join Categories on Books.CategoryId = Categories.CategoryId";
    var books = db.Query(sql);
    var columns = new []{"BookId", "Title", "ISBN", "Category"};
    var grid = new WebGrid(books, columnNames : columns);
}
<p>The time is @DateTime.Now </p>
<div id="grid">
    @grid.GetHtml()
</div>

Now, if you run the page and click one of the pager links, or a sorting link, you will see that the time changes, indicating that the whole page refreshes to display the new page of data or the re-sorted grid. The div with the id of "grid" will be the ajaxUpdateContainer, so that needs to be passed into the constructor. But there is one other thing to do, and that is to obtain the jquery library and make it available to the page. You can download jQuery from here: http://jquery.com/. Choose the compressed and minified version as it's the smallest for downloading to the browser. My sample so far has used a Layout page, which contains the <head> element. This is where the reference to the jQuery library needs to be made. So I add the downloaded file to a new folder called Scripts, and add the following to the head element:

<head>
    <title></title>
    <link href="@Href("~/Styles/StyleSheet.css")" rel="stylesheet" type="text/css" />
    <script type="text/javascript" src="@Href("~/Scripts/jquery-1.4.4.min.js")"></script>
</head>

The contructor for the grid needs to be changed. In the example below, I have moved the code that creates an array for the columns outside of the grid constructor to save having to wrap it for display on my web site but I have also passed the id of the div to the ajaxUpdateContainerId parameter:

@{
    Layout = "~/Shared/_Layout.cshtml";
    var db = Database.Open("Books");
    var sql = "Select BookId, Title, ISBN, Description, FirstName, LastName, Category, DatePublished  " +  
               "From Books Inner Join Authors on Books.AuthorId = Authors.AuthorId " + 
               "Inner Join Categories on Books.CategoryId = Categories.CategoryId";
    var books = db.Query(sql);
    var columns = new []{"BookId", "Title", "ISBN", "Category"};
    var grid = new WebGrid(books, columnNames : columns, ajaxUpdateContainerId: "grid");
}
<p>The time is @DateTime.Now </p>
<div id="grid">
    @grid.GetHtml()
</div>

Now if you run the page and click the sorting or paging links, you see that the time does not change. The only thing that changes is the contents of the div.

If you don't want to allow sorting or paging, you need to pass false for the canPage or the canSort arguments:

var grid = new WebGrid(books, canSort : false, columnNames : columns, ajaxUpdateContainerId: "grid");

This results in a plain table of data and no sorting links.

So far, we have only looked at passing configuration data into the WebGrid constructor. Now we'll have a look at the optional arguments that the WebGrid.GetHtml() method takes:

[Optional, Default Value(null)] string tableStyle CSS style for the table
[Optional, Default Value(null)] string headerStyle CSS style for the header
[Optional, Default Value(null)] string footerStyle CSS style for the footer
[Optional, Default Value(null)] string rowStyle CSS style for rows
[Optional, Default Value(null)] string alternatingRowStyle CSS style for alternating rows.
[Optional, Default Value(null)] string selectedRowStyle CSS style for selected row
[Optional, Default Value(true)] bool displayHeader Whether to display the table header or not
[Optional, Default Value(false)] bool fillEmptyRows If the number of rows on the current page is less than
the rowsPerPage, empty rows are created if this is true.
[Optional, Default Value(null)] string defaultCellValue The value for empty cells. By default, it's "&nbsp;"
[Optional, Default Value(null)] IEnumerable<WebGridColumn> columns Columns to be included
[Optional, Default Value(null)] IEnumerable<string> exclusions Columns to be excluded
[Optional, Default Value(3)] WebGridPagerModes mode Pager display options
[Optional, Default Value(null)] string firstText Replace the default << link for the first page
[Optional, Default Value(null)] string previousText Replace the default > link for the next page
[Optional, Default Value(null)] string nextText Replace the default > link for the next page
[Optional, Default Value(null)] string lastText Replace the default >> link for the last page
[Optional, Default Value(5)] int numericLinksCount Set the number of numeric links in the pager

The first options all cover styling the table and its contents. Here's some css styles that illustrate this:

.table{
    border : 1px solid #809FFF;
    background-color: #E6FFFF;
    color:  #6078BF;
    border-collapse: collapse;
}

.header{
    background-color: #6078BF;
    color: white;
}

.alternate{
    background-color: #EEE6FF;
}

Now if we apply these to the GetHtml method:

@grid.GetHtml(
    tableStyle : "table",
    alternatingRowStyle : "alternate",
     headerStyle : "header"
)

we can see the result like this:

Now it's time to look at columns. Ideally, you would have some control over which columns of data are returned by the SQL query, but this may not always be the case, so you need some way of switching columns off. Or you might want one query to run, but to be able to determine which columns can be seen based on the current roles of the user. If you haven't set the columns for display in the constructor, you can do so here, either by specifying columns to include through the columns parameter, or by specifying which ones not to display through exclusions. The optional arguments for the column constructor are as follows:

[Optional, Default Value(null)] string columnName The column name
[Optional, Default Value(null)] string header Header text if you don't want database field names
[Optional, Default Value(null) Func<object, object> format Formatting to be applied to values
[Optional, Default Value(null)] string style CSS styles to be applied to the content
[Optional, Default Value(true)] bool canSort Switches sorting off when set to false

 

To illustrate these at work, here's some more code followed by an image showing the result:

@grid.GetHtml(
    columns: grid.Columns(
        grid.Column(
            columnName : "BookId",
            header : "Id",
            canSort : false,
            style : "disabled"),
        grid.Column(
            columnName : "Title"),
        grid.Column(
            columnName : "Category"),
        grid.Column(
            columnName : "DatePublished",
            format: @<text>@item.DatePublished.ToShortDateString()</text>)

    )
)

Looking at the rendered grid you can see that only the columns specified have been included in the grid. Further, the BookId column has a new name, Id, and cannot be sorted on. To help give users a visual clue that this is read-only, the column has a CSS class applied which displays the values as a light grey colour. In addition, the DatePublished column has been formatted so that the DateTimes do not include the hours, minutes and seconds.

If you would like to play with the sample code presented in this article, this download includes a number of pages showing the concepts presented here together with a copy of the simple database used.

Date Posted: Sunday, August 8, 2010 9:33 AM
Last Updated: Tuesday, November 11, 2014 8:13 AM
Posted by: Mikesdotnetting
Total Views to date: 62894

32 Comments

Sunday, August 8, 2010 11:14 AM - reav

So nice to finally see webgrid helper explained. Thank you, Mike!

Can you now show how to use some AJAX-powered forms?

Friday, August 13, 2010 8:28 AM - EinarT

This was most useful to read and try! Nice to see Ajax in action. The next I am thinking about, is:
a. How select a row id and open a child window to edit data or add a new row.
b. How to select and return a row id when the grid is in a child window.
c. How to refresh the grid with a filter using Ajax.
Perhaps your next article will give some answers.

Friday, August 13, 2010 12:36 PM - Mike

@EinarT

The next article does indeed provide one or two answers: http://www.mikesdotnetting.com/Article/155/WebMatrix-And-jQuery-Forms

Monday, August 23, 2010 4:36 AM - infocyde

Thanks for sharing this, info has come in handy.

Sunday, September 19, 2010 11:01 AM - reav

how to use "selectedRowStyle" param? is there possible to determine, what row user selected?

Monday, September 20, 2010 9:09 PM - Mike

@reav,

I confess I couldn't work that out which is why I didn't mention it in the article. It would appear that it allows you to provide a css style to the selected row, but I couldn't quite establish how it knew which row that was. There is also another parameter in the WebGrid constructor that refers to selectedRows, but I couldn't get that to do anything either. It's something I intended to revisit at the time, but it completely slipped my mind. I'll see what I can find out.

Saturday, October 2, 2010 8:51 AM - Mike

@reav,

You can make a grid selectable by using the GetSelectLink() helper:
grid.Column(header:"Select", format:@<text>@item.GetSelectLink()</text>)

Wednesday, October 20, 2010 11:27 AM - Esh

Hi! Great article & really useful.
a) From where do you figure out all these parameters? Webmatrix documentation is quite brief.
b) When I try pagermode parameter as per above, WM throws error. (i.e. Webgrid(datasource, mode: 3)
Thanks in advance.
Esh

Monday, December 13, 2010 9:38 PM - Ryan

This grid, much like all of microsoft's helpers leaks dom elements badly when coupled with ajax in IE7. Do you know of any solutions to this? I've hunted endlessly for a way to fix it.

Tuesday, December 14, 2010 7:04 AM - Mike

@Ryan,

I suspect the culprit is the default way in which the jQuery call uses the .load(element, segment) option. The whole of the page is brought back, but only the Grid portion is updated. It's a bit like the Update Panel in that respect. Not very precise... I would implement a more efficient paging model and my own custom AJAX on the thing.

Wednesday, January 12, 2011 2:20 PM - Max

It is possible to add an "edit" and "delete" column to the grid?

Wednesday, January 12, 2011 10:42 PM - Mike

@Max,

Yes - that's very doable. Come back tomorrow and you will find a new post which covers that scenario.

:o)

Wednesday, March 9, 2011 10:22 PM - Tom

This is a great tutorial - thanks Mike. The pagination is a real timesaver with WebGrid - but is there way to display the data outside of a table?

I can think of instances where a table isn't the most semantic solution, but where it'd be really helpful to use WebGrid's pagination feature.

Wednesday, March 9, 2011 11:07 PM - Mike

Tuesday, October 2, 2012 1:15 PM - vineet

Hi Mikesdotnetting,
I want to apply a specific class on full row on the value of a column in that row. Is this possible webgrid.

Regards,
Vineet

Thursday, October 4, 2012 7:38 AM - Mike

@vineet

You need to use client-side Javascript to do that with the current WebGrid.

Tuesday, October 9, 2012 1:35 PM - oleg_harp

Thank you Mike!
How to render row order number instead of ID? It is simply with ROW_NUMBER in SQL Server 2005, 2008, but what is about Compact Edition? Is there a way to do it with WebGrid?

Thursday, October 11, 2012 8:24 AM - Mike

@oleg_harp

You will have to project the data into a new form, for example an anonymous object that includes a property that represents the "row number". You can use LINQ to do that.

Thursday, October 11, 2012 10:24 AM - oleg_harp

Thank you Mike. For me the game is not worth the candle.

Tuesday, October 23, 2012 12:16 AM - Eric Schrepel

Pretty new, so dumb question likely. Using WebGrid to display data, working fine. But I want the data source for webgrid to update when user picks an item from an <input type="select"> dropdown field above the webgrid.

How do I pass the value from the dropdown field into parameters for the webgrid datasource and re-display the webgrid?

Wednesday, October 24, 2012 1:41 PM - Mike

@Eric,

I believe I cover that scenario in this article: Displaying Search Results In A WebGrid

Sunday, March 3, 2013 5:13 AM - Philip Hunt

Is there a way of suppressing the repeated display of the category. In your example the words ASP.NET Development and Javascript are repeated against each book to which they apply. Is there a way of telling the browser to only display the value of that field when it changes from the previous line?

Wednesday, March 6, 2013 6:02 AM - Mike

Wednesday, March 20, 2013 8:19 PM - Brenton White

Is there a way to put an id=myTable (e.g., <table id=myTable class="grid"> in the rendered HTML? I need to refer to the table by ID for HTML2CSV export. Or, better yet, is there a way to export and save the data from the grid to CSV or Excel format? THANKS.

Wednesday, March 20, 2013 9:21 PM - Mike

@Brenton,

You can't apply an ID to the rendered source except through jQuery, but you can put the grid in a div and then reference it like that:

div#myTable table{ ... }

Or you can read this article: Exporting The Razor WebGrid To Excel

Wednesday, March 20, 2013 11:09 PM - Brenton White

MANY thanks. You couldn't have given me a better answer! Very much appreciated.

Wednesday, October 30, 2013 12:15 PM - George Stanly

Dear Mike - are there any techniques you can share using Webmatrix Webgrid to format a row based on conditions. Here is what I am trying to do. I have a list of lab tests that I wanted to format - the row based on the status of the test. "Not started" has white background. "In progress" has yellow and "completed" has green. The table that is used to populate the webgrid has all the values for the status and the color to use. Thanks for your help.

Wednesday, November 12, 2014 1:21 AM - Roger

The WebGrid certainly speeds things up when displaying data but how do you align the column headings? I want some left aligned and others right aligned but can't find any way of doing it and centered headings just don't look right.

Wednesday, November 12, 2014 5:56 AM - Mike

@roger

You can use the :nth child selector in your CSS to target specific th elements: http://www.w3schools.com/cssref/sel_nth-child.asp

Wednesday, November 12, 2014 4:47 PM - Roger

Many thanks, works a treat. :-)

Monday, November 17, 2014 11:26 AM - Gautam

Hi Mike, I add the jquery script at the end of my html file.. when ajax attribute is added to the webgrid, the jquery script must be in the head section... Is there a way to make the webgrid add the ajax script for the grid after the jquery script at the bottom?

Monday, November 17, 2014 1:39 PM - Mike

@Gautam,

There is no easy way that I know of.
Add your comment

If you have any comments to make about this article, please use this form to do so. Make sure that your comment relates specifically to the article above. More general comments can be posted through the form on the Contact page.

Please note, all comments are moderated, and some may not be published. The kind of things that will ensure your comment is deleted without ever seeing the light of day are as follows:

  • Not relevant to the article
  • Gratuitous links to your own site or product
  • Anything abusive or libellous
  • Spam
  • Anything in a language I don't understand including gibberish.

I do not pass email addresses on to spammers, so a valid one will assist me in responding to you personally if required.

Recent Comments

Allen Michaels 12/17/2014 4:37 PM
In response to Cascading DropDownLists with jQuery and ASP.NET
Fantastic thank you so much!...

Emily 12/17/2014 12:36 PM
In response to Parameterized IN clauses with ADO.NET and LINQ
Thanks, very helpful!!!! Can i use this for multiple in's ????? IN (.....) and IN(...) and IN...

sss 12/16/2014 3:06 PM
In response to Solving the Operation Must Use An Updateable Query error
good...

Gjuro 12/15/2014 10:30 PM
In response to Examining the Edit Methods and Edit View
You have one fromr (and it should be from, I suppose). :-)...

Gjuro 12/15/2014 10:27 PM
In response to Adding Search
Hi, thnx for all this C#->VB translations. Yet, the following code block is (slightly) in error it a...

Scot 12/14/2014 1:39 PM
In response to Entity Framework 6 Recipe - Alphabetical Paging In ASP.NET MVC
Thanks,Mike I found solution....

Gjuro 12/13/2014 10:52 PM
In response to Accessing Your Model's Data from a Controller
The article mentions "Creating an Entity Framework Data Model for an ASP.NET MVC Application" (at is...

Samuel 12/13/2014 8:40 AM
In response to Displaying The First n Characters Of Text
I have failed to use the extension because it throws an error that it doesn't recognise the chop be...

Ignas 12/12/2014 5:11 PM
In response to Cleaner Conditional HTML Attributes In Razor Web Pages
Any suggestions for Html Helper elements with HtmlAttributes, when you need to conditionally set it...

Gautam 12/11/2014 8:50 PM
In response to Validation In Razor Web Pages 2
Hi Mike Is this required for V3, non html helper input...