The WebGrid Helper - Making Selections

The WebGrid helper, introduced via ASP.NET Web Pages, provides a means for displaying tabular data easily. This article examines how to enable selection within the WebGrid at row level.

The scenario is a common one - you want to display summary data within a table on a web page, and you want to provide your user with a means by which they can view more detailed information on a particular item in the table, or make changes, such as editing or deleting an item. With the Web Forms GridView, this is easily accomplished by ticking boxes that automatically enable selection, editing and deleting. It's also pretty easy to do with the WebGrid. First, let's take a look at a typical WebGrid which displays some data from a database containing information on Books:

Here's the code for the page:

@{
    Layout = "/Shared/_Layout.cshtml";
    var db = Database.Open("Books");
    var sql = "Select BookId, Title, ISBN, Description, FirstName, LastName, Category From Books " + 
               "Inner Join Authors on Books.AuthorId = Authors.AuthorId " + 
               "Inner Join Categories on Books.CategoryId = Categories.CategoryId";
    var data =  db.Query(sql);
    var grid = new WebGrid(data);
}
<div id="grid">
@grid.GetHtml(
    tableStyle : "table",
    alternatingRowStyle : "alternate",
    headerStyle : "header",
    columns: grid.Columns(

        grid.Column(
            columnName : "Author",
            format: @<text>@item.FirstName @item.LastName</text>
            ),
        grid.Column(
            columnName : "Title",
            format: @<text>@item.Title</text>
            ),
        grid.Column(
            columnName : "Category"
            )
    )
)
</div>

If you are not familiar with how to set the basic properties of a WebGrid through Razor code, you should review my previous article on the WebGrid helper. If you want to allow your users to edit items, you ideally want to provide a column which contains a selection link like this:

The WebGridRow object has a GetSelectLink method which provides the means to do this. All you need to do is add another column to the grid and call the method. Here's the revised grid code with an additional column and a selectedRowStyle value set:

@grid.GetHtml(
    tableStyle : "table",
    alternatingRowStyle : "alternate",
    selectedRowStyle: "selected",
    headerStyle : "header",    
    columns: grid.Columns(
        grid.Column(
            header:"", 
            format:@<text>@item.GetSelectLink("Edit")</text>
            ),
        grid.Column(
            columnName : "Author",
            format: @<text>@item.FirstName @item.LastName</text>
            ),
        grid.Column(
            columnName : "Title",
            format: @<text>@item.Title</text>
            ),
        grid.Column(
            columnName : "Category"
            )
    )
)

Notice that the header property in the new column has been set to an empty string, and a string has been passed in to the GetSelectLink method ("Edit"). This string is the text that will appear on each row. You do not have to pass in a string. It is optional, but the default value is "Select" if you don't provide one yourself.

Great! But what happens if you click a link? At the moment, nothing happens except that the page refreshes, and the selected row takes on the colour specified in the css file. However, you might notice that the URL has changed a little in the browser eg:

The selected row is indicated in the querystring. The querystring key value "row" is set by default, but you can change this by applying your own value to the selectionFieldName property in your WebGrid constructor:

var grid = new WebGrid(data, selectionFieldName: "custom");
This would change the URL to read something like http://localhost:39612/Default.cshtml?custom=4. The presence of a selectionFieldName in the querystring (indicating that someone clicked the selection link) is tested using the WebGrid.HasSelection property:
@if(grid.HasSelection){
    //do something
}

In this example, I chose to use RenderPage to bring in the contents of another file, which contains a form for editing a book entry. In order to populate that form with the correct book details, I needed to pass the selected book to the partial page. This is done using the PageData dictionary mechanism:

@if(grid.HasSelection){
    var book = grid.SelectedRow;
    @RenderPage("~/Partials/EditBook.cshtml", new { Book = book } )
}

The code in EditBook.cshtml uses PageData to fetch the correct book, plus some other stuff from the database to populate the form ready for editing:

@{
    
    var bookid = Page.Book.BookId;
    var db = Database.Open("Books");    
    var authors = db.Query("SELECT AuthorId, FirstName + ' ' + LastName AS AuthorName FROM Authors");
    var categories = db.Query("SELECT CategoryId, Category FROM Categories");
    var sql = "SELECT BookId, Title, ISBN, Description, CategoryId, AuthorId, DatePublished FROM Books WHERE BookId = @0";
    var book = db.QuerySingle(sql, bookid);

}


    <form id="edit-book-form" action="@Href("~/Methods/EditBook")">
        <fieldset>
            <legend>Edit Book</legend>
         <div class="row">
                <span class="label"><label for="title">Title:</label></span>
                <input type="text" name="title" id="title" size="50" value="@book.Title" />
            </div>
            <div class="row">
                <span class="label"><label for="isbn">ISBN:</label></span>
                <input type="text" name="isbn" id="isbn" size="20" value="@book.ISBN" />
            </div>
            <div class="row">
                <span class="label"><label for="description">Description:</label></span>
                <textarea cols="50" rows="8" name="description" id="description">@book.Description</textarea>
            </div>
            <div class="row">
                <span class="label"><label for="authorId">Author:</label></span>
                <select name="authorId" id="authorId">
                    <option value="">-- Select Author --</option>
                @{
                    foreach(var author in authors){
                        if(author.AuthorId == book.AuthorId){
                            <option value="@author.AuthorId" selected="selected">@author.AuthorName</option>
                        } else {
                            <option value="@author.AuthorId">@author.AuthorName</option>
                        }
                    }
                }
                </select>
            </div>
            <div class="row">
                <span class="label"><label for="categoryId">Category:</label></span>
                <select name="categoryId" id="categoryId">
                    <option value="">-- Select Category --</option>
                @{
                    foreach(var category in categories){
                        if(category.CategoryId == book.CategoryId){
                            <option value="@category.CategoryId" selected="selected">@category.Category</option>
                        } else {
                            <option value="@category.CategoryId">@category.Category</option>
                        }        
                    }
                }
                </select>
            </div>
            <div class="row">
                <span class="label"><label for="datePublished">Date Published:</label></span>
                <input type="text" id="datePublished" name="datePublished" value="@book.DatePublished.ToString("d")" />
            </div>    
            <div class="row">
                <span class="label">&nbsp;</span>
                <input type="submit" id="submit" name="submit" value="Submit" />
            </div> 
            </fieldset>
        </form>

The resulting page looks like this:

There are alternatives to making rows selectable. For instance, you can use jQuery as illustrated in another of my previous articles. You would need to use a jQuery approach is you enable AJAX paging or sorting on the grid, as no querystring is generated when you click a link in the grid cells. Or you could simply format the text to include a hyperlink to another page:

grid.Column(
    header:"", 
    format:@<a href="/Edit/@item.BookId">Edit</a>)

If you wanted to provide more than one selection column, say an Edit and a Delete option, you would need to use this approach or the jQuery one, as currently, you cannot set the querystring selectFieldName property on a per column basis, so there will be no easy way to distinguish whether someone clicked the Edit link or the Delete link.

The full code is available in a download here.

Date Posted:
Last Updated:
Posted by:
Total Views to date: 31305

6 Comments

- Max-B

It is possible to have an Edit and Delete link for the same row?

- Max-B

Oh sorry! I don't read all find my way on this! Thanks for the good posts!!

- Boyd Campbell

If I repopulate the grid with a brand new set of data, the WebGrid is keeping a handle on the previously selected row. IE: When I make a run to get a totally new set of data, all of the "Selected" related API's still have a row selected. This is causing an issue in my "if ( grid.HasSelection )" since it should not be hit at all after a new set of data.

How do I "reset" the state of the grid back to an unselected state?

- ChrisL

Is it possible to make multiple selections with the WebGrid?

- Rex

Is there any possible way to set an "id" value to
format: @<text>@item.FirstName @item.LastName</text>

For example: format: @<text id = "someID">@item.FirstName @item.LastName</text>

I noticed that if i attempt the above example i will get an error. For what i am doing, i do not want render another page. I want to display a jquery modal dialog to the click event upon clicking "Edit/Select" and load some database information into the dialog using jquery ajax. To edit the data, i want to select the information using the row ID as this would be pivotal in loading the correct information. So Please let me know if there is a way to set an id to and get row information from the web grid.

If i used: format: @<a id = "someID" href = "#">"Edit"</a>
I will lose the row id information that is required for me to load the correct data on jquery ajax call

NOTE: I know this may be a difficult way around things and there are obviously much easier solutions, but bear with me and let me know if there are any solutions that meet my requirements.

Best Regards

- Mike

Rex,

Take a look at the following article: Inline Editing With The WebGrid. I apply dynamic Id values to rows in that.

Recent Comments

Justin Kusuma 8/4/2015 7:01 AM
In response to Posting Data With jQuery AJAX In ASP.NET Razor Web Pages
Hi Mike, thanks for replying... I'm looking for more elegant way to provide user feedback. As now...

Arthur 8/3/2015 4:54 PM
In response to Getting the identity of the most recently added record
Thanks Mike, your site is a mine of useful info :)...

MRP 8/3/2015 4:06 AM
In response to Entity Framework Recipe: Many To Many Relationship On The Same Table
my question is about HashSet and ICollection ! why you use this type and why not user Ilist<> and ?...

Arbaz 8/1/2015 3:39 PM
In response to Scheduled Tasks In ASP.NET With Quartz.Net
Hey Mike, What If I want to set a schedule weekly or every Sunday to run. What would the code for I...

Emma 7/31/2015 3:29 AM
In response to How To Send Email In ASP.NET MVC
As mentioned by John Noble, there seems to be something missing in regards to: return...

Satyabrata Mohapatra 7/30/2015 6:42 AM
In response to Implementing Google's EU End User Consent Policy
Thanks.... Learned a lot....

Jaswanth 7/29/2015 1:31 PM
In response to Displaying Google Analytics Data in ASP.NET
Hi Mike, My application uses MVC 1. I need to fetch the data of pages which has top views. I tried...

Sergey 7/28/2015 6:48 AM
In response to Custom TagHelpers in ASP.NET MVC 6
Thanks a lot!...

Dome 7/27/2015 7:55 PM
In response to Posting Data With jQuery AJAX In ASP.NET Razor Web Pages
Hi, I've just created a new project with WebApi & MVC, then created a controller with the following...

Ted Driver 7/26/2015 5:44 PM
In response to How To Send Email In ASP.NET MVC
Thanks for the great example Mike! I noticed in my implementation of your code that if I put some in...