为什么在提交后 DropDownListFor 会失去多选功能,而 ListBoxFor 不会?

11

我已经阅读了许多关于使用MultiSelectList的文章,但仍然不理解我的DropDownListFor出了什么问题。我有一个具有相同视图、ViewModel和数据的ListBoxFor可以很好地工作。我想使用DropDownListFor,因为它有一个ListBoxFor没有的optionLabel参数。

当视图首次加载时,DropDownListFor和ListBoxFor都显示了多个选定的项目。

初始视图

当单击提交按钮时,所选项目集合被发送回控制器操作并刷新视图,ListBoxFor仍显示两个选定项目,但是DropDownListFor只显示一个选定项目。

刷新后的视图 控制器操作像这样构造MultiSelectList:

vm.TasksFilterGroup.Assignees = new MultiSelectList(employees, "Id", "FullName", new string[] { "51b6f06a-e04d-4f98-88ef-cd0cfa8a2757", "51b6f06a-e04d-4f98-88ef-cd0cfa8a2769" });

The View code looks like this:

<div class="form-group">
  <label>ListBoxFor</label>
  @Html.ListBoxFor(m => m.TasksFilterGroup.SelectedAssignees, Model.TasksFilterGroup.Assignees, new { @class = "form-control", multiple = "multiple" })
</div>
<div class="form-group">
  <label>DropDownListFor</label>
  @Html.DropDownListFor(m => m.TasksFilterGroup.SelectedAssignees, Model.TasksFilterGroup.Assignees, new { @class = "form-control", multiple = "multiple" })
</div>

为什么在提交后,DropDownListFor会失去多选功能,而ListBoxFor却不会?
1个回答

23

根据方法名称,DropDownListFor() 用于创建一个 <select>(选择1个选项),而 ListBoxFor() 用于创建一个 <select multiple>(选择多个选项)。虽然这两种方法共享很多公共代码,但它们产生的结果是不同的。

添加 multiple="multiple" 属性会改变显示方式,但不会改变这些方法执行的代码功能。

如果您检查 源代码,您会注意到所有 DropDownListFor() 的重载最终都调用了 private static MvcHtmlString DropDownListHelper() 方法,类似地,ListBoxFor() 最终调用了 private static MvcHtmlString ListBoxHelper() 方法。

这两种方法都调用了private static MvcHtmlString SelectInternal()方法,但不同之处在于DropDownListHelper()传递了allowMultiple = false,而ListBoxHelper()则传递了allowMultiple = true

SelectInternal()方法中,关键代码行是

object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(fullName, typeof(string[])) : htmlHelper.GetModelStateValue(fullName, typeof(string));

defaultValue 的值用于构建 <option> 元素的 HTML,并用于设置 selected 属性。

对于 ListBoxFor()defaultValue 的值将是由您的 SelectedAssignees 属性定义的数组。对于 DropDownListFor(),它返回 null,因为您的属性值不能转换为 string(它是一个数组)。

由于 defaultValuenull,因此没有任何 <option> 元素具有设置 selected 属性,这会导致模型绑定失败。

另外需要注意的是,在将模型传递给视图之前,在 GET 方法中设置 SelectedAssignees 的值,您会发现在使用 DropDownListFor() 时,由于上述原因,它们都不会被选中。

还要注意生成 SelectList 的代码应该只是:

vm.TasksFilterGroup.Assignees = new SelectList(employees, "Id", "FullName" });

当使用DropDownListFor()ListBoxFor()方法时,设置第三个参数没有意义,因为绑定到属性(SelectedAssignees)的值确定哪些选项被选中(方法忽略第三个参数)。 如果您想选择与这些Guid值匹配的选项,则在GET方法中使用以下内容:

vm.TasksFilterGroup.SelectedAssignees= new string[]{ "51b6f06a-e04d-4f98-88ef-cd0cfa8a2757", "51b6f06a-e04d-4f98-88ef-cd0cfa8a2769" };

2
非常感谢您提供这么详尽和专业的回答。谢谢。 - RJBreneman
1
这是一个很好而且深入的回答。非常有见地。 - CodingYoshi
非常感谢,描述得非常清楚。 - Marek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接