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: 26841

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

Recent Comments

hosein ey 2/25/2015 1:56 PM
In response to ASP.NET MVC 5 with EF 6 - Working With Files
tnx for this article do you think next version of entityframework support's the sql server and ?...

Saywer Ford 2/25/2015 5:15 AM
In response to Optimising ASP.NET Web Pages Sites - Bundling And Minification
Great Article. I did everything right and working fine. How about page specific js files. Lets I...

Saravanan 2/24/2015 10:54 AM
In response to Optimising ASP.NET Web Pages Sites - Bundling And Minification
Hi, Great article about the Bundling and minification. Regards the caching as you mentioned the...

Justin 2/24/2015 10:43 AM
In response to Scheduled Tasks In ASP.NET With Quartz.Net
I'm having the same problem as Ingmar. Everything is working fine locally in Visual Studio, but I it...

Logan Mudlo 2/23/2015 4:59 PM
In response to WebMatrix - A First Application
Is there a way to prevent the automatic close on a Database.Open() call?...

Mog0 2/23/2015 11:16 AM
In response to ASP.NET MVC 5 with EF 6 - Working With Files
Probably a silly question but why did you disable cascade delete and implement it yourself?...

Jose-Maria 2/19/2015 3:29 PM
In response to Migrating From Razor Web Pages To ASP.NET MVC 5 - Views and Controllers
Just great ! Many thanks Mike....

Satyabrata Mohapatra 2/19/2015 12:02 PM
In response to Migrating From Razor Web Pages To ASP.NET MVC 5 - Views and Controllers
This is great!!...

Ryan Helmoski 2/19/2015 10:24 AM
In response to Conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value
Thank you!...

Harsha 2/19/2015 8:27 AM
In response to iTextSharp - Adding Text with Chunks, Phrases and Paragraphs
Hi, Can text area be created so that I can type text in the area in pdf document? We have Add text...