73

I'm trying to bind dictionary values within MVC.

Within the action I have:

model.Params = new Dictionary<string, string>();

model.Params.Add("Value1", "1");
model.Params.Add("Value2", "2");
model.Params.Add("Value3", "3");

and within the view I have:

@foreach (KeyValuePair<string, string> kvp in Model.Params)
{ 
<tr>
  <td>
    <input type="hidden" name="Params.Key" value="@kvp.Key" />
    @Html.TextBox("Params[" + kvp.Key + "]")
  </td>
</tr>
}

But the view doesn't display the initial values, and when the form is submitted the Params property is null?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
user644344
  • 885
  • 1
  • 7
  • 7

3 Answers3

99

In ASP.NET MVC 4, the default model binder will bind dictionaries using the typical dictionary indexer syntax property[key].

If you have a Dictionary<string, string> in your model, you can now bind back to it directly with the following markup:

<input type="hidden" name="MyDictionary[MyKey]" value="MyValue" />

For example, if you want to use a set of checkboxes to select a subset of a dictionary's elements and bind back to the same property, you can do the following:

@foreach(var kvp in Model.MyDictionary)
{
    <input type="checkbox" name="@("MyDictionary[" + kvp.Key + "]")"
        value="@kvp.Value" />
}
Colin Mackay
  • 18,736
  • 7
  • 61
  • 88
Ant P
  • 24,820
  • 5
  • 68
  • 105
63

you should take a look to this post from scott hanselman: http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx

The default binder just understand dictionaries in the format:

params[0].key = kvp.key
params[0].value = kvp.value

The index of the param must be sequential, starting from 0 and without any gaps. The current helpers don't support this, so you should create the form input fields by yourself.

you can of course implement your own binder, like this one: http://siphon9.net/loune/2009/12/a-intuitive-dictionary-model-binder-for-asp-net-mvc/

Jokin
  • 4,188
  • 2
  • 31
  • 30
28

Building on @AntP's answer, there is an even less verbose way, letting MVC do more of the work (at least with TextBoxFor() on a Dictionary<string, string> - I haven't tried CheckBoxFor() on a Dictionary<xxx, bool>):

@foreach(var kvp in Model.MyDictionary)
{
    @Html.Label(kvp.Key);
    @Html.TextBoxFor(m => m.MyDictionary[kvp.Key]);
}
Sphinxxx
  • 12,484
  • 4
  • 54
  • 84
  • 1
    `CheckBoxFor` would work with a `Dict` but it wouldn't work in any scenario where you want to bind back specific key/value pairs where the value is not just a bool - to do that you need to build the markup yourself (as in my example). – Ant P Dec 19 '14 at 11:39
  • Why should this only work for booleans? I got this to work for Dict as well, and I don't see why it shouldn't work for other data types as well. – Kai Weber May 16 '17 at 11:55
  • @KaiWeber - This probably works for any `Dictionary` (in fact, `` is what I use in my answer). However, I haven't tested it with anything else. What I think Ant P says is that it only works for typed dictionaries (`Dictionary<>`), and not non-generic collections like [IDictionary](https://msdn.microsoft.com/en-us/library/system.collections.idictionary.aspx) – Sphinxxx May 16 '17 at 19:03
  • 2
    I just tried this using a `Dictionary`, which didn't work. If I use a `Dictionary` instead, it works. Anybody has an explanation for this? – nkaenzig May 25 '19 at 14:54
  • 2
    My experience was that the value can be anything, but the key had to be a `string` instead of an `int`. Maybe the model binder confuses it with a positional argument. – Arithmomaniac Nov 28 '19 at 08:26