Well I’ll be darned.
I just spent half an hour today debugging what I thought was a strange oddity today with an ASP.NET MVC Html.DropDownList(), specifically within it’s SelectList element on a simple “add / edit” detail screen on an ASP.NET MVC web form.
I had this code:
[sourcecode language='html']
<%= Html.DropDownList("ModuleMessageType.ModuleID", new SelectList(ViewData.Model.Modules, "ID", "Name", ViewData.Model.FormType == AppHelper.FormType.Add && ViewData.Model.ModuleMessageType.ModuleID == 0 ? Request["moduleid"].ToInt() : ViewData.Model.ModuleMessageType.ModuleID), "-- Select --", new { tabindex = 2, title = "Modules" })%>
Basically stating that when launching the page in "Add" mode, you should select the module in the drop down list based on the ID you receive in the QueryString, for "Edit" mode just use the one in Model State. But now in reality, what was happening is it was selecting correctly in "Edit" but was just being ignored in "Add", and for the life of me I could not figure out why. It was just sitting there stating "-- Select --". I definitely had a "moduleid" in the QueryString, it definitely was lowercase and well, it definitely should have been marked with "selected".
So I thought, let's remove the complexity of the thing and just specifically tell the SelectList which ID I wish to select by passing it the value of 4. I definitely had an ID of 4, which I confirmed in viewing html source, but alas, still a failure using the below:
[sourcecode language='html']
<%= Html.DropDownList("ModuleMessageType.ModuleID", new SelectList(ViewData.Model.Modules, "ID", "Name", 4, "-- Select --", new { tabindex = 2, title = "Modules" })%>
This was getting ridiculous. I'm basically at the bottom-line here of what is within my control in order to mark the item as selected, so I started scouring through some of the ASP.NET MVC RC 2 Source Code (intrepidly heading in like I knew what I was looking for) but it turns out I did, as for the Html.DropDownList() I noticed there appeared to be a priority-based implementation for the drop down list's decision on what item should be selected; and of course unfortunately, what you choose to enter into the selectedValue property appears to be the LAST port of call from what I could see.
The priority appears to be as follows:
- Value in Model State (Viewdata-supplied);
- Fallback to: value in selectedValue property (Parameter-supplied)
I gathered this from SelectExtensions.cs:117 of the ASP.NET MVC RC 2 Source Code:
object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(name, typeof(string[])) : htmlHelper.GetModelStateValue(name, typeof(string));
// If we haven't already used ViewData to get the entire list of items then we need to
// use the ViewData-supplied value before using the parameter-supplied value.
if (!usedViewData) {
if (defaultValue == null) {
defaultValue = htmlHelper.ViewData.Eval(name);
}
}
Interesting huh. I personall feel this is a bit outrageous; the end-user's specification should always dictate the final operation I think (Consider CSS for example and classes vs. style). I do not know if this is by design of the ASP.NET MVC team, or a simple bug. Obviously they wished to implement an order of precidence, that part is clear, but to object what the user has entered into selectedValue due to a value being picked up elsewhere? Seems a bit odd to me. I'd be interested to hear what others think.
So after understanding more about what's going on here and realising most likely what was occuring is that the value held in ViewData.Model.ModuleMessageType.ModuleID ("0") is the one chosen as the selectedValue, not the one I actually entered into selectedValue - thus resulting in no selection at all in my drop down list.
Considering this, I re-worked my code a little (and I'm happier with the end result anyway) plus with a small change to my controller which sets my ModuleID property to Request["moduleid"] (in instances whereby we're adding record) and changing my Html.DropDownList() slightly with removal entirely of the selectedValue property as follows it all worked correctly:
[sourcecode language='html']
<%= Html.DropDownList("ModuleMessageType.ModuleID", new SelectList(ViewData.Model.Modules, "ID", "Name"), "-- Select --", new { tabindex = 2, title = "Modules" })%>
You may be surprised as to how I can remove the declaration to the selectedValue property all together, but if you consider it, it is not needed. For both "add" and "edit" mode of my page it is gathered from my ViewData now, which based on rules above is exactly where the drop down list pulls it from.
Problem solved!
4 Comments
You are voted!
Track back from WebDevVote.com
This is interesting.
Good work, thanks.
Great work man. This helped me a lot.
One Trackback/Pingback
[...] Unexpected Behaviour with ASP.NET MVC SelectList SelectedValue [...]
Post a Comment