asp.net mvc下拉列表选择值覆盖问题的解决方案

时间:2011-01-31 19:53:27

标签: asp.net-mvc enums drop-down-menu

我编写了一个帮助方法,在我的asp.net MVC应用程序中显示我模型中的枚举,作为我视图中的下拉列表。

这是我的代码:

public static List<SelectListItem> CreateSelectItemList<TEnum>(object enumObj,
                                                            string defaultItemKey,
                                                            bool sortAlphabetically,
                                                            object firstValueOverride)
    where TEnum : struct
    {
        var values = (from v in (TEnum[])Enum.GetValues(typeof(TEnum))
                      select new
                      {
                          Id = Convert.ToInt32(v),
                          Name = ResourceHelpers.GetResourceValue(AppConstants.EnumResourceNamespace,
                                                                  typeof(TEnum).Name, v.ToString())
                      });


        return values.ToSelectList(e => e.Name,
                                               e => e.Id.ToString(),
                                               !string.IsNullOrEmpty(defaultItemKey) ? ResourceHelpers.GetResourceValue(AppConstants.EnumResourceNamespace, defaultItemKey) : string.Empty,
                                               enumObj,
                                               sortAlphabetically,
                                               firstValueOverride);

    }

这实际上会生成选择项目列表:

public static List<SelectListItem> ToSelectList<T>(
    this IEnumerable<T> enumerable,
    Func<T, string> text,
    Func<T, string> value,
    string defaultOption,
    object selectedVal,
    bool sortAlphabetically,
    object FirstValueOverride)
{

    int iSelectedVal = -1;

    if(selectedVal!=null)
    {
        try
        {
            iSelectedVal = Convert.ToInt32(selectedVal);
        }
        catch
        {
        }
    }

    var items = enumerable.Select(f => new SelectListItem()
    {
        Text = text(f),
        Value = value(f),
        Selected = (iSelectedVal > -1)? (iSelectedVal.ToString().Equals(value(f))) : false
    });

    #region Sortare alfabetica
    if (sortAlphabetically)
        items = items.OrderBy(t => t.Text);
    #endregion Sortare alfabetica

    var itemsList = items.ToList();

    Func<SelectListItem, bool> funct = null;
    string sValue = string.Empty;
    SelectListItem firstItem = null;
    SelectListItem overridenItem = null;
    int overridenIndex = 0;

    if (FirstValueOverride != null)
    {
        sValue = FirstValueOverride.ToString();

        funct = (t => t.Value == sValue);
        overridenItem = itemsList.SingleOrDefault(funct);
        overridenIndex = itemsList.IndexOf(overridenItem);

        if (overridenItem != null)
        {
            firstItem = itemsList.ElementAt(0);
            itemsList[0] = overridenItem;
            itemsList[overridenIndex] = firstItem;
        }
    }

    if(!string.IsNullOrEmpty(defaultOption))
        itemsList.Insert(0, new SelectListItem()
        {
            Text = defaultOption,
            Value = "-1"
        });

    return itemsList;
}

这是我称之为的方法:

        public static MvcHtmlString EnumDropDownList<TEnum>(this HtmlHelper htmlHelper, 
                                                        object enumObj,
                                                        string name,
                                                        string defaultItemKey,
                                                        bool sortAlphabetically,
                                                        object firstValueOverride,
                                                        object htmlAttributes)
    where TEnum : struct
    {
        return htmlHelper.DropDownList(name,
                                        CreateSelectItemList<TEnum>(enumObj,
                                                                defaultItemKey,
                                                                sortAlphabetically,
                                                                firstValueOverride), 
                                         htmlAttributes);
    }

现在我遇到了here所描述的问题 当我调用这个辅助方法并且输入的名称与属性的名称相同时,选择的值不会被选中。
那里描述的替代解决方案对我不起作用。唯一有效的解决方案是更改名称,而不是使用FormCollection使用模型绑定。 我不喜欢这种解决方法,因为我不能再使用ViewModel模式进行验证,我必须为每个枚举编写一些额外的代码。
我尝试编写一个自定义模型绑定器以某种方式对此进行补偿,但是当我开始操作时,没有一个我可以覆盖的方法被调用。

有没有办法在不使用FormCollection的情况下执行此操作? 我可以以某种方式拦截ASP.NET MVC,当它试图将值放入我的输入字段并使其选择正确的值时?

提前谢谢。

1 个答案:

答案 0 :(得分:0)

我现在有一个解决方案 以下是EnumDropDownList函数的代码,其他函数列在问题中:

public static MvcHtmlString EnumDropDownList(this HtmlHelper htmlHelper,
                                                   object enumObj,
                                                   string name,
                                                   string defaultItemKey,
                                                   bool sortAlphabetically,
                                                   object firstValueOverride,
                                                   object htmlAttributes)
{

    StringBuilder sbRezultat = new StringBuilder();

    int selectedIndex = 0;

    var selectItemList = new List<SelectListItem>();


    if (enumObj != null)
    {
        selectItemList = CreateSelectItemList(enumObj, defaultItemKey, true, null);

        var selectedItem = selectItemList.SingleOrDefault(item => item.Selected);
        if (selectedItem != null)
        {
            selectedIndex = selectItemList.IndexOf(selectedItem);

        }
    }

    var dict = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);


    TagBuilder tagBuilder = new TagBuilder("select");

    tagBuilder.MergeAttribute("name", name,true);

    bool bReadOnly = false;

    //special case for readonly
    if(dict.ContainsKey("readonly"))
    {
        //remove this tag it won't work the way mvc renders it anyway
        dict.Remove("readonly");
        bReadOnly = true;
    }

    //in case the style element is completed if the drop down is not readonly
    tagBuilder.MergeAttributes(dict, true);

    if (bReadOnly)
    {
        //add a small javascript to make it readonly and add the lightgrey style
        tagBuilder.MergeAttribute("onchange", "this.selectedIndex=" + selectedIndex + ";",true);
        tagBuilder.MergeAttribute("style", "background: lightgrey", true);
    }

    sbRezultat.Append(tagBuilder.ToString(TagRenderMode.StartTag));


    foreach (var option in selectItemList)
    {
        sbRezultat.Append(" <option value='");
        sbRezultat.Append(option.Value);
        sbRezultat.Append("' ");
        if (option.Selected)
            sbRezultat.Append("selected");
        sbRezultat.Append(" >");
        sbRezultat.Append(option.Text);


        sbRezultat.Append("</option>");
    }
    sbRezultat.Append("</select>");
    return new MvcHtmlString(sbRezultat.ToString());
}

我还写了For(EnumDropDownFor)类型的泛型函数:

public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
    Expression<Func<TModel, TProperty>> expression,
                                string defaultItemKey,
                                bool sortAlphabetically,
                                object firstValueOverride,
                                object htmlAttributes)
    where TProperty : struct
{
    string inputName = GetInputName(expression);

    object selectedVal = null;
    try
    {
        selectedVal = htmlHelper.ViewData.Model == null
            ? default(TProperty)
            : expression.Compile()(htmlHelper.ViewData.Model);
    }
    catch//in caz ca e ceva null sau ceva de genu'
    {
    }

    return EnumDropDownList(htmlHelper,
                            selectedVal,
                            inputName,
                            defaultItemKey,
                            sortAlphabetically,
                            firstValueOverride,
                            htmlAttributes);
}

和一些辅助方法:

public static string GetInputName<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
{
    if (expression.Body.NodeType == ExpressionType.Call)
    {
        MethodCallExpression methodCallExpression = (MethodCallExpression)expression.Body;
        string name = GetInputName(methodCallExpression);
        return name.Substring(expression.Parameters[0].Name.Length + 1);

    }
    return expression.Body.ToString().Substring(expression.Parameters[0].Name.Length + 1);
}

private static string GetInputName(MethodCallExpression expression)
{
    MethodCallExpression methodCallExpression = expression.Object as MethodCallExpression;
    if (methodCallExpression != null)
    {
        return GetInputName(methodCallExpression);
    }
    return expression.Object.ToString();
}