在Html.DropDownList的<option>下添加html类标记

我一直在寻找如何在我的html.dropdownlist上添加HTML类标记的答案。 这里是代码

<%: Html.DropDownList("PackageId", new SelectList(ViewData["Packages"] as IEnumerable, "PackageId", "Name", Model.PackageId))%> 

我想添加select元素下的选项类,以便我可以使用这个链接select:

 <select id="category"> <option value="1">One</option> <option value="2">Two</option> </select> <select id="package"> <option value="1" class="1">One - package1</option> <option value="2" class="1">One - package2</option> <option value="3" class="2">Two - package1</option> <option value="4" class="2">Two - package2</option> </select> $("#series").chained("#mark"); 

我已经为DropDownlistFor扩展方法做了这个,而不是你使用的DropDownList,但你可以自己弄清楚。 这些东西大多是从MVC源文件复制/粘贴的。 你可以在这里find来源。

 public class ExtendedSelectListItem : SelectListItem { public object htmlAttributes { get; set; } } public static partial class HtmlHelperExtensions { public static MvcHtmlString ExtendedDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<ExtendedSelectListItem> selectList, string optionLabel, object htmlAttributes) { return SelectInternal(htmlHelper, optionLabel, ExpressionHelper.GetExpressionText(expression), selectList, false /* allowMultiple */, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); } private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, string optionLabel, string name, IEnumerable<ExtendedSelectListItem> selectList, bool allowMultiple, IDictionary<string, object> htmlAttributes) { string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name); if (String.IsNullOrEmpty(fullName)) throw new ArgumentException("No name"); if (selectList == null) throw new ArgumentException("No selectlist"); object defaultValue = (allowMultiple) ? GetModelStateValue(htmlHelper, fullName, typeof(string[])) : GetModelStateValue(htmlHelper, fullName, 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 (defaultValue == null) defaultValue = htmlHelper.ViewData.Eval(fullName); if (defaultValue != null) { IEnumerable defaultValues = (allowMultiple) ? defaultValue as IEnumerable : new[] { defaultValue }; IEnumerable<string> values = from object value in defaultValues select Convert.ToString(value, CultureInfo.CurrentCulture); HashSet<string> selectedValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase); List<ExtendedSelectListItem> newSelectList = new List<ExtendedSelectListItem>(); foreach (ExtendedSelectListItem item in selectList) { item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text); newSelectList.Add(item); } selectList = newSelectList; } // Convert each ListItem to an <option> tag StringBuilder listItemBuilder = new StringBuilder(); // Make optionLabel the first item that gets rendered. if (optionLabel != null) listItemBuilder.Append(ListItemToOption(new ExtendedSelectListItem() { Text = optionLabel, Value = String.Empty, Selected = false })); foreach (ExtendedSelectListItem item in selectList) { listItemBuilder.Append(ListItemToOption(item)); } TagBuilder tagBuilder = new TagBuilder("select") { InnerHtml = listItemBuilder.ToString() }; tagBuilder.MergeAttributes(htmlAttributes); tagBuilder.MergeAttribute("name", fullName, true /* replaceExisting */); tagBuilder.GenerateId(fullName); if (allowMultiple) tagBuilder.MergeAttribute("multiple", "multiple"); // If there are any errors for a named field, we add the css attribute. ModelState modelState; if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState)) { if (modelState.Errors.Count > 0) { tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName); } } tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(name)); return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal)); } internal static string ListItemToOption(ExtendedSelectListItem item) { TagBuilder builder = new TagBuilder("option") { InnerHtml = HttpUtility.HtmlEncode(item.Text) }; if (item.Value != null) { builder.Attributes["value"] = item.Value; } if (item.Selected) { builder.Attributes["selected"] = "selected"; } builder.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(item.htmlAttributes)); return builder.ToString(TagRenderMode.Normal); } } 

这是@ john-landheer解决scheme的一个小改进版本。

事情改善了:

  • GetModelStateValue()问题GetModelStateValue()修复
  • 添加了DropDownList()扩展方法
  • 不显眼的validation属性将被渲染,就像他们应该

     using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Web; using System.Web.Mvc; namespace App.Infrastructure.Helpers { public class ExtendedSelectListItem : SelectListItem { public object HtmlAttributes { get; set; } } public static class ExtendedSelectExtensions { internal static object GetModelStateValue(this HtmlHelper htmlHelper, string key, Type destinationType) { System.Web.Mvc.ModelState modelState; if (htmlHelper.ViewData.ModelState.TryGetValue(key, out modelState)) { if (modelState.Value != null) { return modelState.Value.ConvertTo(destinationType, null /* culture */); } } return null; } public static MvcHtmlString ExtendedDropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<ExtendedSelectListItem> selectList) { return ExtendedDropDownList(htmlHelper, name, selectList, (string)null, (IDictionary<string, object>)null); } public static MvcHtmlString ExtendedDropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<ExtendedSelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes) { return ExtendedDropDownListHelper(htmlHelper, null, name, selectList, optionLabel, htmlAttributes); } public static MvcHtmlString ExtendedDropDownListHelper(this HtmlHelper htmlHelper, ModelMetadata metadata, string expression, IEnumerable<ExtendedSelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes) { return SelectInternal(htmlHelper, metadata, optionLabel, expression, selectList, false, htmlAttributes); } public static MvcHtmlString ExtendedDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<ExtendedSelectListItem> selectList, string optionLabel, object htmlAttributes) { if (expression == null) throw new ArgumentNullException("expression"); ModelMetadata metadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, htmlHelper.ViewData); return SelectInternal(htmlHelper, metadata, optionLabel, ExpressionHelper.GetExpressionText(expression), selectList, false /* allowMultiple */, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); } private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata, string optionLabel, string name, IEnumerable<ExtendedSelectListItem> selectList, bool allowMultiple, IDictionary<string, object> htmlAttributes) { string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name); if (String.IsNullOrEmpty(fullName)) throw new ArgumentException("No name"); if (selectList == null) throw new ArgumentException("No selectlist"); object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(fullName, typeof(string[])) : htmlHelper.GetModelStateValue(fullName, 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 (defaultValue == null) defaultValue = htmlHelper.ViewData.Eval(fullName); if (defaultValue != null) { IEnumerable defaultValues = (allowMultiple) ? defaultValue as IEnumerable : new[] { defaultValue }; IEnumerable<string> values = from object value in defaultValues select Convert.ToString(value, CultureInfo.CurrentCulture); HashSet<string> selectedValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase); List<ExtendedSelectListItem> newSelectList = new List<ExtendedSelectListItem>(); foreach (ExtendedSelectListItem item in selectList) { item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text); newSelectList.Add(item); } selectList = newSelectList; } // Convert each ListItem to an <option> tag StringBuilder listItemBuilder = new StringBuilder(); // Make optionLabel the first item that gets rendered. if (optionLabel != null) listItemBuilder.Append( ListItemToOption(new ExtendedSelectListItem() { Text = optionLabel, Value = String.Empty, Selected = false })); foreach (ExtendedSelectListItem item in selectList) { listItemBuilder.Append(ListItemToOption(item)); } TagBuilder tagBuilder = new TagBuilder("select") { InnerHtml = listItemBuilder.ToString() }; tagBuilder.MergeAttributes(htmlAttributes); tagBuilder.MergeAttribute("name", fullName, true /* replaceExisting */); tagBuilder.GenerateId(fullName); if (allowMultiple) tagBuilder.MergeAttribute("multiple", "multiple"); // If there are any errors for a named field, we add the css attribute. System.Web.Mvc.ModelState modelState; if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState)) { if (modelState.Errors.Count > 0) { tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName); } } tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(fullName, metadata)); return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal)); } internal static string ListItemToOption(ExtendedSelectListItem item) { TagBuilder builder = new TagBuilder("option") { InnerHtml = HttpUtility.HtmlEncode(item.Text) }; if (item.Value != null) { builder.Attributes["value"] = item.Value; } if (item.Selected) { builder.Attributes["selected"] = "selected"; } builder.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(item.HtmlAttributes)); return builder.ToString(TagRenderMode.Normal); } } } 

这是内置在ASP.NET MVC中的DropDownList助手不可能实现的。 因此,如果您需要这样做,您将不得不编写自己的帮手。 你可以看看使用TagBuilder生成选项的ASP.NET MVC的源代码,你可以在你的自定义实现中附加任何属性。 另一个较不优雅的解决scheme是手动循环查看视图中的数据集并生成单个选项元素。

我想到的第一件事是JQuery在这里。 你可以用下面的代码轻松做到这一点:

 $("#bla").find("option").addClass("poo"); 

我写了一个简单的修改html的包装:

  public static MvcHtmlString DisableFirstItem(MvcHtmlString htmlString) { return new MvcHtmlString( htmlString.ToString() .Replace("<option value=\"Unknown\">", "<option disabled value=\"Unknown\">") ); } 

然后用这个辅助函数包装我的DropDownListFor:

  @Html.Raw(MyHtmlHelpers.DisableFirstItem( Html.DropDownListFor(m => m.Instrument, new SelectList(ReflectionHelpers.GenerateEnumDictionary<OrderInstrument>(true), "Key", "Value", Model.Instrument), new { @class = "form-control" }) )) 

如果你愿意,你显然可以使帮助function更复杂。