ASP.NET 4.8 Razor 中 DropDownListFor 为什么不显示选中值?
结论
可以在循环中为每一行创建一个指定选中值的 SelectList,再传给 DropDownListFor。同时,检查 SelectedCode 转换后的值是否与目标选项的 Value 一致,以及 ModelState 中是否有旧值覆盖了它。
模型属性有值,还需要选项列表中存在与之匹配的值。目前没有提供 Code、ComboList<Code> 和 ToSelectList() 的实现,还不能唯一确定原因。
为每一行显式指定选中值
下面的示例假定 ToSelectList() 返回标准的 SelectListItem 集合,且选项的 Value 使用与 SelectedCode.ToString() 相同的表示方式:
@for (int i = 0; i < Model.Details.Count; i++)
{
var selectedCode = Model.Details[i].SelectedCode;
var selectedValue = selectedCode.HasValue
? selectedCode.Value.ToString()
: null;
var options = new SelectList(
Model.CodesToSelect.ToSelectList(),
"Value",
"Text",
selectedValue);
<div class="col-sm-3">
@Html.LabelFor(
d => d.Details[i].SelectedCode,
new { @class = "control-label" })
@Html.DropDownListFor(
d => d.Details[i].SelectedCode,
options,
new { @class = "form-control" })
</div>
}
"Value" 和 "Text" 对应传入集合中元素的属性,最后一个参数 selectedValue 用来确定选中项。
每行的 SelectedCode 可能不同,所以在循环内为每行构造对应的 SelectList,也能避免依赖辅助方法从集合索引表达式中自动推断选中值。
如果 ToSelectList() 返回的元素没有 Value、Text 属性,需要根据实际返回的类型调整字段名,不能直接照用这个示例。
检查 SelectedCode 与 option.value 是否匹配
在浏览器中查看生成的 HTML,检查目标选项的 value 和 selected:
<select name="Details[0].SelectedCode">
<option value="1">选项一</option>
<option value="2" selected="selected">选项二</option>
</select>
下拉列表按 value 匹配选中值,页面显示的文字不是匹配依据。如果目标值不在选项列表中,就无法选中它;单选下拉列表没有明确选中项时,通常会显示第一项。
如果 Code 是枚举,还要检查枚举名称与底层数值的区别。例如,枚举值的 ToString() 可能得到 SomeCode,而选项的 Value 是 "2",两者不会匹配。
如果确认选项使用枚举的十进制底层值,可以将上面示例中的转换替换为:
var selectedValue = selectedCode.HasValue
? Enum.Format(typeof(Code), selectedCode.Value, "d")
: null;
这段转换只适用于 Code 确实是枚举的情况。应根据 ToSelectList() 的实际输出选择转换方式,不要仅凭属性名判断。
检查是否被 ModelState 覆盖
如果问题发生在 POST 后重新返回同一个视图,经典 ASP.NET MVC 的表单辅助方法通常会优先使用 ModelState 中保存的提交值。即使控制器修改了模型属性,页面也可能仍显示之前提交的值。
如果业务明确要求使用控制器重新设置的值,可以在返回视图前移除对应字段的记录:
ModelState.Remove("Details[0].SelectedCode");
return View(model);
其中的 0 是示例索引,应替换为实际需要更新的行号。字段键需要与生成的 name 一致;如果使用了模板前缀,也要包含该前缀。
验证失败后如果需要保留用户输入,就不应随意移除这些记录。优先检查具体字段,不必为了一个下拉列表调用 ModelState.Clear()。
readonly 不适用于 select
原代码中的:
new { @class = "form-control", @readonly = "readonly" }
不能让 HTML <select> 变成只读。readonly 也不决定选中项,可以从下拉列表上移除。
如果确实需要禁止用户修改,可以使用 disabled,但被禁用的控件不会随表单提交。需要提交该值时,可以另加 HiddenFor,并在服务端校验提交值。
建议依次检查 ToSelectList() 输出的选项值、每行显式传入的选中值,以及 POST 场景下的 ModelState。如果生成的 HTML 已经选中了正确选项,页面却仍显示第一项,再检查前端脚本或下拉列表插件是否修改了选中状态。
备注:内容仅供参考。
