在MVC DropDownLists中保留选定的值

时间:2009-10-23 17:38:38

标签: asp.net-mvc drop-down-menu html-helper

我是MVC的新手,但我已经全身心投入,阅读了所有文档和所有问题以及我能找到的所有博客文章,而我正在做的就是完全绕过轴。

我正在尝试制作“创建”动作和视图。我的数据输入相对简单,常见:我有一个下拉列表和一个文本框。在我的情况下,我正在创建一个用户联系渠道,下拉框在电子邮件和textmsg之间选择,文本框然后输入相关的联系信息,或者是一个格式良好的电子邮件地址,或者是手机号码。

这是我的View页面的一个(略微简化的形式):

 <tr>
     <td><%= Html.DropDownList("ChannelDescription", Model.ChannelDescription, "Select a Channel", new { id = "ChannelDDL", onchange="ChannelDDLChanged()" })%>
         <br />
         <%= Html.ValidationMessage("ChannelDescription", "Please Select a Channel") %>
      </td>
      <td>
           <%= Html.TextBox("SubscriberNotificationAddr") %> <br />
           <%= Html.ValidationMessage("SubscriberNotificationAddr", "Please enter a contact address or number") %>
       </td>
  </tr>

我使用的是强类型的ViewData模型,而不是使用ViewDataDictionary。 ChannelDescription元素是一个SelectList,它使用选项列表初始化,没有选择。

表单的初始显示,表单中的数据输入以及控制器从表单中提取数据都很顺利。

我的问题是,如果数据包含错误,例如格式错误的电子邮件地址或手机号码,并且我必须返回到视图,我还没有成功地重新显示下拉列表选择。 ChannelDescription元素在控制器中重新创建,用户可以选择所选项目。我在View的那一行设置了断点,并验证了项目列表中所选元素的Selected属性设置为true,但它仍然显示默认的“选择一个频道”。

这似乎是一种非常普遍的情况,不应该这么难。我做错了什么?

仅供参考,这是在Firefox 3.5.2下运行的MVC 1.0(发布),Windows 7和VS 2008。

3 个答案:

答案 0 :(得分:1)

在查看上面的答案之后,我想查看一下,因为我见过的所有例子确实都使用了ViewDataDictionary,而不是强类型的ViewDataModel。

所以我做了一些实验。我构造了一个非常简单的视图,它使用了一个普通的ViewDataDictionary,并通过命名键传递了值。它坚持所选项目就好了。然后我将View(和控制器)剪切并粘贴到另一个,只更改切换到强类型ViewData模型所需的内容。瞧,它也坚持选定的项目。

那么我的简单测试和我的应用程序之间还有什么不同?在我的测试中,我只使用了“Html.DropDownList(”name“,”optionLabel“)”。但是,在我的应用程序中,我需要添加HTML属性,并且包含HtmlAttributes的唯一可用重载也包括选择列表。

事实证明,带有选择列表参数的DropDownList重载已经破坏!查看下载的MVC源代码,当只使用名称,名称和optionLabel调用DropDownList时,它最终从ViewData中检索目标选择列表,然后通过以下调用调用私有SelectInternal方法: p>

    return SelectInternal(htmlHelper, optionLabel, name, selectList, true /* usedViewData */, false /* allowMultiple */, (IDictionary<string, object>)null /* htmlAttributes */);

但是,如果使用selectList参数调用它,则最终会出现以下内容:

   return SelectInternal(htmlHelper, optionLabel, name, selectList, false /* usedViewData */, false /* allowMultiple */, htmlAttributes);

不同之处在于,在第一个(将正常工作)中,“usedViewData”参数为true,而在第二个参数中,它为false。这实际上没问题,但是在SelectInternal例程中暴露了一个内部缺陷。

如果usedViewData为false,则从ViewData模型中获取对象变量“defaultValue”。 但是,defaultValue被用作字符串或字符串数​​组,实际上从ViewData返回的是SelectList。 (IEnumerable<SelectListItem>)。

如果usedViewData为true,则defaultValue将为null或字符串。

然后,如果defaultValue不为null,它最终会进入包含以下内容的代码块:

        foreach (SelectListItem item in selectList) {
            item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text);
            newSelectList.Add(item);

selectList是传入的原始selectList,因此该项是SelectListItem(字符串Text,string Value和bool Selected)。但selectedValues派生自defaultValue,并成为SelectLists列表,而不是字符串列表。因此,对于每个项目,它根据selectedValues列表是否“包含”item.Value来设置Selected标记。好吧,SelectLists列表永远不会“包含”一个字符串,因此item.Selected永远不会被设置。 (更正:实际上,在使用调试器进行更多跟踪之后,我发现selectedValues是通过“ToString()”调用从defaultValue派生的。所以它实际上是一个字符串列表,但它不是包含我们想要的值,而是包含“System.Web.Mvc.SelectList” - 将“ToString()”应用于复杂对象(如SelectList)的结果。结果仍然相同 - 我们不会找到我们正在寻找的值列表)。

然后将新构造的“newSelectList”替换为原始的“selectList”,并继续从中构建HTML。

正如cagdas(我为骂你的名字道歉,但我不知道如何在我的美国键盘上制作这些角色)上面说过,我想我必须建立自己的方法来代替DropDownList的HtmlHelper。我想自从这个版本1和Release2在Beta 2中,除非我们自己做到这一点,否则我们真的不能指望任何错误修复?

顺便说一句,如果你这么跟着我,这段代码在src \ SystemWebMvc \ Mvc \ Html \ SelectExtensions.cs中,在116-136左右

答案 1 :(得分:1)

我与来自MVC团队的Brad Wilson进行了一些讨论,他向我解释说我误解了应该如何使用DropDownList助手方法(我认为可能相当普遍的误解,从我读过的内容) )。

基本上,EITHER在ViewModel的命名参数中给它SelectList,并让它从选择了适当项目的那个构建下拉列表,或者将SelectList作为单独的参数给它,并让它的命名参数ViewModel只是所选项的值字符串。如果给它一个SelectList参数,那么它期望命名值是字符串或字符串列表,而不是SelectList。

因此,现在您的ViewModel最终为视图中的一个概念项(下拉列表)提供了两个元素。因此,您可能拥有

的模型
string SelectedValue {get; set;}
SelectList DropDownElements { get; set;}

然后您可以使用选项预先填充DropDownElements,但在模型视图绑定中,您只需要处理SelectedValue元素。当我这样做时,它对我来说似乎很有效。

答案 2 :(得分:0)

是的,我也有很多问题让DropDownList尊重我给它的选定项目。

请检查my answer in this question。据我所知,这是我能让它发挥作用的唯一方法。通过ViewData传递列表。

仅供参考,我停止使用该HtmlHelper方法。我现在只是通过循环输出<select><option>标记,并通过自己检查来设置selected标记的option属性。