代码之家  ›  专栏  ›  技术社区  ›  Andrew Flanagan

具有所选值的ASP.NET MVC多选择列表未正确选择

  •  26
  • Andrew Flanagan  · 技术社区  · 14 年前

    我知道有人问过这个问题,但我完全困惑于:

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

    <%= 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" })%>
    

    但问题是,当我发帖时,这个项目现在被命名为“somethingelse”。我知道我能解决这个问题,但怎么了?

    6 回复  |  直到 7 年前
        1
  •  21
  •   Leniel Maccaferri    11 年前

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

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

    并不像你想象的那样工作。它起作用,因为下拉列表的名称是“items”。这是因为有一个名为“items”的表单参数发布回了您的操作。该参数存储在操作的视图状态中(不要与视图数据混淆)。 在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
  •  51
  •   Darin Dimitrov    14 年前

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

    模型:

    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 应从存储库中提取属性。

        3
  •  5
  •   Aviko    12 年前

    我也有同样的问题,我使用自己的扩展方法生成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());
        }
    
        4
  •  3
  •   Thorsten Westheider    10 年前

    实际上,如果您查看MVC源代码,默认情况下,此行为将被烘焙到DropDownListfor中(搜索allowMultiple:false)。解决方案是使用listbox for(在MVC源代码allowmultiple:true中也会看到这一点),这在HTML方面很有意义,两者都呈现为

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

    您不必像上面的答案中所建议的那样在模型上使用不同的属性,我只需切换到ListBoxfor(CSS从中获取它)即可实现这一点:

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

    工作起来就像一个魅力,即使是在出错的情况下发布和重新显示视图。

        5
  •  1
  •   Liron    7 年前

    我在使用时也遇到了类似的问题 ListBoxFor 和A MultiSelectList 分配为 ViewBag 属性。@杰罗的 answer 帮助我发现,如果在viewbag字段和model字段之间存在命名冲突,那么所选的值就不会正确显示。

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

    //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" })
    
        6
  •  0
  •   Josue Morales    9 年前

    您可以使用此转到“items”的值

    <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")
    

    这将使您以字符串形式获得所选项目,并用逗号(,)分隔。