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:
Last Updated:
Posted by:
Total Views to date: 68484

36 Comments

- reav

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

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

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

- Mike

@EinarT

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

- infocyde

Thanks for sharing this, info has come in handy.

- reav

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

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

- Mike

@reav,

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

- 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

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

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

- Max

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

- Mike

@Max,

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

:o)

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

- Mike

- 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

- Mike

@vineet

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

- 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?

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

- oleg_harp

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

- 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?

- Mike

@Eric,

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

- 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?

- Mike

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

- 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

- Brenton White

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

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

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

- 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

- Roger

Many thanks, works a treat. :-)

- 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?

- Mike

@Gautam,

There is no easy way that I know of.

- Gaetan C

I use the webgrid very often and I Wonder if it's possible to merge to different sql query into on single webgrid?

- ashwini

Thanks for taking time to write this :)

- Davo

Hi,

An excellent tutorial, congratulations. I am wondering how the WebGrid performs when there is too much vertical content to render? For example does it add scroll bars to the browser window, etc?

Kind Regards,

Davo

- Mike

@Davo,

What's "too much"? There is no limit to the depth of a web page. If you place a webgrid (or any other object) in a container with a fixed height that isn't large enough for the content, you control whether scrollbars etc appear through CSS.
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 I end up deleting quite a lot. The kind of things that will ensure your comment is deleted without ever seeing the light of day are as follows:

  • Requests to fix your code (post a question to forums.asp.net instead, please)
  • Gratuitous links to your own site or product
  • Anything abusive or libellous
  • Spam

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

privacy policy