asp.net-mvc-3mvccontribmvccontrib-grid

MvcContrib grid and checkboxes


Lets say I render a Checkbox:

@Html.CheckboxFor(x => x.Checked) // Checked is true by default

ASP will turn that as:

<input checked="checked" data-val="true" data-val-required="The field is required." id="Checked" name="Checked" type="checkbox" value="true" />
<input name="Checked" type="hidden" value="false" />

Since ASP outputs two inputs with the same name for a Checkbox, we also get two GET parameters in the URL when submitting the form with the checkbox:

http://...?Checked=true&Checked=false

Lets say I'm also using MvcContrib for displaying a table with sorting.

When I sort a column, MvcContrib is unable to understand the duplicate GET parameters, and instead of writing ?Checked=true&Checked=false, it writes ?Checked=true%2Cfalse, which can't be parsed to a bool by MVC3. The error message after sorting is:

String was not recognized as a valid Boolean.

Has anyone else experienced this problem with the MvcContrib grid?


Solution

  • Okay, think I have come up with the solution:

    Create your own HtmlTableGridRenderer:

    public class CustomTableGridRenderer<TViewModel> : HtmlTableGridRenderer<TViewModel> where TViewModel : class
    {
        protected override void RenderHeaderText(GridColumn<TViewModel> column)
        {
            if (IsSortingEnabled && column.Sortable)
            {
                string sortColumnName = GenerateSortColumnName(column);
    
                bool isSortedByThisColumn = GridModel.SortOptions.Column == sortColumnName;
    
                var sortOptions = new GridSortOptions
                {
                    Column = sortColumnName
                };
    
                if (isSortedByThisColumn)
                {
                    sortOptions.Direction = (GridModel.SortOptions.Direction == SortDirection.Ascending)
                        ? SortDirection.Descending
                        : SortDirection.Ascending;
                }
                else //default sort order
                {
                    sortOptions.Direction = column.InitialDirection ?? GridModel.SortOptions.Direction;
                }
    
                var routeValues = CreateRouteValuesForSortOptions(sortOptions, GridModel.SortPrefix);
    
                //Re-add existing querystring
                foreach (var key in Context.RequestContext.HttpContext.Request.QueryString.AllKeys.Where(key => key != null))
                {
                    if (!routeValues.ContainsKey(key))
                    {
                        routeValues[key] = Context.RequestContext.HttpContext.Request.QueryString[key];
                    }
                }
    
                var link = HtmlHelper.GenerateLink(Context.RequestContext, RouteTable.Routes, column.DisplayName, null, null, null, routeValues, null);
                RenderText(link);
            }
            else
            {
                base.RenderHeaderText(column);
            }
        }
    }
    

    ... and just replace

                    if(! routeValues.ContainsKey(key))
                    {
                        routeValues[key] = Context.RequestContext.HttpContext.Request.QueryString[key];
                    }
    

    ... with routeValues[key] = Context.RequestContext.HttpContext.Request.QueryString[key];

    And use your new rendering like so:

    @Html.Grid()...RenderUsing(new CustomTableGridRenderer())