键入问题编写一个简短的HtmlHelper DropDownList重载方法

时间:2016-06-15 21:03:40

标签: c# asp.net-mvc html-helper

我正在尝试重载DropDownListFor HtmlHelper方法。

通常,参数'htmlAttributes'将是一个匿名对象。如果htmlAttributes还不是RouteValueDictionary,我会根据我要添加的自定义逻辑将其转换为此类型,以便根据需要修改集合,例如根据“禁用”的值处理禁用控件参数值,仅作为一个例子。

    public static MvcHtmlString DropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression, 
            IEnumerable<SelectListItem> selectList, 
            string optionLabel, 
            object htmlAttributes,
            bool disabled)
    {
        if (!(htmlAttributes is RouteValueDictionary))
        {
            htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
        }
        else
        {
            htmlAttributes = (RouteValueDictionary)htmlAttributes;
        }

        if (disabled)
        {
            //manipulate the htmlAttributes collection here                     
        }
        return htmlHelper.DropDownListFor(expression, selectList, optionLabel, htmlAttributes);
    }

当我在最后一行调用“本机”System.Web.Mvc.Html.DropDownListFor时遇到问题。当它期望Anonymous对象时,它无法正确处理类型为“RouteAttributes”的htmlAttributes。因此,我得到的HTML没有正确地将routingAttributes集合的名称/值html属性转换为nanme / value HTML属性对。我明白了(注意键和值属性):

例如,当我用它调用它时:

@Html.DropDownListFor(model => model.FrequencyCode, Model.FrequencyCodes.ToListOfSelectListItem(Model.FrequencyCode), "", new { @class = "form-control" }, true)

我明白了:

<input data-val="true" data-val-required="Frequency is required" id="FrequencyCode" name="FrequencyCode" type="hidden" value="" />
<select Count="1" Keys="System.Collections.Generic.Dictionary`2+KeyCollection[System.String,System.Object]" Values="System.Collections.Generic.Dictionary`2+ValueCollection[System.String,System.Object]" id="FrequencyCode" name="FrequencyCode">
<option value=""></option>
<option value="A">ANNUAL</option>
<option value="M">MONTHLY</option>
<option value="Q">QUARTERLY</option>
</select>

似乎解决这个问题的一种方法是,如果我可以将RouteDictionary类型的对象转换回匿名对象。我该怎么做呢?还有更好的方法吗?

1 个答案:

答案 0 :(得分:1)

解决此问题的最佳方法是进行多次重载,就像内置HtmlHelper方法一样(请DropDownListFor()方法引用source code)。

public static MvcHtmlString DropDownListFor<TModel, TProperty>(
        this HtmlHelper<TModel> htmlHelper, 
        Expression<Func<TModel, TProperty>> expression, 
        IEnumerable<SelectListItem> selectList, 
        string optionLabel, 
        IDictionary<string, object> htmlAttributes,
        bool disabled)

public static MvcHtmlString DropDownListFor<TModel, TProperty>(
        this HtmlHelper<TModel> htmlHelper, 
        Expression<Func<TModel, TProperty>> expression, 
        IEnumerable<SelectListItem> selectList, 
        string optionLabel, 
        object htmlAttributes,
        bool disabled)

然而,生成一个禁用的<select>元素没有多大意义 - 你生成了许多额外的不必要的html,并且禁用的控件不提交那里的值,因此模型绑定可能会失败,如果方法是绑定到值类型的属性,或具有验证属性,ModelState将无效。更好的方法是仅显示所选文本值和隐藏输入(假设您要发布值)。

public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string optionLabel, object htmlAttributes, bool disabled)
{
    if (disabled)
    {
        StringBuilder html = new StringBuilder();
        // add a hidden input
        html.Append(htmlHelper.HiddenFor(expression).ToString());
        // Get the display text
        ModelMetadata metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
        string value = metaData.Model.ToString();
        var displayText = selectList.Where(x => x.Value == value).Select(x => x.Text).FirstOrDefault();
        TagBuilder div = new TagBuilder("div");
        div.AddCssClass("readonly");
        div.InnerHtml = displayText;
        html.Append(div.ToString());
        return MvcHtmlString.Create(html.ToString());
    }
    return htmlHelper.DropDownListFor(expression, selectList, optionLabel, htmlAttributes);
}

然后使用css将.readonly样式设置为<input /><select>元素,如果这是你想要的。