asp.net-mvc ASP.NET MVC MultiSelectList 的选定值未正确选择

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3737985/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 00:30:30  来源:igfitidea点击:

ASP.NET MVC MultiSelectList with selected values not selecting properly

asp.net-mvc

提问by Andrew Flanagan

I know others have asked this question, but I'm totally confused by this:

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

This displays the dropdown with no values selected:

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

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

This displays the dropdown with the values that I'm passing in (Model.items) selected properly like what I'd expect:

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

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

But the problem is that this item is now named "somethingelse" when i POST. I know I can hack around this but what's going?

但问题是,当我 POST 时,这个项目现在被命名为“somethingelse”。我知道我可以解决这个问题,但这是怎么回事?

采纳答案by Jero

The problem you have is using Model.Items as a parameter. The code

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

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

isn't actually working as you would expect. It's working because the name of the dropdown is "items". That's because there was a form param called "items" posted back to your action. That param gets stored in the action's ViewState (don't confuse with ViewData). The Html.DropdownList() sees that there is a ViewState param named the same as you have named your dropdown and uses that ViewState param to work out the selected values. It completely ignores the Model.items that you passed in.

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

If anyone can explain the logic of not being able to override the default behavior then I'd love to hear it.

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

So, that's your first problem. To get around it all you have to do is to rename the dropdown to something else - exactly like you did in your second example. Now your second problem comes into play: the list of selected items must be a collection of simple objects (I think it actually needs to be an IEnumerable but I'm not 100% sure).

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

The DropDownList() method will try and match those selected values to the Value in your AvailableItemscollection. If it can't do that it will try to match against the Text.

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

So, try this to see if it works

所以,试试这个,看看它是否有效

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

Good luck

祝你好运

回答by Darin Dimitrov

Too little context provided in your question but I will try to show a full working example:

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

Model:

模型:

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

Controller:

控制器:

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

View:

看法:

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

Notice that the Html.ListBoxForis more adapted if you want to generate a multiple select. Obviously the AvailableItemsproperty should be fetched from a repository.

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

回答by Aviko

I had the same problem, I used my own extention method to generate the html and problem solved

我遇到了同样的问题,我使用自己的扩展方法来生成 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());
    }

回答by Thorsten Westheider

Actually, if you look at the MVC source code this behavior is baked into DropDownListFor by default (search for allowMultiple: false). The solution is to use ListBoxFor instead (you will see that as well in the MVC source code, allowMultiple: true), which makes a lot of sense as HTML wise, both render to

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

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

You don't have to use different properties on the model as suggested in the answers above this one, I got this working by simply switching to ListBoxFor instead (CSS takes it from there):

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

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

Works like a charm, even with POST and re-displaying the view on error.

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

回答by Liron

I had a similar problem, when using ListBoxForand a MultiSelectListassigned as a ViewBagproperty. @jero's answerhelped me figure out that if there's a naming collision between the ViewBag field and the model field, then the selected values don't appear properly.

我有一个类似的问题,当使用ListBoxForMultiSelectList分配为ViewBag属性时。@jero 的回答帮助我弄清楚,如果 ViewBag 字段和模型字段之间存在命名冲突,则所选值不会正确显示。

If I did the following, the items did not show up as selected.

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

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

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

If I changed it to the following, then it was fine.

如果我将其更改为以下内容,那就没问题了。

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

回答by Josue Morales

You can go to the to the value of "items" with this

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

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

that will get you the selected items as a string separate with commas (,)

这将使您将所选项目作为用逗号 (,) 分隔的字符串