Issue
My controller code
public ActionResult Create(string id)
{
ViewBag.ALLO_ID = new SelectList(_unitOfWork.AllocationMRepository.Get(), "ALLO_ID", "ALLO_ID", id);
ViewBag.TERR_ID = new SelectList(_unitOfWork.TerrListRepository.Get().OrderBy(m => m.TERR_ID), "TERR_ID", "TERR_ID");
ViewBag.ITEM_ID = new SelectList(_unitOfWork.SampleInfo.Get(), "ITEM_ID", "ITEM_ID");
return View();
}
In my view
<div class="editor-field">
@Html.DropDownList("ALLO_ID", String.Empty)
@Html.ValidationMessageFor(model => model.ALLO_ID)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.TERR_ID, "Territory Name")
</div>
<div class="editor-field">
@Html.DropDownList("TERR_ID", String.Empty)
@Html.ValidationMessageFor(model => model.TERR_ID)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.ITEM_ID, "Item Name")
</div>
<div class="editor-field">
@Html.DropDownList("ITEM_ID", String.Empty)
@Html.ValidationMessageFor(model => model.ITEM_ID)
</div>
How these drop drop lists are created? It is not very much clear to me. I want to know how does it work.
Solution
Grab the source and build it on your machine. Seriously. I refer to my copy frequently.
To answer your specific question, i.e. “how is the framework creating dropdown lists?” It’s iterating through an enumerable collection and creating a string.
The MVC libraries define numerous extension methods for the HtmlHelper
class. You can easily define your own. Of course, you can build other helper classes, but HtmlHelper
is readily available for just this purpose.
Stepping through a few layers of overloads, we find:
private IHtmlString BuildDropDownList(string name, string defaultOption, IEnumerable<SelectListItem> selectList,
object selectedValue, IDictionary<string, object> htmlAttributes) {
var modelState = ModelState[name];
if (modelState != null) {
selectedValue = selectedValue ?? ModelState[name].Value;
}
selectedValue = ConvertTo(selectedValue, typeof(string));
if (selectedValue != null) {
var newSelectList = new List<SelectListItem>(from item in selectList select new SelectListItem(item));
var comparer = StringComparer.InvariantCultureIgnoreCase;
var selectedItem = newSelectList.FirstOrDefault(item => item.Selected || comparer.Equals(item.Value ?? item.Text, selectedValue));
if (selectedItem != default(SelectListItem)) {
selectedItem.Selected = true;
selectList = newSelectList;
}
}
TagBuilder tagBuilder = new TagBuilder("select") {
InnerHtml = BuildListOptions(selectList, defaultOption)
};
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("name", name, replaceExisting: true);
tagBuilder.GenerateId(name);
// If there are any errors for a named field, we add the css attribute.
AddErrorClass(tagBuilder, name);
return tagBuilder.ToHtmlString(TagRenderMode.Normal);
}
It’s really not that complicated. The extension methods which use the “For” suffix are a bit more sophisticated.
@Html.LabelFor(model => model.TERR_ID, "Territory Name")
Notice the lambda expression? The framework doesn’t actually execute it; instead, it parses it as metadata and uses it to get the fully-qualified name of the item. This can then be used to retrieve various information from the original property, such as DataAnnotations
pertaining to validation.
The crux of the implementation is in private static MvcHtmlString SelectInternal(...)
, contained in System.Web.Mvc.Html.SelectExtensions
. The integration with model state and validation is noteworthy.
Answered By – Tim M.
Answer Checked By – Gilberto Lyons (BugsFixing Admin)