ASP.NET MVC MultiSelectList,选择的值未正确选择

And*_*gan 26 asp.net-mvc

我知道其他人已经问过这个问题了,但我对此完全感到困惑:

这将显示未选择任何值的下拉列表:

<%= Html.DropDownList("items", new MultiSelectList(Model.AvailableItems,
    "id", "name", Model.items), new { multiple = "multiple" })%>
Run Code Online (Sandbox Code Playgroud)

这将显示下拉列表,其中包含我正在传入的值(Model.items),就像我期望的那样:

<%= Html.DropDownList("somethingelse", new MultiSelectList(Model.AvailableItems,
    "id", "name", Model.items), new { multiple = "multiple" })%>
Run Code Online (Sandbox Code Playgroud)

但问题是,当我发布POST时,此项目现在被命名为"somethingelse".我知道我可以解决这个问题但是会发生什么?

Dar*_*rov 52

您的问题提供的上下文太少,但我会尝试展示一个完整的工作示例:

模型:

public class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class MyModel
{
    public IEnumerable<int> SelectedItemIds { get; set; }
    public IEnumerable<Item> AvailableItems { 
        get 
        {
            return new[] 
            {
                new Item { Id = 1, Name = "Item 1" },
                new Item { Id = 2, Name = "Item 2" },
                new Item { Id = 3, Name = "Item 3" },
            };
        } 
    }
}
Run Code Online (Sandbox Code Playgroud)

控制器:

[HandleError]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new MyModel
        {
            SelectedItemIds = new[] { 2, 3 }
        };
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(IEnumerable<int> selectedItemIds)
    {
        var model = new MyModel
        {
            // Important: Don't ever try to modify the selectedItemIds here
            // The Html helper will completely ignore it and use 
            // the POSTed values
            SelectedItemIds = selectedItemIds
        };
        return View(model);
    }
}
Run Code Online (Sandbox Code Playgroud)

视图:

<% using (Html.BeginForm()) { %>
    <%= Html.ListBoxFor(x => x.SelectedItemIds, 
        new MultiSelectList(Model.AvailableItems, "Id", "Name")) %>
    <input type="submit" value="GO" />
<% } %>
Run Code Online (Sandbox Code Playgroud)

请注意,Html.ListBoxFor如果要生成多重选择,则更适合.显然AvailableItems应该从存​​储库中获取属性.

  • 这应该是首选答案.关键是使用Html.ListBoxFor (6认同)

Jer*_*ero 21

您遇到的问题是使用Model.Items作为参数.代码

<%= Html.DropDownList("items", new MultiSelectList(Model.AvailableItems,
    "id", "name", Model.items), new { multiple = "multiple" })%>
Run Code Online (Sandbox Code Playgroud)

实际上并没有像你期望的那样工作.这是有效的,因为下拉列表的名称是"items".那是因为有一个名为"items"的表单参数发布回您的操作.该参数存储在动作的ViewState中(不要与ViewData混淆).Html.DropdownList()看到有一个ViewState参数名称与您命名的下拉列表相同,并使用该ViewState参数来计算选定的值.它完全忽略了您传入的Model.items.

如果有人能够解释无法覆盖默认行为的逻辑,那么我很乐意听到它.

那么,那是你的第一个问题.要解决这个问题,您只需将下拉列表重命名为其他内容 - 就像您在第二个示例中所做的那样.现在你的第二个问题发挥作用:所选项目的列表必须是简单对象的集合(我认为它实际上需要是一个IEnumerable,但我不是100%肯定).

DropDownList()方法将尝试将这些选定的值与AvailableItems集合中的Value进行匹配.如果它不能这样做,它将尝试匹配文本.

所以,试试看它是否有效

<%= Html.DropDownList("somethingelse", new MultiSelectList(Model.AvailableItems,
    "id", "name", Model.items.Select(c=> c.name)), new { multiple = "multiple" })%>
Run Code Online (Sandbox Code Playgroud)

祝好运

  • 如果您选择了多个项目,则此功能无效.它很简单地选择您选择的第一个并忽略其余部分.见达林的答案. (3认同)

Avi*_*iko 5

我有同样的问题,我使用自己的扩展方法来生成HTML并解决了问题

    public static MvcHtmlString ListBoxMultiSelectFor<TModel, TProperty>(
        this HtmlHelper<TModel> helper,
        Expression<Func<TModel, TProperty>> expression,
        IEnumerable<SelectListItem> selectList,
        object htmlAttributes)
    {
        return ListBoxMultiSelectFor(helper, expression, selectList, new RouteValueDictionary(htmlAttributes));
    }

    public static MvcHtmlString ListBoxMultiSelectFor<TModel, TProperty>(
        this HtmlHelper<TModel> helper,
        Expression<Func<TModel, TProperty>> expression,
        IEnumerable<SelectListItem> selectList,
        IDictionary<string, object> htmlAttributes)
    {
        string name = ExpressionHelper.GetExpressionText(expression);

        TagBuilder selectTag = new TagBuilder("select");
        selectTag.MergeAttributes(htmlAttributes);
        selectTag.MergeAttribute("id", name, true);
        selectTag.MergeAttribute("name", name, true);
        foreach (SelectListItem item in selectList)
        {
            TagBuilder optionTag = new TagBuilder("option");
            optionTag.MergeAttribute("value", item.Value);
            if (item.Selected) optionTag.MergeAttribute("selected", "selected");
            optionTag.InnerHtml = item.Text;
            selectTag.InnerHtml += optionTag.ToString();
        }

        return  new MvcHtmlString(selectTag.ToString());
    }
Run Code Online (Sandbox Code Playgroud)


Tho*_*der 5

实际上,如果您查看 MVC 源代码,则默认情况下此行为已烘焙到 DropDownListFor 中(搜索 allowMultiple: false)。解决方案是改用 ListBoxFor(您将在 MVC 源代码中看到,allowMultiple: true),这在 HTML 方面很有意义,两者都呈现为

<select ...>
   <option ...>
   <option ...>
   ...
</select>
Run Code Online (Sandbox Code Playgroud)

您不必按照上面的答案中的建议在模型上使用不同的属性,我通过简单地切换到 ListBoxFor 来实现这一点(CSS 从那里获取它):

@Html.ListBoxFor(model => model.SelectedCategories, 
   new MultiSelectList(Model.Categories, Model.SelectedCategories),
   new { multiple = "multiple" })
Run Code Online (Sandbox Code Playgroud)

即使使用 POST 并在出错时重新显示视图,它也能像魅力一样工作。