ASP.NET MVC Html.DropDownList SelectedValue

时间:2009-03-09 02:36:09

标签: asp.net-mvc

我试过这是RC1,然后升级到RC2,但没有解决问题。

// in my controller
ViewData["UserId"] = new SelectList(
    users, 
    "UserId", 
    "DisplayName", 
    selectedUserId.Value); // this has a value

result:在对象

上设置SelectedValue属性
// in my view
<%=Html.DropDownList("UserId", (SelectList)ViewData["UserId"])%>

结果:所有预期选项都呈现给客户端,但未设置所选属性。 SelectedValue中的项目存在于列表中,但列表中的第一项始终默认为选中。

我该怎么做?

更新 感谢John Feminella的回复,我发现了问题所在。 “UserId”是我的视图强类型化的模型中的属性。当Html.DropDownList(“UserId”更改为除“UserId”之外的任何其他名称)时,将正确呈现所选值。

这会导致值不被绑定到模型。

9 个答案:

答案 0 :(得分:69)

这就是我解决这个问题的方法:

我有以下内容:

控制器:

ViewData["DealerTypes"] = Helper.SetSelectedValue(listOfValues, selectedValue) ;

查看

<%=Html.DropDownList("DealerTypes", ViewData["DealerTypes"] as SelectList)%>

由以下内容更改:

查看

<%=Html.DropDownList("DealerTypesDD", ViewData["DealerTypes"] as SelectList)%>

看起来DropDown的名称不能与ViewData名称相同:S很奇怪,但它有效。

答案 1 :(得分:50)

试试这个:

public class Person {
    public int Id { get; set; }
    public string Name { get; set; }
}

然后:

var list = new[] {   
    new Person { Id = 1, Name = "Name1" }, 
    new Person { Id = 2, Name = "Name2" }, 
    new Person { Id = 3, Name = "Name3" } 
};

var selectList = new SelectList(list, "Id", "Name", 2);
ViewData["People"] = selectList;

Html.DropDownList("PeopleClass", (SelectList)ViewData["People"])

使用MVC RC2,我得到:

<select id="PeopleClass" name="PeopleClass">
    <option value="1">Name1</option>
    <option selected="selected" value="2">Name2</option>
    <option value="3">Name3</option>
</select>

答案 2 :(得分:5)

您仍然可以将DropDown命名为“UserId”,并且仍然可以为您正确运行模型绑定。

此操作的唯一要求是包含SelectList的ViewData键与要绑定的Model属性的名称不同。在您的具体情况下,这将是:

// in my controller
ViewData["Users"] = new SelectList(
    users, 
    "UserId", 
    "DisplayName", 
    selectedUserId.Value); // this has a value

// in my view
<%=Html.DropDownList("UserId", (SelectList)ViewData["Users"])%>

这将生成一个名为UserId的select元素,它与模型中的UserId属性具有相同的名称,因此模型绑定器将使用在Html.DropDownList帮助程序生成的html的select元素中选择的值来设置它。

我不确定为什么当您使用等于属性名称的键将选择列表放在ViewData中时,为什么特定的Html.DropDownList构造函数不会选择SelectList中指定的值。我怀疑它与在其他场景中如何使用DropDownList助手有关,其中约定是在ViewData中有一个与模型中属性同名的SelectList。这将正常工作:

// in my controller
ViewData["UserId"] = new SelectList(
    users, 
    "UserId", 
    "DisplayName", 
    selectedUserId.Value); // this has a value

// in my view
<%=Html.DropDownList("UserId")%>

答案 3 :(得分:2)

上一篇MVC 3帖子中的代码不起作用,但这是一个好的开始。我会修好它。我已经测试了这段代码,它在MVC 3 Razor C#中工作。此代码使用ViewModel模式填充返回List<SelectListItem>的属性。

Model类

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

ViewModel类

using System.Web.Mvc;

public class ProductListviewModel
{
    public List<SelectListItem> Products { get; set; }
}

控制器方法

public ViewResult List()
{
    var productList = new List<SelectListItem>();

    foreach (Product p in Products)
    {
        productList.Add(new SelectListItem
        {
            Value = p.ProductId.ToString(),
            Text = "Product: " + p.Name + " " + p.Price.ToString(),
            // To set the selected item use the following code 
            // Note: you should not set every item to selected
            Selected = true
        });
    }

    ProductListViewModel productListVM = new ProductListViewModeld();

    productListVM.Products = productList;

    return View(productListVM);
}

视图

@model MvcApp.ViewModels.ProductListViewModel

@using (Html.BeginForm())
{
    @Html.DropDownList("Products", Model.Products)
}

HTML输出类似于

<select id="Products" name="Products">
    <option value="3">Product: Widget 10.00</option>
    <option value="4">Product: Gadget 5.95</option>
</select>

取决于您如何格式化输出。我希望这有帮助。代码确实有效。

答案 4 :(得分:2)

如果我们认为这不是团队应该修复的错误,那么MSDN应该改进文档。令人困惑的确是来自这个糟糕的文件。在MSDN中,它将参数 name 解释为,

Type: System.String
The name of the form field to return.

这只是意味着它生成的最终html将使用该参数作为选择输入的名称。但是,它实际上意味着更多。

我猜设计师假设用户将使用视图模型来显示下拉列表,也会使用回发到相同视图模型。但在很多情况下,我们并没有真正遵循这一假设。

使用上面的例子,

public class Person {
    public int Id { get; set; }
    public string Name { get; set; }
}

如果我们遵循这个假设,我们应该为这个下拉列表相关视图定义一个视图模型

public class PersonsSelectViewModel{
    public string SelectedPersonId,
    public List<SelectListItem> Persons;
}

因为当回发时,只有所选的值会回发,所以它假设它应该回发到模型的属性SelectedPersonId,这意味着Html.DropDownList的第一个参数 name 应该是'SelectedPersonId'。因此,设计人员认为在视图中显示模型视图时,模型的属性SelectedPersonId应该保持该下拉列表的默认值。甚至想到你的List&lt; SelectListItem&gt;人们已经设置了Selected标志以指示选择了哪一个/默认,tml.DropDownList实际上将忽略它并重建它自己的IEnumerable&lt; SelectListItem&gt;并根据名称设置默认/选定项目。

以下是asp.net mvc的代码

private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata,
            string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple,
            IDictionary<string, object> htmlAttributes)
{
    ...

    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);
    }

    ...

    return tagBuilder.ToMvcHtmlString(TagRenderMode.Normal);
}

因此,代码实际上更进一步,它不仅尝试在模型中查找名称,而且还在viewdata中查找,一旦找到它,它将重建selectList并忽略原始的Selected。

问题是,在很多情况下,我们并没有真正使用它。我们只想抛出一个带有一个/多个项目的selectList,选择set true。

当然解决方案很简单,使用的名称不在模型中,也不在viewdata中。当它找不到匹配时,它将使用原始的selectList,原来的Selected将生效。

但我仍然认为mvc应该通过增加一个条件来改善它

if ((defaultValue != null) && (!selectList.Any(i=>i.Selected)))
{
    selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
}

因为,如果原始的selectList已经有一个Selected,你为什么要忽略它呢?

只是我的想法。

答案 5 :(得分:1)

这似乎是SelectExtensions类中的一个错误,因为它只会检查ViewData而不是所选项的模型。因此,诀窍是将所选项目从模型复制到属性名称下的ViewData集合中。

这取自我在MVC论坛上给出的答案,我在使用blog postKazi's DropDownList attribute中也有更完整的答案......

给定模型

public class ArticleType
{
   public Guid Id { get; set; }
   public string Description { get; set; }
}

public class Article
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public ArticleType { get; set; }
}

的基本视图模型
public class ArticleModel
{
     public Guid Id { get; set; }
     public string Name { get; set; }

     [UIHint("DropDownList")]
     public Guid ArticleType { get; set; }
}

然后我们编写一个DropDownList编辑器模板,如下所示..

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script runat="server">  
    IEnumerable<SelectListItem> GetSelectList()
    {
        var metaData = ViewData.ModelMetadata;
        if (metaData == null)
        {
            return null;
        }

        var selected = Model is SelectListItem ? ((SelectListItem) Model).Value : Model.ToString();
        ViewData[metaData.PropertyName] = selected;

        var key = metaData.PropertyName + "List";
        return (IEnumerable<SelectListItem>)ViewData[key];
    }
</script>
<%= Html.DropDownList(null, GetSelectList()) %>

如果您将视图模型中的ArticleType更改为SelectListItem,这也会有效,尽管您必须按照Kazi的博客实现类型转换器并注册它以强制绑定器将其视为一种简单类型。

在你的控制器中我们有......

public ArticleController 
{
     ...
     public ActionResult Edit(int id)
     {
          var entity = repository.FindOne<Article>(id);
          var model = builder.Convert<ArticleModel>(entity);

          var types = repository.FindAll<ArticleTypes>();
          ViewData["ArticleTypeList"] = builder.Convert<SelectListItem>(types);

          return VIew(model);
     }
     ...
}

答案 6 :(得分:0)

问题是dropbox与listbox不同,至少是ASP.NET MVC2设计所期望的方式:dropbox只允许零或一个值,因为listbox可以有多个值选择。因此,严格遵循HTML,该值不应该在选项列表中作为“selected”标志,而是在输入本身。

请参阅以下示例:

<select id="combo" name="combo" value="id2">
  <option value="id1">This is option 1</option>
  <option value="id2" selected="selected">This is option 2</option>
  <option value="id3">This is option 3</option>
</select>

<select id="listbox" name="listbox" multiple>
  <option value="id1">This is option 1</option>
  <option value="id2" selected="selected">This is option 2</option>
  <option value="id3">This is option 3</option>
</select>

组合选择了该选项,但也设置了其值属性。因此,如果您希望ASP.NET MVC2呈现一个Dropbox并且还选择了一个特定值(即默认值等),您应该在呈现中为其赋予一个值,如下所示:

// in my view             
<%=Html.DropDownList("UserId", selectListItems /* (SelectList)ViewData["UserId"]*/, new { @Value = selectedUser.Id } /* Your selected value as an additional HTML attribute */)%>

答案 7 :(得分:0)

在ASP.NET MVC 3中,您只需将列表添加到ViewData ...

即可
var options = new List<SelectListItem>();

options.Add(new SelectListItem { Value = "1", Text = "1" });
options.Add(new SelectListItem { Value = "2", Text = "2" });
options.Add(new SelectListItem { Value = "3", Text = "3", Selected = true });

ViewData["options"] = options;

...然后在你的剃须刀视图中按名称引用它......

@Html.DropDownList("options")

您不必手动“使用”DropDownList调用中的列表。这样做也可以为我正确设置所选的值。

免责声明:

  1. 没有尝试使用网络表单视图引擎,但它也应该有用。
  2. 我没有在v1和v2中对此进行过测试,但它可能会有效。

答案 8 :(得分:0)

我设法获得了预期的结果,但是方法略有不同。在下拉列表中,我使用了模型,然后引用了它。不确定这是否是您想要的。

pageSize

上面的Model.Matters.FeeStructures是我的ID,它可以是您应选择的项目的值。