请参阅下面的更新摘要......
我理解使用' For' Html Helpers是首选,但是当我将DropDownListFor用作多选时,我遇到了问题。
此示例(DropDownList)完美运行:
@Html.DropDownList(
"ProtocolDisciplines",
new MultiSelectList(Model.Disciplines, "DisciplineId", "Discipline", Model.ProtocolDisciplines.Select(pd => pd.DisciplineId)),
new { @class = "form-control", multiple = "multiple", size = "8" }
)
此示例(DropDownListFor)工作正常除了默认值(s)未设置:
@Html.DropDownListFor(
model => model.ProtocolDisciplines,
new MultiSelectList(Model.Disciplines, "DisciplineId", "Discipline", Model.ProtocolDisciplines.Select(pd => pd.DisciplineId)),
new { @class = "form-control", multiple = "multiple", size = "8" }
)
更新 根据我学习的内容,我已经从原帖发布了更新内容。这是仍然无法正常工作的代码。要清楚,除了在渲染时没有选择默认值时,它正在完美地完成所有操作。在我使用的示例中,只有一个默认值。
@Html.ListBoxFor(
model => model.ProtocolDisciplines,
new MultiSelectList(Model.Disciplines, "DisciplineId", "Discipline", Model.ProtocolDisciplines),
new { @class = "form-control", size = "8" }
)
我已经确定Disciplines(db中所有16个Disciplines的列表)和ProtocolDisciplines(属于Protocol的Disciplines列表)是相同的类型(DisciplineViewModel)。此外,该类(见下文)仅包含2个属性(DisciplineId和Discipline)。
我有一个断点,模型返回到视图,我已经验证了Disciplines和ProtocolDisciplines都有预期的值,所以我目前专注于视图和ListBoxFor帮助器。作为一个注释,我也尝试使用具有相同行为的DropDownListFor帮助程序完全相同的代码。)
我怀疑问题出在MultiSelectList的创建中。如您所见,我使用了重载(IEnumerable ListItems,字符串DataValue,字符串DataText,IEnumerable SelectedValues)。似乎SelectedValues根本没有匹配ListValues中的任何内容,但我无法弄清楚原因。两者中使用的类型相同,DataValue和DataTypes名称与类型的成员匹配(只是为了安全)。我知道ListItems是正确的,因为列表正确呈现它们。
我不知所措。
参考:
public partial class DisciplineViewModel
{
public Guid DisciplineId { get; set; }
public string Discipline { get; set; }
}
以下是模型:
public partial class ProtocolViewModelEdit
{
[Key]
public Guid ProtocolId { get; set; }
[Display(Name = "Name")]
public string Protocol { get; set; }
public string ProtocolType { get; set; }
[Display(Name = "Type")]
public Guid ProtocolTypeId { get; set; }
[Display(Name = "Status")]
public Guid ProtocolStatusId { get; set; }
public virtual ICollection<ProtocolTypeViewModel> ProtocolTypes { get; set; }
public virtual ICollection<ProtocolStatusViewModel> ProtocolStatuses { get; set; }
public virtual ICollection<DisciplineViewModel> ProtocolDisciplines { get; set; }
public virtual ICollection<ProtocolXProgramViewModel> ProtocolPrograms { get; set; }
public virtual ICollection<DisciplineViewModel> Disciplines { get; set; }
public virtual ICollection<ProgramViewModel> Programs { get; set; }
}
答案 0 :(得分:1)
您提到了post on the MSDN forums,其中OP描述了以下内容:
1)selectedValues参数必须仅使用一组键值填充。它不能是所选对象的集合,因为HtmlHelper不会将dataValueField应用于此集合。
2)如果使用ListBox,则无法将name参数设置为与Model属性相同。此外,您不能将ViewBag属性命名为包含与Model属性相同的项集合。
3)如果使用ListBox,它会变得更加不稳定。您应该将ViewBag属性命名为包含与Model属性名称相同的项集合。现在,当您在View中使用ListBoxFor时,您必须使用不存在的ViewBag属性(这很重要!)。 HtmlHelper.ListBoxFor将自动查找具有指定的Model属性名称的ViewBag属性。
这些都不是实际问题。最终必须将SelectList
转换为HTML select
元素,该元素只能用于简单类型(字符串,整数等)。实际上,一切都是字符串,它只是模型绑定器的工作,它将发布的值转换为更具体的类型,如int
。因此,很明显,您无法绑定对象列表。
其他两个提到的问题是ModelState
的结果。绑定表单字段的值由ModelState
中的内容确定,Request
中的内容由ViewData
,ViewBag
/ Model
和最后{{1}的值组成作为最后的手段。如果您在SelectList
中设置ViewBag
,其名称与Model
上的媒体资源相同,那么ModelState
中该密钥的值将为SelectList
而非因此,您的选择将没有选定的项目,因为任何选项值都不会与SelectList
实例匹配。同样,这只是标准行为,而且它只是一个&#34; bug&#34;如果你不知道事情是如何运作的,并且没有意识到你正在做的事情的含义。
这里的问题正是第一个问题。您将对象列表作为选定值传递,并且根本无法将其正确绑定到HTML select元素。但是,如果您不打算创建自己的MultiSelectList
,事情会变得容易得多。所有帮助者需求都是IEnumerable<SelectListItem>
。 Razor将负责创建SelectList
/ MultiSelectList
并设置相应的选定值。只是做:
@Html.ListBoxFor(
m => m.ProtocolDisciplines,
Model.Disciplines.Select(d => new SelectListItem { Value = d.DisciplineId.ToString(), Text = d.Discipline }),
new { @class = "form-control", size = 8 }
)
<强>更新强>
回答关于Razor&#34;如何知道&#34;的问题,就像我在回答中说的那样,信息来自ModelState
。但是,正如斯蒂芬在下面的评论中指出的那样,你绑定它的属性是一组对象。这永远不会奏效。同样,HTML select元素的发布值始终是简单类型,而不是对象。因此,您需要一个属性,模型绑定器可以将发布的数据绑定到该属性,然后您需要使用该信息来查找所需的实际对象,然后再设置类似ProtocolDisciplines
属性的内容。换句话说:
public List<int> SelectedProtocolDisciplines { get; set; }
public IEnumerable<SelectListItem> DisciplineOptions { get; set; }
由于您使用的是视图模型,如果您在该视图模型中包含选择列表项,则会更好,因此我为其添加了一个属性。在您的操作(GET和POST)中,您需要设置此属性:
model.DisciplineOptions = model.Disciplines..Select(d => new SelectListItem {
Value = d.DisciplineId.ToString(),
Text = d.Discipline
});
由于您需要在GET和POST操作中调用它,您可能希望将其分解为控制器上可以调用的私有方法。然后,在您看来:
@Html.ListBoxFor(m => m.SelectedProtocolDisciplines, Model.DisciplineOptions, new { @class = "form-control" })
最后,在你的POST动作中:
var protocolDisciplines = db.Disciplines.Where(m => model.SelectedProtocolDisciplines.Contains(m.DisciplineId));
然后,如果这是&#34;创建&#34;方法,您可以使用它在您的实体上设置适当的属性。如果您正在编辑现有实体,则需要做更多工作:
// Remove deselected disciplines
entity.ProtocolDisciplines
.Where(m => !model.SelectedProtocolDisciplines.Contains(m.DisciplineId))
.ToList()
.ForEach(m => entity.ProtocolDisciplines.Remove(m));
// Add new selected disciplines
var addedDisciplineIds = model.SelectedProtocolDisciplines.Except(entity.ProtocolDisciplines.Select(m => m.DisciplineId));
db.Disciplines
.Where(m => addedDisciplineIds.Contains(m.DisciplineId))
.ToList()
.ForEach(m => entity.ProtocolDisciplines.Add(m));
这种额外的步法对于维持现有的,不变的M2M关系是必要的。