[ASP.NET MVC]通过对HtmlHelper扩展简化“列表控件”的绑定
在众多表单元素中,有一类<select>元素用于绑定一组预定义列表。传统的ASP.NET Web Form中,它对应着一组重要的控件类型,即ListControl,我们经常用到DropDownList, ListBox、CheckBoxList和RadioButtonList都是其子类。ASP.NET MVC通过对HtmlHelper和HtmlHelper<TModel>的扩展实现了对不同类型的<select>元素的绑定,它们以扩展方法的形式定义在SelectExtensions中。当我们在操作这些扩展方法的时候,必须手工地提供以 IEnumerable<SelectListItem>对象表示的列表项。如果我们建立一个独立的组件来维护这些预定的列表,那么我们就可以定义一些更加简单的扩展方法以避免手工地指定列表项。[源代码从这里下载]
一、创建一个独立的列表维护组件
我们将这些绑定在<select>元素中的预定义列表中的元素称为Code。作为简单的演示模拟,我们创建了一个名为CodeManager的组件。我们先来看看用于描述单一Code的CodeDescription类型的定义,如下面的代码所示,CodeDescription具有ID、Code、Description、EffectiveStartDate 和EffectiveEndDate。以表示国家的列表为例,Code代表某个国家的代码(比如CN),Description则是一个可读性的描述(比如China)。EffectiveStartDate 和EffectiveEndDate决定了Code的有效期限,比如一组表示“税率”的列表,在不同的时间范围内可能采用不同的列表。换言之,作为统一类别(通过Category属性表示)的列表中可能具有“多套”,它们可以共享相同的Code,我们通过ID来区分这些具有相同Code的列表项。
1: public class CodeDescription
2: {
3: public string Id { get; set; }
4: public string Code { get; set; }
5: public string Description { get; set; }
6: public string Category{get;set;}
7: public DateTime EffectiveStartDate { get; set; }
8: public DateTime EffectiveEndDate { get; set; }
9:
10: public CodeDescription(string code, string description, string category)
11: {
12: this.Id = Guid.NewGuid().ToString();
13: this.Code = code;
14: this.Description = description;
15: this.Category = category;
16: this.EffectiveStartDate = DateTime.MinValue;
17: this.EffectiveEndDate = DateTime.MaxValue;
18: }
19: }
如下所示的CodeCollection 表示一组CodeDescription列表,它直接继承自Collection<CodeDescription>类型。由于CodeDescription具有有效期限的概念,我们需要筛选出当前有效的Code,所以我们定义了如下一个GetEffectiveCodes方法。
1: public class CodeCollection : Collection<CodeDescription>
2: {
3: public IEnumerable<CodeDescription> GetEffectiveCodes()
4: {
5: return this.Where(code => code.EffectiveStartDate <= DateTime.Today && code.EffectiveEndDate >= DateTime.Today).ToList();
6: }
7: }
在进行Code绑定的时候,我们都是“类别”为单位的。我们总是获取某一个类别(比如国家、性别、婚姻状况和政治面貌等)的Code列表绑定到界面上。如下所示的CodeManager定义了一个GetCode方法获取指定类别的Code列表。而作为Code存储,我们采用了静态字段的形式,从如下所示的代码可以看出我们实际定义了三类Code,即Gender、MaritalStatus和Country,分别表示性别、婚姻状况和国籍。
1: public static class CodeManager
2: {
3: private static CodeDescription[] codes = new CodeDescription[]
4: {
5: new CodeDescription("M","Male","Gender"),
6: new CodeDescription("F","Female","Gender"),
7: new CodeDescription("S","Single","MaritalStatus"),
8: new CodeDescription("M","Married","MaritalStatus"),
9: new CodeDescription("CN","China","Country"),
10: new CodeDescription("US","Unite States","Country"),
11: new CodeDescription("UK","Britain","Country"),
12: new CodeDescription("SG","Singapore","Country")
13: };
14: public static CodeCollection GetCodes(string category)
15: {
16: CodeCollection codeCollection = new CodeCollection();
17: foreach(var code in codes.Where(code=>code.Category == category))
18: {
19: codeCollection.Add(code);
20: }
21: return codeCollection;
22: }
23: }
二、定义HtmlHelper的扩展方法实现基于“列表类别”的绑定
现在我们来定义针对HtmlHelper的扩展方法通过从CodeManager获取的Code列表来进行“列表控件”的绑定。表示列表项的SelectListItem具有Text和Value两个属性,分别表示显示的文本和对应的值。在默认的情况下,它们应该对应于CodeDescription的Description和Code,但是有时候却需要进行相应的定制。比如说,有时候我们希望通过CodeDescription的ID来作为SelectListItem的值,或者说通过将SelectListItem显示为Code和Description的组合,比如“CN-China”。为此我们定义了如下一个BindingOption类型。
1: public class BindingOption
2: {
3: public string OptionalLabel { get; set; }
4: public string TextTemplate { get; set; }
5: public string ValueTemplate { get; set; }
6:
7: public BindingOption()
8: {
9: this.OptionalLabel = null;
10: this.TextTemplate = "{Description}";
11: this.ValueTemplate = "{Code}";
12: }
13: }
OptionalLabel表示添加的提示性的文本(比如“请选择一个Xxx”),而TextTemplate 和ValueTemplate 表示最终作为SelectListItem的Text和Value属性的模板,模板中包含相应的站位符({Id}、{Code}和{Description})。
我们为HtmlHelper编写了如下4个扩展方法用于针对DropDownList和ListBox的绑定,在参数中我们无须提供SelectListItem列表,而只需要提供Code和类别即可。而BindingOption 决定了最终作为SelectListItem的Text和Value属性,以及是否需要添加一个提示性的文字和文字内容。在真正的项目中,我们可以将BindingOption的设置定义在配置文件中。
1: public static class SelectExtensions
2: {
3: public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, string codeCategory, BindingOption bindingOption = null)
4: {
5: bindingOption = bindingOption ?? new BindingOption();
6: var listItems = GenerateListItems(codeCategory, bindingOption);
7: return htmlHelper.DropDownList(name, listItems, bindingOption.OptionalLabel);
8: }
9: public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string codeCategory, BindingOption bindingOption = null)
10: {
11: bindingOption = bindingOption ?? new BindingOption();
12: var listItems = GenerateListItems(codeCategory, bindingOption);
13: return htmlHelper.DropDownListFor<TModel, TProperty>(expression, listItems,bindingOption.OptionalLabel);
14: }
15:
16: public static MvcHtmlString ListBox(this HtmlHelper htmlHelper, string name, string codeCategory, BindingOption bindingOption = null)
17: {
18: bindingOption = bindingOption ?? new BindingOption();
19: var listItems = GenerateListItems(codeCategory, bindingOption);
20: return htmlHelper.ListBox(name, listItems, bindingOption.OptionalLabel);
21: }
22: public static MvcHtmlString ListBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string codeCategory, BindingOption bindingOption = null)
23: {
24: bindingOption = bindingOption ?? new BindingOption();
25: var listItems = GenerateListItems(codeCategory, bindingOption);
26: return htmlHelper.ListBoxFor<TModel, TProperty>(expression, listItems);
27: }
28:
29: public static IEnumerable<SelectListItem> GenerateListItems(string codeCategory, BindingOption bindingOption)
30: {
31: var items = new List<SelectListItem>();
32: foreach (var code in CodeManager.GetCodes(codeCategory).GetEffectiveCodes())
33: {
34: var item = new SelectListItem
35: {
36: Text = FormatTemplate(bindingOption.TextTemplate, code),
37: Value = FormatTemplate(bindingOption.ValueTemplate, code)
38: };
39: items.Add(item);
40: }
41: return items;
42: }
43:
44: private static string FormatTemplate(string template, CodeDescription code)
45: {
46: return template.Replace("{Id}", code.Id)
47: .Replace("{Code}", code.Code)
48: .Replace("{Description}", code.Description);
49: }
50: }
三、使用这些扩展方法
现在我们创建一个简单的ASP.NET MVC应用来演示对DropDownList和ListBox的绑定。为此我们定义了如下一个Person类型,其Gender、MaritalStatus 和Country 属性分别对应于CodeManager维护的三组Code。在创建的HomeController中,我们将初始化Person对象的呈现定义在Index操作中。
1: public class Person
2: {
3: public string Name { get; set; }
4: public string Gender { get; set; }
5: [Display(Name = "MaritalStatus")]
6: public string MaritalStatus { get; set; }
7: public string Country { get; set; }
8: }
9:
10: public class HomeController : Controller
11: {
12: public ActionResult Index()
13: {
14: return View(new Person
15: {
16: Name = "Zhan San",
17: Gender = "M",
18: Country = "CN",
19: MaritalStatus = "S"
20: });
21: }
22: }
我们定义的扩展方法是用在Index操作定义的Index.cshtml视图中,下面是Index.cshtml的定义:
1: @model CodeBinding.Models.Person
2: @{
3: ViewBag.Title = "Index";
4: }
5:
6: <table>
7: <tr>
8: <td>@Html.LabelFor(m=>m.Name)</td>
9: <td>@Html.TextBoxFor(m=>m.Name)</td>
10: </tr>
11: <tr>
12: <td>@Html.LabelFor(m=>m.Gender)</td>
13: <td>@Html.DropDownListFor(m => m.Gender, "Gender", new BindingOption
14: {
15: OptionalLabel = "Please select your gender...",
16: TextTemplate = "{Code}-{Description}",
17: ValueTemplate = "{Code}"
18: })</td>
19: </tr>
20: <tr>
21: <td>@Html.LabelFor(m=>m.MaritalStatus)</td>
22: <td>@Html.DropDownListFor(m => m.MaritalStatus, "MaritalStatus",new BindingOption
23: {
24: OptionalLabel = "Please select your marital status...",
25: TextTemplate = "{Code}-{Description}",
26: ValueTemplate = "{Code}"
27: })</td>
28: </tr>
29: <tr>
30: <td>@Html.LabelFor(m=>m.Country)</td>
31: <td>@Html.ListBoxFor(m => m.Country, "Country")</td>
32: </tr>
33: </table>
最后看看最终呈现出来的效果:
- Spring的事务传播行为
- Jquery 获取checkbox属性checked为undefined和改变状态 值不变
- Hibernate--Criteria Query and DetachedCriteria
- Python3 大作战之 encode 与 decode 讲解
- SpringMVC 文件下载时 浏览器不能正确显示另存的文件名
- 手把手教你用python抢火车票
- SpringMVC 实现多文件上传的两种方式及其效率比较
- Spring 之加载配置属性文件和注解属性绑定
- SpringMVC 提交表单400 Bad Request
- 如何在三年内快速成长为一名技术专家
- 【微信开发】getAccessToken 和 getJsapi_ticket缓存支持
- 微信JS-SDK 注入权限验证配置
- Maven 项目下slf4j 包冲突问题
- 谷歌教你学 AI-第三讲简单易懂的估算器
- HTML 教程
- HTML 简介
- html div 标签介绍
- html span 标签介绍
- html a 超链接标签
- HTML Br换行标签介绍
- HTML P段落标签介绍
- HTML br与p标签区别
- Html H 标题标签
- html px em pt长度单位
- HTML form 标签
- HTML radio 单选框
- HTML B 加粗标签
- HTML strong加粗粗体标签
- HTML em 强调标签
- HTML i 斜体标签
- HTML u下划线标签
- HTML s 删除线标签
- Html img 图片标签
- Html上标注sup与下标注sub标签
- HTML nobr 禁止换行标签
- HTML hr 水平线标签
- HTML label 标签
- HTML input 标签
- HTML textarea 标签
- HTML select下拉列表标签
- HTML checkbox 多选框
- HTML font color 标签
- HTML iframe 框架标签
- HTML Table 表格
- HTML dl dt dd 标签
- HTML ol li有序列表标签
- HTML ul li 无序列表标签
- HTML 注释
- CSS 教程
- CSS 简介
- CSS 语法
- CSS Id 和 Class选择器
- CSS 样式的创建
- CSS background 背景介绍
- CSS 文本样式
- CSS font 字体
- CSS A 链接
- CSS ul ol列表样式
- CSS TABLE 样式
- CSS 框模型
- CSS border 边框
- CSS Outlines 轮廓
- CSS 外边距 Margin
- CSS Padding 内边距
- CSS 分组和嵌套选择器
- CSS 尺寸 (Dimension)
- CSS Display 属性
- CSS Position 定位
- CSS Float 浮动
- CSS 水平对齐(Horizontal Align)
- CSS 组合选择符
- CSS 伪类
- CSS 伪元素
- CSS 导航栏
- CSS 下拉菜单
- CSS 图片廊
- CSS 图像透明/不透明
- CSS sprite 图像拼合技术
- CSS 媒体类型
- CSS 属性选择器
- CSS 实例
- Android 天气APP(二十九)壁纸设置、图片查看、图片保存
- Chrome 私人珍藏-stylus插件实现个性化百度界面定制
- Python 基础篇-简单的异常捕获
- Python 技巧篇-让我的程序暂停一下
- Python+selenium 技术篇-浏览器后台运行
- Python 基础篇-python3安装pyHook和pywin32库
- 漫画:如何螺旋遍历二维数组?(修订版)
- 一文快速入门分库分表(必修课)
- 写出漂亮 Python 代码的 20条准则
- 简单red5+obs推流实现直播系统开发,具体设置介绍
- 使用pandas进行数据快捷加载
- 关于 JavaScript 中 null 的一切
- 总结 | DataFrame、Series、array、tensor的创建及相互转化
- 这就是你日日夜夜想要的docker!!!---------Harbor私有仓库
- Go by Example 中文版: 写文件