DropDownListFor Helper在预填充空值时做出错误的选择

时间:2016-03-03 23:21:06

标签: c# asp.net-mvc-3 data-binding html.dropdownlistfor

我正在使用MVC3从数据库填充HTML表单。许多表单元素是下拉列表(选择)。我正在使用“DropDownListFor”帮助程序预先填充列表并在编辑数据的POSTing上执行数据绑定(这很好)。

我在数据库中有一些可以为空的字段,其中“NULL / Empty String”是合法的默认值。因此,不是“高”,“中”和“低”作为3种可能的选项,它使用“高”,“中”和“”,其中空字符串或空值等同于“低”。我在下拉列表中镜像了这种行为,以便它们提供相应的文本/值对。

问题在于模型绑定时预先填充下拉列表并根据数据库中的值选择要在DDL中显示的默认值。如果传入的数据库值为“高”,则它会正确选择“高”选项。如果数据库值为“medium”,则正确选择“medium”选项。但是 - 如果数据库值为NULL /空字符串,则选择“medium”选项而不是“low”选项(或者根本没有“选择”,这是我所期望的)!根据我正在使用的DropDownListFor重载,它正确地在列表顶部插入默认的optionLabel,但随后它通过将'selected =“selected”'标记为“medium”选项来迅速覆盖它 - 来自哪里?

这是控制器中的代码;

IcxPdcEditViewModel editViewModel = new IcxPdcEditViewModel()
{
    TwoPlusEmptyDDL = twoPlusEmptyList.TwoPlusEmptyList
};
return View(editViewModel);

其中助手按如下方式设置DDL;

public class PDCTwoPlusEmptyList
{
    public List<SelectListItem> TwoPlusEmptyList { get; set; }
    public PDCTwoPlusEmptyList()
    {
        this.TwoPlusEmptyList = new List<SelectListItem>()
        {
            new SelectListItem() { Text = "medium", Value = "medium" },
            new SelectListItem() { Text = "high", Value = "high" }
        };
    }
}

以下是View中的相应代码;

<td>Maintenance:</td>
<td><%= Html.DropDownListFor(model => model.Plant.Maintenance, Model.TwoPlusEmptyDDL, "low") %></td>

以下是数据库值为NULL /空字符串(已确认);

时生成的HTML源代码
<td><b>Maintenance</b>*:</td>
<td><select id="Plant_Maintenance" name="Plant.Maintenance">
  <option value="">low</option>
  <option selected="selected" value="medium">medium</option>
  <option value="high">high</option>
</select></td>

最后一个想法,我根本没有使用ViewBag,而且我的ViewData中没有冲突的名称。

有关导致此行为的原因的任何想法?

2 个答案:

答案 0 :(得分:0)

由于经过了大量的调试,我找到并修复了问题! :)

问题是我正在重复使用&#34; PDCTwoPlusEmptyList&#34;对于单个表单上的4个不同的下拉列表(即单个视图),作为节省控制器中的代码的方法。这&#34;可重复使用&#34; DDL对象能够正确地重置&#34; Selected&#34;当值为NULL时,标志匹配从数据库传入的值EXCEPT,在这种情况下,它记住&#34;记住&#34;来自之前DDL的值。解决方案是为页面上的每个DDL创建一个单独的PDCTwoPlusEmptyList实例 - 现在一切正常。经验教训!

答案 1 :(得分:0)

我遇到了同样的问题 - 重用List<SelectListItem>,并在值为null时发现DropDownListFor未正确设置所选值。进入System.Web.Mvc的代码后,我找到了原因。

调用堆栈如下所示:

public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, object htmlAttributes)
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes)
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata, string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple, IDictionary<string, object> htmlAttributes)

SelectInternal的代码如下所示:

private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata, string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple, IDictionary<string, object> htmlAttributes)
{
    string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);

    // ...

    bool usedViewData = false;

    // If we got a null selectList, try to use ViewData to get the list of items.
    if (selectList == null)
    {
        selectList = htmlHelper.GetSelectData(name);
        usedViewData = true;
    }

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

    // If we haven't already used ViewData to get the entire list of items then we need to
    // use the ViewData-supplied value before using the parameter-supplied value.
    if (defaultValue == null && !String.IsNullOrEmpty(name))
    {
        if (!usedViewData)
        {
            defaultValue = htmlHelper.ViewData.Eval(name);
        }
        else if (metadata != null)
        {
            defaultValue = metadata.Model;
        }
    }

    if (defaultValue != null)
    {
        selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
    }

    // ...
}

调用SelectInternal时,defaultValue变量用于存储模型中的值。这是针对null进行测试的,如果值 null,则selectList会被新列表覆盖,并为所选属性设置列出与defaultValue匹配的项目。

如果在后续拨打SelectInternal时,defaultValue null,则selectList不会更新,因此列表中的所选项目仍为与最近一次具有非空值且使用相同selectList的调用相同。

处理此行为的一些可能选项:

  • 为每个列表使用单独的列表(由this answer建议)
  • 编写一个自定义HtmlHelper,清除所有列表项的选定属性
  • 在将List传递给DropDownListFor之前克隆List,因此原始列表永远不会被修改