1

我是 MVC 的新手,但我一直在研究这一切,阅读了所有文档、所有问题以及我能找到的所有博客文章,而我所做的只是完全缠绕在车轴上。

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

这是我的视图页面的(稍微简化的形式):

 <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 元素在控制器中重新创建,用户选择作为选定项。我已经在视图的那一行设置了断点,并验证了项目列表的选定元素的 Selected 属性设置为 true,但它仍然显示默认的“选择通道”。

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

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

4

3 回答 3

1

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

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

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

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

    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”参数为真,而在第二个中,它为假。这实际上没问题,但暴露了 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,因此 item 是一个 SelectListItem(字符串 Text、字符串 Value 和 bool Selected)。但是 selectedValues 是从 defaultValue 派生的,并成为 SelectLists 的 List,而不是字符串的 List。因此,对于每个项目,它根据 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 行

于 2009-10-23T21:33:10.213 回答
1

我与 MVC 团队的 Brad Wilson 进行了一些讨论,他向我解释说我误解了如何使用 DropDownList 辅助方法(根据我的阅读,我认为这种误解可能相当普遍)。

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

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

string SelectedValue {get; set;}
SelectList DropDownElements { get; set;}

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

于 2009-11-04T20:36:55.280 回答
0

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

请检查我在这个问题中的回答。据我所知,这是我让它发挥作用的唯一方法。通过 ViewData 传递列表。

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

于 2009-10-23T17:59:33.343 回答