The upside for this is that Asp.Net code is opensource, so I took a sneak peak in the upcomming MVC 3 source code to see what the DropDownListFor code looked like.
Lets cut to the chase.
for all kinds of select html variations MVC uses the SelectListItem class. We just need to subcass it and add a new string property named GroupKey. Here is the code fot that.
public class GroupedSelectListItem : SelectListItem
{
public string GroupKey { get; set; }
}
Then i took the helper overloads for DropDownListFor, renamed them to DropDownGroupListFor, and changed the selectlist parameter type to IEnumerable<GroupedSelectListItem>.
public static MvcHtmlString DropDownGroupListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<GroupedSelectListItem> selectList)
Finaly we just need to change a for loop inside the "GetSelectData" private method in order to take optgroup into consideration. Here is how it looks like.
foreach (var group in selectList.GroupBy(i => i.GroupKey)) {
listItemBuilder.AppendLine(string.Format("<optgroup label=\"{0}\">", group.Key));
foreach (GroupedSelectListItem item in group) {
listItemBuilder.AppendLine(ListItemToOption(item));
}
listItemBuilder.AppendLine("</optgroup>");
}
As requested here is a usage example. Say we have the following list of vehicles in our Model.
.. and the entity class looks like this
Now in our View we add the following statement.
<%: Html.DropDownGroupListFor(model => model.VehicleId,
Lookups.Vehicles.Select(v => new GroupedSelectListItem() {
GroupKey = v.Group,
Text = v.Description,
Value = v.Id.ToString()
}),
"---- select Vehicle ----")%>
Note that Lookups is a static helper class that gets the collection we need to bind to the dropdownlist.
public class Lookups
{
public static ICollection<Vehicle> Vehicles {
get {
ICollection<Vehicle> vehicles = null;
using (DBEntities context = new DBEntities()) {
vehicles = context.Vehicles.ToList();
}
return vehicles;
}
}
}
After that, we use a simple linq selection in order to transform our source collection in the form of List of GroupedSelectListItem. This is the format that the DropDownGroupListFor helper understands
If everything went according to plan then we expect to see something like this in the output:
You can download the code here (Updated: February 2011)
Can you help with a usage example? I am trying:
ReplyDelete<%: Html.DropDownGroupListFor(m => m.SelectedItem, Model.ListOfStuff, new {size = 10}) %>
and I get "type arguments cannot be inferred from the usage"
Otherwise, thanks for the great example.
Hi and thanks for the comment. You are absolutely right. I forgot to add a usage example. Check out the blog in a few minutes. I have it updated.
ReplyDeleteNote that I also found a minor bug in the attached code. So It will be updated too.
Cheers!
I'm not sure what bug you found in the code, but I think I found a small one. This is hard, because I can't post html in the comment. The line:
ReplyDeleteoptgroup label=\"{0}\"", group.Key));
Shouldn't it be:
optgroup label=\"{0}\">", group.Key));
So the optgroup tag gets closed? In my program, the optgroup tag was not getting closed and the first item did not show up.
Billy C.
@Billy
ReplyDeleteYes that was the minor fix I was talking about. I double ckecked it and as far as I can see I included it in the updated file.