【问题标题】:Why is ListBoxFor not selecting items, but ListBox is?为什么 ListBoxFor 不选择项目,但 ListBox 是?
【发布时间】:2011-12-08 18:06:41
【问题描述】:

我认为有以下代码:

<%= Html.ListBoxFor(c => c.Project.Categories,
        new MultiSelectList(Model.Categories, "Id", "Name", new List<int> { 1, 2 }))%>

<%= Html.ListBox("MultiSelectList", 
        new MultiSelectList(Model.Categories, "Id", "Name", new List<int> { 1, 2 }))%>

唯一的区别是第一个助手是强类型的(ListBoxFor),并且它无法显示选定的项目(1,2),即使项目出现在列表中,等等。更简单的 ListBox 按预期工作。

我显然在这里遗漏了一些东西。我可以使用第二种方法,但这真的很困扰我,我想弄清楚。

供参考,我的模型是:

public class ProjectEditModel
{
    public Project Project { get; set; }
    public IEnumerable<Project> Projects { get; set; }
    public IEnumerable<Client> Clients { get; set; }
    public IEnumerable<Category> Categories { get; set; }
    public IEnumerable<Tag> Tags { get; set; }
    public ProjectSlide SelectedSlide { get; set; }
}

更新

我刚刚将 ListBox 名称更改为 Project.Categories(与我的模型匹配),现在无法选择该项目。

<%= Html.ListBox("Project.Categories",
        new MultiSelectList(Model.Categories, "Id", "Name", new List<int> { 1, 2 }))%>

我显然不明白这里发生的魔法。

更新 2

好的,这纯粹是命名,例如,这行得通...

<%= Html.ListBox("Project_Tags",
new MultiSelectList(Model.Tags, "Id", "Name", Model.Project.Tags.Select(t => t.Id)))%>

...因为字段名称是 Project_Tags,而不是 Project.Tags,实际上,除了 Tags 或 Project.Tags 之外的任何内容都可以。我不明白为什么这会导致问题(除了它与实体名称匹配),而且我在这方面做得不够好,无法深入了解。

【问题讨论】:

  • 我遇到了类似的问题。具体来说,尽管我是根据另一个值从控制器内为列表框(在模型上)设置我选择的值。为了让它工作,我必须设置 ModelState["Categories"].Value。我不喜欢这样,但是 ModelState 对象可以在模型本身上使用的想法充其量是令人困惑的,最坏的情况是令人愤怒。

标签: asp.net-mvc listbox


【解决方案1】:

我自己也偶然发现了这个问题,最后我意识到问题是命名约定。

您不能将包含 SelectList 或 MultiSelectList 的 ViewBag 或 ViewData 属性命名为与包含所选项目的属性模型相同的名称。如果您使用 ListBoxFor 或 DropDownListFor 帮助器,至少不会。

这是一个例子:

    public class Person
    {
          public List<int> Cars { get; set; }
    }

    [HttpGet]
    public ActionResult Create()
    {
          //wont work
          ViewBag.Cars = new SelectList(carsList, "CarId", "Name"); 

          //will work due to different name than the property.
          ViewBag.CarsList = new SelectList(carsList, "CarId", "Name"); 

          return View();
    }

    //View
    @Html.ListBoxFor(model => model.Cars, ViewBag.CarsList as SelectList)

我相信还有很多其他方法可以做到这一点,但它解决了我的问题,希望它对某人有所帮助!

【讨论】:

  • 感谢 Olivier,修复了它。这真的很愚蠢,因为当我在 ViewBag 中创建常规选择列表项时,我将它们设置为相同的名称,它们工作得很好。
【解决方案2】:

我也遇到了同样的问题,在 ListBox 和 ListBoxFor 上也遇到了同样的问题。

无论我做什么,我都无法在 ListBoxFor 上进行选择。如果我更改为 ListBox 并将其命名为与我绑定到的数据的属性名称不同的名称,则会发生选择。

但是因为我没有使用 ListBoxFor 并且数据位于模型类 (Model.Departments) 中,所以我在返回控制器的路上没有得到模型绑定,因此属性为空。

编辑我在这里找到了其他人发布的解决方案; Challenges with selecting values in ListBoxFor

【讨论】:

  • 该死,我希望我能记住这个修复。如果我没记错的话,它并没有我想的那么优雅。模型绑定发生了一种模糊的行为,导致您的绑定被覆盖。不太好。如果我能找到一个例子,我会分享。
【解决方案3】:

另外,您可以尝试在控制器中清除 c.Project.Categories 的 ModelState:

[HttpPost]
public ActionResult Index(ModelType model)
{
    ModelState.Remove("Project.Categories");
    return View("Index", model);
}

并使用下一个构造:

<%= Html.ListBoxFor(c => c.Project.Categories,
        new MultiSelectList(Model.Categories, "Id", "Name"))%>

其中 c.Project.Categories 是 IEnumerable&lt;int&gt;。

对不起我的英语。祝你好运!

【讨论】:

  • 我认为这是最好的答案。原因如下:查看 HtmlHelper ListBox 源代码可以看到代码首先尝试从控制器 ModelState 中获取 Selected 值,并且仅当它为 NULL 时才会使用提供的选定值多选列表。 ==> 所以如果你想覆盖存储在 ModelState 中的值(例如从浏览器发送到 Controller 的值),清除 MoelState 是要走的路。
  • 源码是here第173行
【解决方案4】:

正确的答案是它不能很好地工作。因此,我阅读了 MVC 代码。您需要做的是实现 IConvertible 并创建一个 TypeConverter。

所以,在我的例子中,我有一个 Country 类,这样人们就可以从国家列表中进行选择。选择它没有乐趣。我期待一个对象等于 selectedItems 与 listitems 的比较,但不,这不是它的工作原理。尽管MultiListItem 可以正常工作并正确获取所选项目,但当它绑定到您的模型时,这一切都是基于检查您的对象实例的字符串表示是否与字符串"value" 匹配(或名称,如果缺少)在SelectItemList 的项目列表中。

所以,实现IConvertible,从ToString 返回字符串值,它与SelectItemList 中的值匹配。例如,在我的情况下,CountryCode 被序列化为SelectItem Value 属性,所以在ToString IConvertible 中我返回了CountryCode。现在一切都正确选择了。

我会指出TypeConverter 是在进来的路上使用的。这次是相反的。 Countrycode 又回来了,“EN”需要转换为 Country 类实例。这就是TypeConverter 出现的地方。这也是我意识到这种方法有多么难以使用的时候。

p.s 所以在你的Category 类上你需要实现IConvertible。如果它来自我公司的实体框架,那么您需要使用部分类来实现IConvertible并实现ToString并用您也写的TypeConverter装饰它。

【讨论】:

    【解决方案5】:

    虽然这不是您的主要问题的答案,但值得注意的是,当 MVC 生成名称时,它会将 Project.Tags 之类的内容转换为 Project_Tags,用下划线替换句点。

    这样做的原因是因为元素 ID 中的句点看起来像一个名为 Project 的元素,具有 CSS 的标签类。显然是一件坏事,因此翻译为下划线以保持行为可预测。

    在你的第一个例子中,

    <%= Html.ListBoxFor(c => c.Project.Categories,
        new MultiSelectList(Model.Categories, "Id", "Name", new List<int> { 1, 2 }))%>
    

    列表框正在尝试为已提供给页面的强类型模型绑定到 Model.Project.Categories(使用 lambda 表示法)。我不确定 ListBoxFor 中的第二个参数在做什么。

    传递给页面的模型是什么?

    【讨论】:

    • 应您的要求,我已将我的模型添加到问题中。我知道 MVC 将 Id 更改为 Id="Project_Tags",但名称并非如此,它仍然是“Project.Tags”,并且名称是发布回操作的内容,因此,我猜部分这会造成麻烦,因此,如果我将 name 属性更改为 Project_Tags,它就可以工作。我很惊讶,如果它真的不能用于多选列表,甚至还有一个强类型的 listboxfor helper!感谢您的帮助!
    【解决方案6】:

    试试这个

    <%= Html.ListBoxFor(c => c.Project.Categories,
        new MultiSelectList(
            Model.Categories
            ,"Id"
            ,"Name"
            ,Model.Project.Tags.Select(
            x => new SelectListItem()
                {
                Selected = true,
                Text = x.TEXT,
                Value = x.ID.ToString()
                }).ToList())
    
           )
    )%>
    

    【讨论】:

      【解决方案7】:

      当您没有将列表框绑定到其数据源时,Html.ListboxFor 和 Html.Listbox 工作得很好。我假设预期用途是这样的:

      // Model
      public class ListBoxEditModel
      {
          public IEnumerable<Category> Categories { get; set; }
          public IEnumerable<Category> SelectedCategories { get; set; }
      }    
      

      在视图中:

      @Html.ListBoxFor(m => m.SelectedCategories,
                       new MultiSelectList(Model.Categories, "Id", "Name"))
      // or, equivalently
      @Html.ListBox("SelectedCategories" ,
                       new MultiSelectList(Model.Categories, "Id", "Name"))
      

      请注意,在这种情况下,您不必明确说明在 MultiSelectList 中选择了哪些值 - 您绑定到的模型优先,即使您这样做!

      【讨论】:

        猜你喜欢
        • 2019-08-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-26
        • 1970-01-01
        • 2019-11-18
        • 1970-01-01
        • 2015-10-04
        相关资源
        最近更新 更多