ASP.NET MVC - 为下拉菜单构建数据 (select/option, DropDownList/ListBox, SelectList/SelectListItem)

创建时间:
2014-06-08 10:19
最近更新:
2018-08-22 20:36

CodeGen 的生产代码 (2018-04-20)

一、在 Controller 中设置 ViewBag:

private void Set_ViewBag_TypeOfDotNet_Items(object selectedValue)
{
    Dictionary<byte, string> dic = new TypeOfDotNet_Bll().SltDic();
    IEnumerable<SelectListItem> items = Tl.SelectList.FromDic(dic, selectedValue);
    ViewBag.TypeOfDotNet_Items = items;
}

二、在 Action 中调用 示例:

Set_ViewBag_TypeOfDotNet_Items(null);

三、将 文本框 改为 下拉菜单:

@Html.EditorFor      (model => model.IdOfDotNetType)
@Html.DropDownListFor(model => model.IdOfDotNetType, ViewBag.TypeOfDotNet_Items as IEnumerable<SelectListItem>, "请选择")

CodeGen 的生产代码 (2018-04-15)

Controller

private void Set_ViewBag_TplGroup_Items(object selectedValue)
{
    Dictionary<uint, string> dic = new TplGroup_Bll().SltDic();
    IEnumerable<SelectListItem> items = Tl.SelectList.FromDic(dic, selectedValue);
    ViewBag.TplGroup_Items = items;
}

Create.cshtml & Edit.cshtml

<div class="form-group">
    @Html.LabelFor(model => model.IdOfTplGroup, "所属模板组", new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.DropDownListFor(model => model.IdOfTplGroup, ViewBag.TplGroup_Items as IEnumerable<SelectListItem>, "请选择", new { disabled = "disabled" })
        @Html.ValidationMessageFor(model => model.IdOfTplGroup)
    </div>
</div>

<div class="form-group">
    @Html.LabelFor(model => model.IdOfTplGroup, "所属模板组", new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.DropDownList("IdOfTplGroup", ViewBag.TplGroup_Items as IEnumerable<SelectListItem>, "请选择", new { disabled = "disabled" })
        @Html.ValidationMessageFor(model => model.IdOfTplGroup)
    </div>
</div>

测试记录

控制器 与 视图 如下时,select & option 生成成功,且 selected 绑定成功:

IEnumerable<SelectListItem> items = ...;
ViewBag.TplGroup_Items = items;
@Html.DropDownList("IdOfTplGroup", ViewBag.TplGroup_Items as IEnumerable<SelectListItem>, "请选择", new { disabled = "disabled" })
@Html.DropDownListFor(model => model.IdOfTplGroup, ViewBag.TplGroup_Items as IEnumerable<SelectListItem>, "请选择", new { disabled = "disabled" })

控制器 与 视图 如下时,select & option 生成成功,但 selected 绑定失败:

IEnumerable<SelectListItem> items = ...;
ViewBag.IdOfTplGroup = items;
@Html.DropDownList("IdOfTplGroup", null, "请选择", new { disabled = "disabled" })
@Html.DropDownListFor(model => model.IdOfTplGroup, null, "请选择", new { disabled = "disabled" })

Resource

  1. 在 ASP.NET MVC 中使用 DropDownList - DropDownList / DropDownListForname / expression 参数,被用于依次在 ViewData 与 Model 中搜索同名 属性,当 selectList 参数为 null 时,搜索到的数据视为 selectList,否则用于 selected

为下拉菜单构建数据 - IEnumerable<SelectListItem>

EnumHelper 类 - Provides methods for working with enumeration values and select lists.

以下示例摘自 《ASP.NET MVC 3 高级编程》 page 100:

Plan A

public ActionResult Edit(int id)
{
	var album = storeDB.Albums.Single(a => a.AlbumId == id);
	ViewBag.Genres = new SelectList(
		storeDB.Genres.OrderBy(g => g.Name),
		"GenreId",
		"Name",
		album.GenreId
	);
	return View(album);
}

Plan B

public ActionResult Edit(int id)
{
	var album = storeDB.Albums.Single(a => a.AlbumId == id);
	ViewBag.Genres = storeDB.Genres
		.OrderBy(g => g.Name)
		.AsEnumerable()
		.Select(g => new SelectListItem {
			Text = g.Name,
			Value = g.GenreId.ToString(),
			Selected = album.GenreId == g.GenreId
		});
	);
	return View(album);
}

以上 Action 不仅构建了主要模型 (用于编辑的专辑),还构建了 Html.DropDownList() 辅助方法所需要的表示模型。
相比 Plan A,Plan B 避免了一些反射开销。

select 中的 SnowflakeId 未能 ToNSystem()

2018-08-22 测试结果:

@Html.DropDownListFor(model => model.IdOfPrjDb, ViewBag.PrjDb_Items as IEnumerable<SelectListItem>, "请选择", new { disabled = "disabled" })

在 CodeGen 中有以上代码,
如果将 ViewBag.PrjDb_Items 中的每一项的 Value 都经 SnowflakeApi.ToNSystem(ulong.Parse(item.Value))ulong 转换为 NSystem 字符串,
那么,即使将 ViewBag.PrjDb_Items 中某项的 Selected 属性设置为 true,最终生成的 HTML 中 对应的 option 中仍不会有 selected="selected"
猜想其原因是 DropDownListFor() 会忽略 ViewBag.PrjDb_Items 中的 Selected,而使用 ulongValue 自行确定哪个项目应被选中。
因此,在没有找到解决方案之前,暂不对 select/option 中的 value 进行转换。
至此,除此之外的 SnowflakeId 都已经 ToNSystem() 处理后才输出至响应中。

Example 1

View

Create.cshtml & Edit.cshtml 均如下:

@model T.CodeGenerator.Dal.Edm_CodeGenerator.Property

@using (Html.BeginForm())
{
    ...
    <div class="form-group">
        @Html.LabelFor(model => model.ObjecxId, "ObjecxId", new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownList("ObjecxId", String.Empty)
            @Html.ValidationMessageFor(model => model.ObjecxId)
        </div>
    </div>
    ...
    <div class="form-group">
        @Html.LabelFor(model => model.SqlServerTypeId, "SqlServerTypeId", new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownList("SqlServerTypeId", String.Empty)
            @Html.ValidationMessageFor(model => model.SqlServerTypeId)
        </div>
    </div>
    ...
}

Controller

public ActionResult Create()
{
    ViewBag.ObjecxId = new SelectList(db.Objecx, "ObjecxId", "ObjecxName");
    ViewBag.SqlServerTypeId = new SelectList(db.SqlServerType, "SqlServerTypeId", "SqlServerTypeName");
    return View();
}

public ActionResult Create(Property property)
{
    if (ModelState.IsValid) {
        db.Property.Add(property);
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    ViewBag.ObjecxId = new SelectList(db.Objecx, "ObjecxId", "ObjecxName", property.ObjecxId);
    ViewBag.SqlServerTypeId = new SelectList(db.SqlServerType, "SqlServerTypeId", "SqlServerTypeName", property.SqlServerTypeId);
    return View(property);
}

public ActionResult Edit(int? id)
{
    Property property = db.Property.Find(id);
    ViewBag.ObjecxId = new SelectList(db.Objecx, "ObjecxId", "ObjecxName", property.ObjecxId);
    ViewBag.SqlServerTypeId = new SelectList(db.SqlServerType, "SqlServerTypeId", "SqlServerTypeName", property.SqlServerTypeId);
    return View(property);
}

public ActionResult Edit(Property property)
{
    if (ModelState.IsValid) {
        db.Entry(property).State = System.Data.Entity.EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    ViewBag.ObjecxId = new SelectList(db.Objecx, "ObjecxId", "ObjecxName", property.ObjecxId);
    ViewBag.SqlServerTypeId = new SelectList(db.SqlServerType, "SqlServerTypeId", "SqlServerTypeName", property.SqlServerTypeId);
    return View(property);
}

Example 2

View

@Html.DropDownList("status")
@Html.ValidationMessageFor(model => model.status)

Controller

public ActionResult Edit(int? id)
{
    if(id == null) {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    BookCode bookCode = BookCodeBll.GetById(id.Value);
    if(bookCode == null) {
        return HttpNotFound();
    }
    ViewBag.status = DictBll.GetSelectList("BookCodeStatus", selectedValue: bookCode.status);
    return View(bookCode);
}

DictBll.cs

public static class DictBll
{
    public static Dict[] GetBy(string dictType, int? flag = 1)
    {
        using(var db = new DbContext_FtnLibraryManagement()) {
            var query =
                from d in db.Dict
                select d;
            if(!string.IsNullOrWhiteSpace(dictType)) {
                query = query.Where(e => e.DictType == dictType);
            }
            if(flag.HasValue) {
                query = query.Where(e => e.Flag == flag.Value);
            }
            query = query.OrderBy(e => e.Seq);
            var array = query.ToArray();
            return array;
        }
    }

    public static SelectList GetSelectList(string dictType, int? flag = 1, object selectedValue = null)
    {
        Dict[] dicts = GetBy(dictType, flag);
        var selectList = new SelectList(dicts, "DictCode", "DictValue", selectedValue);
        return selectList;
    }

}

常见异常

异常详细信息: System.InvalidOperationException: 具有键“***”的 ViewData 项属于类型“System.***”,但它必须属于类型“IEnumerable<SelectListItem>”。

解决方案: 在 post-action 中,需要重复 get-action 中的 ViewBag.*** = new SelectList();