3

I am using ASP.Net MVC with C#. I have a model which has a member for filter criteria. This member is a IList>. The key contains value to display and the value tells if this filter is selected or not. I want to bind this to bunch of checkboxes on my view. This is how I did it.

<% for(int i=0;i<Model.customers.filterCriteria.Count;i++) { %>
<%=Html.CheckBoxFor(Model.customers.filterCriteria[i].value)%>&nbsp;
<%=Model.customers.filterCriteria[i].key%>
<% } %>

This displays all checkboxes properly. But when I submit my form, in controller, I get null for filtercriteria no matter what I select on view.

From this post I got a hint for creating separate property. But how will this work for IList..? Any suggestions please?

Community
  • 1
  • 1
Anil Soman
  • 2,443
  • 7
  • 40
  • 64
  • am i missing something but it strikes me as odd that you're outputting two checkboxes for each criterion? Why not use a foreach loop? One last question, how are you binding your model in the controller? are you using automatic model binding or are you inspecting the form collection? – WickyNilliams Apr 15 '11 at 12:02
  • @mr.nicksta: my mistake..corrected... I am binding my view to controller by using Inherits atttribute in @Page directive. Is this what you are asking? – Anil Soman Apr 15 '11 at 12:05
  • What you're talking about is how to strongly type your view, whereas i'm talking about is the action on the controller, could you post code for that please? That would give insight as to how you are attempting to pull the values from the HTTP request – WickyNilliams Apr 15 '11 at 12:23

1 Answers1

6

The problem with the KeyValuePair<TKey, TValue> structure is that it has private setters meaning that the default model binder cannot set their values in the POST action. It has a special constructor that need to be used allowing to pass the key and the value but of course the default model binder has no knowledge of this constructor and it uses the default one, so unless you write a custom model binder for this type you won't be able to use it.

I would recommend you using a custom type instead of KeyValuePair<TKey, TValue>.

So as always we start with a view model:

public class Item
{
    public string Name { get; set; }
    public bool Value { get; set; }
}

public class MyViewModel
{
    public IList<Item> FilterCriteria { get; set; }
}

then a controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View(new MyViewModel
        {
            FilterCriteria = new[] 
            {
                new Item { Name = "Criteria 1", Value = true },
                new Item { Name = "Criteria 2", Value = false },
                new Item { Name = "Criteria 3", Value = true },
            }
        });
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        // The model will be correctly bound here
        return View(model);
    }
}

and the corresponding ~/Views/Home/Index.aspx view:

<% using (Html.BeginForm()) { %>
    <%= Html.EditorFor(x => x.FilterCriteria) %>
    <input type="submit" value="OK" />
<% } %>

and finally we write a customized editor template for the Item type in ~/Views/Shared/EditorTemplates/Item.ascx or ~/Views/Home/EditorTemplates/Item.ascx (if this template is only specific to the Home controller and not reused):

<%@ Control 
    Language="C#" 
    Inherits="System.Web.Mvc.ViewUserControl<AppName.Models.Item>" 
%>
<%= Html.CheckBoxFor(x => x.Value) %>
<%= Html.HiddenFor(x => x.Name) %>
<%= Html.Encode(Model.Name) %>

We have achieved two things: cleaned up the views from ugly for loops and made the model binder successfully bind the checkbox values in the POST action.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Thanks @Darin! I will try this and let you know... for now +1 – Anil Soman Apr 17 '11 at 08:27
  • @Darin.. After implementing this, I got below string on my view instead of checkboxes.. I tried to google, but could not get clue.. any idea? AutoInternalCustomerSecurityOutboundSystem – Anil Soman Apr 18 '11 at 05:37
  • @Anil Soman, this is a strong indication that your editor template is not being picked up. This happens because it might not be placed in the correct location or have the correct name. So as you can see in my example the `FilterCriteria` view model property is of type `IList` so the editor template is `~/Views/Shared/EditorTemplates/Item.ascx`. You can replace `Item` with the name of the type you are using but you need to respect this convention for the template to be picked up. – Darin Dimitrov Apr 18 '11 at 05:44
  • Thanks, also [this](http://khalidabuhakmeh.com/submitting-a-dictionary-to-an-asp-net-mvc-action) , post using `Dictionary` can help someone. – Shaiju T Jan 25 '17 at 10:21