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

时间:2010-09-17 18:33:55

标签: asp.net-mvc

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

这会显示没有选择值的下拉列表:

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

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

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

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

6 个答案:

答案 0 :(得分:51)

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

型号:

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" },
            };
        } 
    }
}

控制器:

[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);
    }
}

查看:

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

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

答案 1 :(得分:21)

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

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

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

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

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

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

所以,试试看它是否有效

<%= Html.DropDownList("somethingelse", new MultiSelectList(Model.AvailableItems,
    "id", "name", Model.items.Select(c=> c.name)), new { multiple = "multiple" })%>
祝你好运

答案 2 :(得分: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());
    }

答案 3 :(得分:3)

实际上,如果你看一下MVC源代码,默认情况下这个行为会被挂到DropDownListFor中(搜索allowMultiple:false)。解决方案是使用ListBoxFor(您将在MVC源代码中看到,allowMultiple:true),这在HTML方面很有意义,两者都呈现为

<select ...>
   <option ...>
   <option ...>
   ...
</select>

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

@Html.ListBoxFor(model => model.SelectedCategories, 
   new MultiSelectList(Model.Categories, Model.SelectedCategories),
   new { multiple = "multiple" })

就像魅力一样,即使使用POST并在错误时重新显示视图。

答案 4 :(得分:1)

当使用ListBoxForMultiSelectList作为ViewBag属性时,我遇到了类似的问题。 @ jero的answer帮助我弄清楚,如果ViewBag字段和模型字段之间存在命名冲突,则所选值无法正确显示。

如果我执行了以下操作,则这些项目不会显示为已选中。

//Controller
ViewBag.SelectionIds = new MultiSelectView(possibleValues, "Value", "Name", selectedValues);

//View
@Html.ListBoxFor(model => model.SelectionIds, (MultiSelectList)ViewBag.SelectionIds, new { @class = "form-control" })

如果我将其更改为以下内容,则表示没问题。

//Controller
//Changed ViewBag.SelectionIds to ViewBag.SelectionIdList
ViewBag.SelectionIdList = new MultiSelectView(possibleValues, "Value", "Name", selectedValues);

//View
@Html.ListBoxFor(model => model.SelectionIds, (MultiSelectList)ViewBag.SelectionIdList, new { @class = "form-control" })

答案 5 :(得分:0)

您可以转到&#34;项目&#34;的值用这个

<HttpPost()> _
    Function Edit(ByVal crm_cliente As crm_cliente, ByVal form As FormCollection) As ActionResult
        If ModelState.IsValid Then
            Dim items As String
            crm_cliente.usuario_modifico = "ejmorales"
            crm_cliente.fecha_modifico = Date.Now
            items = form("items")

将使用逗号(,)

分隔所选项目