PAYATHON 2026

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 已经选中了正确选项,页面却仍显示第一项,再检查前端脚本或下拉列表插件是否修改了选中状态。

备注:内容仅供参考。