Quantcast
Viewing all articles
Browse latest Browse all 28

Magic @Html.DropDownList

This is observed in MVC5

When one of properties of model is associated with another list, it is normal practice that  list is saved in the ViewData as a SelectList , then in the View, the same list is retrieved back and passed to the html helper @Html.DropDownList(modelpropertyname,the SelectList)

What puzzled me is the second parameter, the SelectList could be ignored or even is attempted to be retrieved in a wrong way,  the List could still be loaded correctly.

In both scenarios, they end up with calling  @Html.DropDownList(modelpropertyname) with no list provided. There is some magic that is not obvious to us in @Html.DropDownList unless you had a look at the source code.

From here is a very confusing example:

In the edit controller, the SelectLists are created

this.ViewBag.GenreId = new SelectList(this.db.Genres, "GenreId", "Name", album.GenreId);

this.ViewBag.ArtistId = new SelectList(this.db.Artists, "ArtistId", "Name", album.ArtistId);

In the view they are consumed like this

<div>

@Html.LabelFor(model => model.Artist)

</div>

<div class="editor-field">

@Html.DropDownList("ArtistId", (SelectList)ViewData["Artists"])

@Html.ValidationMessageFor(model => model.ArtistId)

</div>

&nbsp;

<div class="editor-label">

@Html.LabelFor(model => model.Genre)

</div>

<div class="editor-field">

@Html.DropDownList("GenreId", (SelectList)ViewData["Genres"])

@Html.ValidationMessageFor(model => model.GenreId)

</div>

And the result is the lists are loaded correctly in run time. What is going on? The Lists are saved in ViewBag, as GenreId, ArtistId, but retrieved by ViewData["Genres"], ViewData["Artists"], I understand that ViewBag is actually saved in ViewData, but should not we use ViewData["GenreId"] and ViewData["ArtistId"]?

Retrieve ViewData with a wrong key is terribly wrong, but  how the list can be loaded regardless, when I changed code

<div class="editor-field">

@Html.DropDownList("ArtistId")

@Html.ValidationMessageFor(model => model.ArtistId)

</div>

The list still loads, this tells me that in @Html.DropDownList, the helper has a way to get to the list someway, this is the magic I am talking about here, so when you pass in wrong ViewData key, the list will still be empty, the list searching mechanism of Html.DropDownList kicks in, it is trying to load ViewData[propertyname] , if no list is provided, and no ViewData under key propertyname, then you have trouble, the error message is

The ViewData item that has the key ‘ArtistId’ is of type ‘System.Int32′ but must be of type ‘IEnumerable<SelectListItem>’.

And at the stack top is

at System.Web.Mvc.Html.SelectExtensions.GetSelectData(HtmlHelper htmlHelper, String name)

this where I found the magic stuff:

</pre>
private static IEnumerable<SelectListItem> GetSelectData(this HtmlHelper htmlHelper, string name)

{

object obj = (object) null;

if (htmlHelper.ViewData != null)

obj = htmlHelper.ViewData.Eval(name);

if (obj == null)

{

throw new InvalidOperationException(string.Format((IFormatProvider) CultureInfo.CurrentCulture, MvcResources.HtmlHelper_MissingSelectData, new object[2]

{

(object) name,

(object) "IEnumerable<SelectListItem>"

}));

}

else

{

IEnumerable<SelectListItem> enumerable = obj as IEnumerable<SelectListItem>;

if (enumerable != null)

return enumerable;

throw new InvalidOperationException(string.Format((IFormatProvider) CultureInfo.CurrentCulture, MvcResources.HtmlHelper_WrongSelectDataType, (object) name, (object) obj.GetType().FullName, (object) "IEnumerable<SelectListItem>"));

}

}

This tells that, if you put the list in ViewData using the same key as propertyname, then Tml.DrowpDownList() can resolve the list without being fed of the list.


Viewing all articles
Browse latest Browse all 28

Trending Articles