ASP.NET MVC Binding to a Dictionary

ASP.NET MVC Binding to a dictionary

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/

ASP.NET Core Binding to a dictionary

Yes it's also true for ASP.NET Core MVC (and/or Razor Pages).

You can see the following is a simple demo in asp.net core.

Class:

 public class Command
{
[Key]
public string ID { get; set; }
public string Name { get; set; }
public Dictionary<string, string> Values { get; set; }
}

Action:

 [HttpPost]
public IActionResult Test(Command command)
{
return View();
}

View:

@model Command
<form asp-action="Test">
<input type="text" name="Id" />
<input type="text" name="Name" />
<input type="hidden" name="Values[0].Key" value="Apple" />
<input type="text" name="Values[0].Value" />
//....
<input type="submit" value="Click" />
</form>

Result:
Sample Image

MVC 5: dictionary in model bound to a series of checkboxes in view?

In order to bind a dictionary, you need to have post values like: DictionaryProperty[N].Key and DictionaryProperty[N].Value. As a result, your Razor code would need to look something like:

@Html.HiddenFor(m_ => m_.DictionaryTest[0].Key)
@Html.CheckBoxFor(m_ => m_.DictionaryTest[0].Value, new { @class = "form-control" })

@Html.HiddenFor(m_ => m_.DictionaryTest[1].Key)
@Html.CheckBoxFor(m_ => m_.DictionaryTest[1].Value, new { @class = "form-control" })

@Html.HiddenFor(m_ => m_.DictionaryTest[2].Key)
@Html.CheckBoxFor(m_ => m_.DictionaryTest[2].Value, new { @class = "form-control" })

However, if your keys are integers, a dictionary is overkill. Just use a simple list. Then, your Razor code would work as-is.

MVC model not binding to dictionary

The model binder treats the dictionary as a collection, if you imagine the dictionary as an IEnumerable<KeyValuePair<string, IEnumerable<OccasionObject>>> it is easy to understand why it isn't bound.

What @Html.CheckBoxFor(m=>m.occasionList[s].FirstOrDefault(ev=>ev.ID == o.ID).isAttending); is generating is:

<input type="checkbox" name="occasionList[0].Value.isAttending" ../>

so the Key is missing.

Try this:

@Html.Hidden("occasionList.Index", s)
@Html.CheckBoxFor(m=>m.occasionList[s].FirstOrDefault(ev=>ev.ID == o.ID).isAttending);
@Html.HiddenFor(m=>m.occasionList[s].Key)

The first hidden is because you potentially will have your indexes out of order, and explicitly providing an ".Index" is the only way to have the model binder work under those circumstances.

Here's another resource that describes model binding to collections.

Bind to a Dictionary of Lists of Lists of Bools with a strongly-typed MVC view by using Checkboxes

Alright, I got it.

I tried using a Tuple<int, List<List<bool>>> instead of a Dictionary<int, List<List<bool>>>. That failed, apparently because the Tuple doesn't have a 0-parameter constructor.

Then, I tried using a custom class that had two properties, an int and a List<List<bool>>. I got that to work after some fiddling, and once that worked I was able to reverse engineer it and get the Dictionary to work.

Here's the working version (same view model and iidToData as before):

...

@{
string machineID;
Submission subm;
tblSignatures sig;
ItemSearchResult result;

int kvInd = 0;

var dc = new CloudDataContext();
}

...

foreach( KeyValuePair<int, List<ItemSearchResult>> kv in ViewBag.iidToData ) {

...

<input type="hidden" name="@("Model.SelectedResults[" + kvInd + "].Key")" value="@kv.Key" />

for(int isr = 0; isr < kv.Value.Count(); isr++) {

...

@if(result.Keytbls.Any()) {

for( int i = 0; i < result.Keytbls.Count(); i++ ) {

...

<td>@Html.CheckBox( "Model.SelectedResults[" + kvInd + "].Value[" + isr + "][" + i + "]", Model.SelectedResults[ kv.Key ][ isr ][ i ] )</td>

...

} else {
<tr><td><input type="hidden" name="@("Model.SelectedResults[" + kvInd + "].Value[" + isr + "]")" /></td></tr>
}

...
}

kvInd++;

}

...

So, the index used on the hidden input for the dictionary key isn't the key, but is instead an enumeration of the KeyValue pairs, 0th one, 1st one, 2nd one, so on. This is the same index used to indicate the value later on.

That leads us to another funny part. The name for the checkbox needs to have Model.DictionaryName[enumerationIndex].Value in it to indicate that we are setting the value for that indexed KeyValue pair.

Also, the html input element produced by that helper function always has a value of true, and the second hidden input always has a value of false. The "checked" attribute indicates whether the value of the input checkbox is sent to the default binder or not, i.e. whether it gets the value of "true, false" or just "false". This is then properly interpreted by the binder as a bool value.

Finally, the hidden input in the else block at the end adds an empty List<List<bool>> for entries that had no matching search results. The .Value pairs with the earlier .Key to indicate a full KeyValue pair to be added to the dictionary. Then, when the binder sees Model.Dictionary[index].Value[index] without ever seeing Model.Dictionary[index].Value[index][index], it makes an empty list but doesn't add any values.

So that was unnecessarily complicated, but now hopefully others can use Dictionaries with Collection values in their ViewModels.

ASP.NET MVC Binding to a Dictionary in View

use "your list property"[index].value.id
example
if you have:

public IList<KeyValuePair<int,string>> Properties { get; set; }

you should write in view:

for (int i = 0; i < Model.Properties.Count; i++)
{
@Html.EditorFor(item=>Model.Properties[i].Value)
}

updated:

 @for (var i=0; i < Model.Translations.Count; i++) { 
<div id="tabs-@(i)">
<div class="@i == 0 ? 'active' : ''" id="@Model.Translations[i].Value.CultureCode">@Model.Translations[i].Value.Title</div>
@Html.TextBoxFor(m => Model.Translations[i].Value.Title);
@Html.TextBoxFor(m => Model.Translations[i].Value.FullDescription);
@Html.TextBoxFor(m => Model.Translations[i].Value.PreviewDescription); }
</div>

</div>
</div>

MVC Model Binding to Dictionary by Key with HTML Helper

Ha! I stopped fighting the tide and wrote a Helper for this, easier than I thought:

public static MvcHtmlString TextBoxForFilterDictionary(this HtmlHelper helper, IDictionary<string, FilterObject> filters, string fieldName, object htmlAttributes = null)
{
FilterObject filter;
if (!filters.TryGetValue(fieldName, out filter))
{
filter = new FilterObject();
}
string nameAttribute = String.Format("Model.Filters[{0}].Filter", fieldName);
MvcHtmlString html = helper.TextBox(nameAttribute, filter.Filter, htmlAttributes);
return html;
}

And in the HTML:

  <%: Html.TextBoxForFilterDictionary(Model.Filters, "matchId")%>
<!-- a bunch of other formatting HTML -->
<%: Html.TextBoxForFilterDictionary(Model.Filters, "matchName")%>
<!-- and so on -->

My generic ViewModel now carries everything the list needs for sorting, paging, and filtering, no filter Dictionary initialization required and nary a weakly typed object in sight.

Worth the little extra effort!



Related Topics



Leave a reply



Submit