CheckBox Issue with MVC 3 WebGrid Paging & Sorting

March 19, 2012 at 8:52 PMWebsite Sorcery

I've encountered an issue with a CheckBox on a page along with the MVC 3 Web Grid. The issue occurs while paging and sorting, giving the following error:

The parameter conversion from type 'System.String' to type 'System.Boolean' failed. See the inner exception for more information.

Investigating the error further it seems the InnerException is:

{"true,false is not a valid value for Boolean."}

It took me a while to find a workable solution and I spent far too long searching on Google for any help on the matter. Fortunately I managed to get it working, and later on in this post I'll give you the answer. Firstly I'll discuss why it occurs.

An Html CheckBox will only post back a value when it is checked, and is ignored when false. This functionality is by design, for good or bad. To compensate for this, a hidden field is created with the same name, and this will post back either true or false. This will give the resulting QueryString:

Unchecked = false.

Checked = true,false.

Slightly odd, but that's the way it is. Let's look at some code.

View Code (cshtml):

@Html.CheckBox("MyBoolValue", @Model.MyBoolValue)

Controller Code:

bool myBoolValue = true;

if (Request.QueryString["MyBoolValue"] != null)
{
     myBoolValue = Request.QueryString["MyBoolValue"].Contains("true");
}

What this means is that a value of true,false will be set to true, or otherwise false.

I'd like to state a few more things in case the above doesn't work for you, and in reality my code was slightly different. I separated the name of the CheckBox completely from the value of the model, so my @Model.MyBoolValue was bound to a CheckBox called BoolValue which is what I read back from the query string. In my Controller method I do not use MVC binding, instead using the QueryString to read the value. The issue occurs with the MVC binding which is why I opted to sidestep it completely.

Another option is to create the CheckBox without the Html.CheckBox helper, coding it like so:

<input type="CheckBox" name="MyBoolValue" value="@Model.MyBoolValue"/>

Or you could go a step further and use Custom Model Binding.

Posted in: .NET Development | ASP.NET MVC | Razor

Tags: ,

ASP.NET MVC WebGrid - Problems Sorting Columns Bound to Dictionary / Enumeration Values

March 16, 2012 at 5:29 AMWebsite Sorcery

I have a WebGrid bound to a List of Dictionary values, with which I dynamically create columns for each Key/Value attribute. I set a default sort (defaultSort) to the first attribute in the Dictionary, and find this sorts perfectly within the WebGrid with ASC/DESC appearing correctly in the query string. The problem I seemed to have was none of the other columns sorted correctly, retaining the sort order of Ascending (ASC).

I did some research and found this is also an issue when binding to Enumeration (enum) values, and imagine it to be the case when binding to other collections too.

I found a fairly tidy way of fixing the issue, and that was to set the Grid.SortColumn on the WebGrid to the last sorted column in the query string. This can be done with little modifications to the Controller and the View.

Controller code:

ViewBag.LastSortedColumn = Request["sort"];

View (cshtml) code:

var grid = new WebGrid();

/* Create & Bind the WebGrid */

grid.SortColumn = (string)ViewBag.LastSortedColumn

What this code does it retain knowledge of the last sorted column, which is necessary as the WebGrid loses track of the last sorted column when it's not bound to a definitive property.

If this has helped you, then please take the time to click the Google+ and Share buttons. Thank you.

Posted in: .NET Development | ASP.NET MVC | Razor

Tags:

Setting the ASP.NET MVC WebGrid Table Header Background Color

March 13, 2012 at 4:06 AMWebsite Sorcery

I was attempting to style a WebGrid and ran into difficulties setting the background color of the table header. Take the following css for example:

.webgrid-header
{
    background-color: Red;
}

Would this set the header background to Red? No-siree! What you need to do is specify a th tag for the webgrid-header, like so:

.webgrid-header th
{
    background-color: Red;
}

Bob's your uncle!

For more information on styling the ASP.NET MVC WebGrid have a look at this post.

Posted in: ASP.NET MVC

Tags:

Styling the ASP.NET MVC WebGrid

March 12, 2012 at 10:26 PMWebsite Sorcery

As great as the ASP.NET MVC WebGrid is, it's very bland out of the box. Fortunately it's easy to style and this doesn't require much explanation. In two minutes flat you can have your WebGrid looking something like this:

ASP.NET MVC WebGrid Css Styling

Here's some starter code:

@grid.GetHtml(
        
            ...
        
        tableStyle: "webgrid",
        headerStyle: "webgrid-header",
        footerStyle: "webgrid-footer",
        alternatingRowStyle: "webgrid-alternating-row",
        selectedRowStyle: "webgrid-selected-row",
        rowStyle: "webgrid-row-style"
    )

Create a WebGrid.css style sheet and ensure you reference it from your _Layout.cshtml (or add the relevant styles to your Site.css if you prefer). Use the following as a start point which gives you the look and feel in the image above:

.webgrid
{
    width: 100%;
    margin: 0px;
    padding: 0px;
    border: 0px;
    border-collapse: collapse;
}

.webgrid a
{
    color: #000;
}

.webgrid-header
{
    padding: 0px 5px;
    text-align: center;
    border-bottom: 2px solid #739ace;
    height: 20px;
    border-top: 2px solid #D6E8FF;
    border-left: 2px solid #D6E8FF;
    border-right: 2px solid #D6E8FF;
}

.webgrid-header th
{
    background-color: #eaf0ff;
    border-right: 1px solid #ddd;
}

.webgrid-footer
{
    padding: 6px 5px;
    text-align: center;
    background-color: #e8eef4;
    border-top: 2px solid #3966A2;
    height: 25px;
    border-bottom: 2px solid #D6E8FF;
    border-left: 2px solid #D6E8FF;
    border-right: 2px solid #D6E8FF;
}

.webgrid-alternating-row
{
    height: 22px;
    background-color: #f2f2f2;
    border-bottom: 1px solid #d2d2d2;
    border-left: 2px solid #D6E8FF;
    border-right: 2px solid #D6E8FF;
}

.webgrid-row-style
{
    height: 22px;
    border-bottom: 1px solid #d2d2d2;
    border-left: 2px solid #D6E8FF;
    border-right: 2px solid #D6E8FF;
}

.webgrid-selected-row
{
    font-weight: bold;
}

Enjoy!

Posted in: ASP.NET MVC

Tags: ,

A Strongly Typed ASP.NET MVC WebGrid

March 12, 2012 at 9:58 PMWebsite Sorcery

I've been working on a project which requires server side paging and sorting to cope with potentially millions of records. I originally used the jQuery Tablesorter plugin with client side sorting and paging which worked well both functionally and aesthetically, but for the full bells and whistles project I stumbled upon the MSDN post Get the Most out of WebGrid in ASP.NET by Stuart Leeks.

Within in the midst of Stuart's post he creates a wrapper for the ASP.NET MVC WebGrid to allow Strong Typing. I debated the practicality of using strong typing with WebGrid which could be said to work better dynamically, but there are other benefits to strong typing which make this a decent little coding nugget.

Before getting to the code I'll give you a quick looky at how it works from the UI:

<div>
    @{
        var grid = new WebGrid<Product>(null, rowsPerPage: Model.PageSize, defaultSort: "Name");
        grid.Bind(Model.Products, rowCount: Model.TotalRows, autoSortAndPage: false);
    }
    @grid.GetHtml(
        columns: grid.Columns(
            grid.Column(
                "Name", format: @<text>@Html.ActionLink((string)item.Name,
                "Details", "Products", new { id = item.Id }, null)</text>),
            grid.Column("Description")
        )
    )
</div>

I created the WebGrid wrapper class in an Infrastructure folder in my website. It references another class called EnumerableExtensions which provides a useful SafeCast method to cast an IEnumerable. Here's the code:

Strongly Typed WebGrid Wrapper (WebGrid.cs)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Helpers;

namespace MyWebSite.Infrastructure
{
    /// <summary>
    /// Wrapper for System.Web.Helpers.WebGrid that preserves the item type from the data source
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class WebGrid<T> : WebGrid
    {
        /// <param name="source">Data source</param>
        /// <param name="columnNames">Data source column names. Auto-populated by default.</param>
        /// <param name="defaultSort">Default sort column.</param>
        /// <param name="rowsPerPage">Number of rows per page.</param>
        /// <param name="canPage">true to enable paging</param>
        /// <param name="canSort">true to enable sorting</param>
        /// <param name="ajaxUpdateContainerId">ID for the grid's container element. This enables AJAX support.</param>
        /// <param name="ajaxUpdateCallback">Callback function for the AJAX functionality once the update is complete</param>
        /// <param name="fieldNamePrefix">Prefix for query string fields to support multiple grids.</param>
        /// <param name="pageFieldName">Query string field name for page number.</param>
        /// <param name="selectionFieldName">Query string field name for selected row number.</param>
        /// <param name="sortFieldName">Query string field name for sort column.</param>
        /// <param name="sortDirectionFieldName">Query string field name for sort direction.</param>
        public WebGrid(IEnumerable<T> source = null, IEnumerable<string> columnNames = null, string defaultSort = null, int rowsPerPage = 10, bool canPage = true, bool canSort = true, string ajaxUpdateContainerId = null, string ajaxUpdateCallback = null, string fieldNamePrefix = null, string pageFieldName = null, string selectionFieldName = null, string sortFieldName = null, string sortDirectionFieldName = null)
            : base(source.SafeCast<object>(), columnNames, defaultSort, rowsPerPage, canPage, canSort, ajaxUpdateContainerId, ajaxUpdateCallback, fieldNamePrefix, pageFieldName, selectionFieldName, sortFieldName, sortDirectionFieldName)
        {
        }
        public WebGridColumn Column(string columnName = null, string header = null, Func<T, object> format = null, string style = null, bool canSort = true)
        {
            Func<dynamic, object> wrappedFormat = null;
            if (format != null)
            {
                wrappedFormat = o => format((T)o.Value);
            }
            WebGridColumn column = base.Column(columnName, header, wrappedFormat, style, canSort);
            return column;
        }
        public WebGrid<T> Bind(IEnumerable<T> source, IEnumerable<string> columnNames = null, bool autoSortAndPage = true, int rowCount = -1)
        {
            base.Bind(source.SafeCast<object>(), columnNames, autoSortAndPage, rowCount);
            return this;
        }
    }
}

Enumberable Extensions (Enumerable Extensions.cs)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace MyWebSite.Infrastructure
{
    public static class EnumerableExtensions
    {
        public static IEnumerable<TTarget> SafeCast<TTarget>(this IEnumerable source)
        {
            return source == null ? null : source.Cast<TTarget>();
        }
    }
}

To read more about the WebGrid I suggest reading Stuart Leeks' excellent post. Additionally, learn how to Style the ASP.NET MVC WebGrid.

Posted in: ASP.NET MVC

Tags: ,

Using conditional statements in an MVC 3 WebGrid column using Razor

March 8, 2012 at 3:01 AMWebsite Sorcery

You may have a requirement to create conditional statements within an MVC 3 WebGrid control. For example, you may have a column which displays a hyperlink if the user is an Administrator, or plain text to standard users. Perhaps you want green text for an item which is valid, or red if not? There are many situations where you want to render a grid cell based on a condition, and the possibilities are endless.

So how can you do this using the MVC 3 WebGrid Control? The solution, fortunately, is easy peasy.

Here's how:

 

@grid.GetHtml(
  columns: grid.Columns(
    grid.Column("Conditional Cell Sample", format: @<text>@if(true) { @Html.ActionLink("Go here", "Home") } else { @Html.ActionLink("Or here", "Away") }</text>))
)

 

Simply replace the @if(true) statement with your conditional logic, and define the two conditions as required.

What if you want a switch statement? You can do that too. You have to love the simplicity of Razor!

Posted in: .NET Development | ASP.NET MVC

Tags: