views:

237

answers:

2

Hi,

In my interface I have a list of text boxes, something like this : http://screencast.com/t/YjIxNjUyNmU

The number of textboxes is unknown as each of them is associated with a Template. In my page, the goal is to associate a number to some of those templates.

Here is a sample HTML code :

<%  // loop on the templates
    foreach(ITemplate template in templates)
    {
        // get the content from the input dictionary
        int val;
        content.TryGetValue(template.Id, out val);
        // convert it as a string
        string value = ((val > 0) ? val.ToString() : string.Empty);

        // compute the element name/id (for dictionary binding)
        string id = ??????????
        string name = ??????????????
%>
        <label for="<%= name %>"><%= template.Name %></label>
        <input type="text" id="<%= id %>" name="<%= name %>" value="<%= value %>" />
        <br />
<%  }
%>

What I expect, in my controller, is to get a IDictionary where the first int is the template ID , and the other is the count given by the user.

Here is what I want :

public ActionResult Save(int? id, Dictionary<int, int> countByTemplate)

I tried a lot of things but nothing worked. I tried to read the sources but it's a maze, and I'm getting a headhache trying to get information about model binding.

Questions :

  • is there a good ressource on how the modelbinding works ? I'd like someting exhaustive, I'm tired of the 84093043 blogs that talk about a given specific example.
  • how can I build my HTML, using to get a IDictionary (or even a IDictionary in my controller's action ?

Thanks a lot for your help

+1  A: 

Information on how to write your input elements for binding to arrays, dictionaries, and other collections can be found at http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx.

Levi
+1  A: 

Ok... Thanks to Levi I was able to get on a solution. Not the cleaner one, but it works.

The HTML should be written this way :

int counter = 0; // loop on the templates foreach(ITemplate template in templates) { // get the value as text int val; content.TryGetValue(template.Id, out val); var value = ((val > 0) ? val.ToString() : string.Empty);

    // compute the element name (for dictionary binding)
    string id = "cbts_{0}".FormatMe(template.Id);
    string dictKey = "cbts[{0}].Key".FormatMe(counter);
    string dictValue = "cbts[{0}].Value".FormatMe(counter++);

%> " value="<%= template.Id %>" /> " name="<%= dictValue %>" value="<%= value %>" /> "><%= template.Name %>
<% } %> I had to add a hidden field to store the value. I introduced a 'fake' counter just to loop over the dictionary te way ASP.Net MVC wants it. As a result I got a dictionary filled with my values and '0' when the textbox is empty.

Another problem appeared : the ModelState was considered not valid because "a value is required". I don't want my values to be required, but looking at the modelbinder code, I did not found a way to tell the binder that a value is NOT required.

So I tricked the ModelState in my controller, removing all error, like this :

public ActionResult Save(int? id, Dictionary<int, int> cbts)
{
    // clear all errors from the modelstate
    foreach(var value in this.ModelState.Values)
        value.Errors.Clear();

Well... I effectively got a solution, but the HTML is kind of ugly now, and counterintuitive (using an index to loop over a non-indexed collection ??). And I need to trick each time I'll use this kind of binding to have it all work properly.

So I will now open a new post to make dictionary binder something better. Here it is : http://stackoverflow.com/questions/2911198/asp-net-mvc-2-better-modelbinding-for-dictionaryint-int

EDIT - There is a cleaner solution, thanks to Pavel Chuchuva.

In the controller code, use a nullable int as value for the dictionary. A bit more code to add, but much cleaner.

public ActionResult Save(int? id, Dictionary<int, int?> cbts)
{
    // this is our final dictionary<int, int>
    Dictionary<int, int> cbtsFinal = new Dictionary<int, int>();
    // loop on the dicitonary with nullable values
    foreach(var key in cbts.Keys)
    {
        // if we have a value
        if(cbts[key].HasValue)
            // then put it in the final dictionary
            cbtsFinal.Add(key, cbts[key].Value);
    }
Mose
Try `public ActionResult Save(int? id, Dictionary<int, int?> cbts)`
Pavel Chuchuva
Thanks, this is better !
Mose