Setup:
I have scaffolded a controller using MvcScaffolding.
For a property, Model.IdCurrencyFrom, the scaffolding created an Html.DropDownListFor:
@Html.DropDownListFor(model => model.IdCurrencyFrom,
((IEnumerable<FlatAdmin.Domain.Entities.Currency>)ViewBag.AllCurrencies).Select(option => new SelectListItem {
Text = (option == null ? "None" : option.CurrencyName),
Value = option.CurrencyId.ToString(),
Selected = (Model != null) && (option.CurrencyId == Model.IdCurrencyFrom)
}), "Choose...")
This works fine, both with new records, or editing existing ones.
Problem:
There are only 3 currencies, AR$, US$ and GB£. So, instead of a drop down list, I want a ListBox.
So I changed the above to:
@Html.ListBoxFor(model => model.IdCurrencyFrom,
((IEnumerable<FlatAdmin.Domain.Entities.Currency>)ViewBag.AllCurrencies).Select(option => new SelectListItem {
Text = (option == null ? "None" : option.CurrencyName),
Value = option.CurrencyId.ToString(),
Selected = (Model != null) && (option.CurrencyId == Model.IdCurrencyFrom)
}))
I now get an ArgumentNullException, Parameter name: source, but only when editing an existing record. Creating new records, this works fine.
Questions:
What is happening?!
Nothing has changed. Switching back to DropDownListFor and it all works fine. Switching to ListBox (as opposed to ListBoxFor) and I get the error.
The model is not null (like I said, it works fine with the DropDownListFor)… and I’ve run out of ideas.
I’ve checked the source of the HTML helpers, it was a fun exercise.
TL;DR;
The problem is that ListBoxFor is for multiple selection and it expects an enumerable Model property. Your Model property (
model.IdCurrencyFrom) is not an enumerable that’s why you get the exception.Here are my findings:
The ListBoxFor method will render a
selectelement withmultiple="multiple"attribute, always. It is hard coded inSystem.Web.Mvc.Html.SelectExtensionsSo maybe you anyway don’t want to allow for the user multiple currencies…
Your problem starts when this ListBoxHelper tries to get the default value from your model property:
It works for DropDownList because it passes false to
allowMultiplewhen callingSelectInternal.Because your
ViewData.ModelStateis empty (because there were no validation occurred in your controller before) thedefaultValuewill benull. ThendefaultValuegets initialized with your model’s default value (your casemodel.IdCurrencyFromisintI guess) so it will be0. :We are getting close to the exception 🙂 Because as I mentioned ListBoxFor only support multiple selection, so it tries to handle
defaultValueasIEnumbrable:And in the second line there is your ArgumentException because
defaultValuesisnull.Because it expects
defaultValueto be enumerable and because string is enumerable. If you change the the type ofmodel.IdCurrencyFromtostringit will work. But of course you will have multiple selection on the UI but you will only get the first selection in your model.