一、RadioButtonListFor的使用
我们先来显示一下扩展的RadioButtonListFor的方法的用法。如下所示的是作为Model的Person类型,其Gender(Male/Female)、MaritalStatus(Single/Married)和Country的选项列表通过独立的组件CodeManager进行单独维护。
在一个针对Person对象进行编辑的强类型View(以Person作为Model)中我们进行了如下的定义。RadioButtonListFor方的最后一个参数(“Gender”、“MaritalStatus”和“Country”)表示对应列表的类别。
1: @using System.Web.UI.WebControls 2: @model Person 3: @{ 4: ViewBag.Title = "Index"; 5: } 6: @using (Html.BeginForm()) 7: { 8: <table id="container"> 9: <tr> 10: <td class="label">@Html.LabelFor(m => m.Name):td> 11: <td>@Html.EditorFor(m => m.Name)td> 12: tr> 13: <tr> 14: <td class="label">@Html.LabelFor(m => m.Gender):td> 15: <td>@Html.RadioButtonListFor(m => m.Gender, "Gender")td> 16: tr> 17: <tr> 18: <td class="label">@Html.LabelFor(m => m.MaritalStatus):td> 19: <td>@Html.RadioButtonListFor(m => m.MaritalStatus, "MaritalStatus")td> 20: tr> 21: <tr> 22: <td class="label">@Html.LabelFor(m => m.Country):td> 23: <td>@Html.RadioButtonListFor(m => m.Country, "Country", RepeatDirection.Vertical)td> 24: tr> 25: <tr> 26: <td colspan="2"><input type="submit" value="Save" />td> 27: tr> 28: table> 29: }
下面这张图表示上面这个View在浏览器中呈现出来的样式,我们可以看到三组RadioButton被有效地生成出来。
二、维护选项列表的组件CodeManager
由于在一个应用中,作为绑定到“列表控件”上的选项列表可能会有很多,将它们进行单独地维护是一个理想的选择。作为模拟,我们创建了如下一个简单的CodeManager组件。我们将列表中的某个选项通过CodeDescription,其三个属性Code、Description分别表示其“值”和“显示文本”,Category表示类别(通过它对列表项进行分组)。CodeManager通过一个静态字段作为列表数据的存储,上面例子中使用到的三组列表维护于此。GetCodes方法用于返回指定“类别”的列表选项。
1: public class CodeDescription 2: { 3: public string Code { get; set; } 4: public string Description { get; set; } 5: public string Category{get;set;} 6: 7: public CodeDescription(string code, string description, string category) 8: { 9: this.Code = code; 10: this.Description = description; 11: this.Category = category; 12: } 13: } 14: public static class CodeManager 15: { 16: private static CodeDescription[] codes = new CodeDescription[] 17: { 18: new CodeDescription("M","Male","Gender"), 19: new CodeDescription("F","Female","Gender"), 20: new CodeDescription("S","Single","MaritalStatus"), 21: new CodeDescription("M","Married","MaritalStatus"), 22: new CodeDescription("CN","China","Country"), 23: new CodeDescription("US","Unite States","Country"), 24: new CodeDescription("UK","Britain","Country"), 25: new CodeDescription("SG","Singapore","Country") 26: }; 27: public static Collection GetCodes(string category) 28: { 29: Collection codeCollection = new Collection(); 30: foreach(var code in codes.Where(code=>code.Category == category)) 31: { 32: codeCollection.Add(code); 33: } 34: return codeCollection; 35: } 36: }
三、RadioButtonList/RadioButtonListFor扩展方法
如下所示的是RadioButtonList/RadioButtonListFor两个扩展方法的定义,参数codeCategory表示作为数据源的列表类别,而RepeatDirection 枚举类型则代表同组的RadioButton的排列方向,默认为水平。从上面的例子我们可以看到,我们通过这个参数将名称为Country的RadioButtonList进行了纵向排列。通过Html生成代码我们可以看出,我们采用了Table作为布局方式,实际上ASP.NET的RadioButtonList也是这么做的。
1: public static class RadioButtonListExtensions 2: { 3: public static MvcHtmlString RadioButtonList(this HtmlHelper htmlHelper, string name, string codeCategory, RepeatDirection repeatDirection = RepeatDirection.Horizontal, IDictionary<string, object> htmlAttributes = null) 4: { 5: var codes = CodeManager.GetCodes(codeCategory); 6: return GenerateHtml(name, codes, repeatDirection, htmlAttributes, null); 7: } 8: public static MvcHtmlString RadioButtonListFor(this HtmlHelper htmlHelper, Expression> expression, string codeCategory, RepeatDirection repeatDirection = RepeatDirection.Horizontal, IDictionary<string, object> htmlAttributes = null) 9: { 10: var codes = CodeManager.GetCodes(codeCategory); 11: 12: ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); 13: string name = ExpressionHelper.GetExpressionText(expression); 14: var attributes = htmlHelper.GetUnobtrusiveValidationAttributes(name, metadata); 15: foreach (var item in attributes) 16: { 17: htmlAttributes.Add(item); 18: } 19: string fullHtmlFieldName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name); 20: string stateValue = (string)metadata.Model; 21: return GenerateHtml(fullHtmlFieldName, codes, repeatDirection, htmlAttributes, stateValue); 22: } 23: private static MvcHtmlString GenerateHtml(string name, Collection codes, RepeatDirection repeatDirection, IDictionary<string, object> htmlAttributes, string stateValue = null) 24: { 25: TagBuilder table = new TagBuilder("table"); 26: int i = 0; 27: if (repeatDirection == RepeatDirection.Horizontal) 28: { 29: TagBuilder tr = new TagBuilder("tr"); 30: foreach (var code in codes) 31: { 32: i++; 33: string id = string.Format("{0}_{1}", name, i); 34: TagBuilder td = new TagBuilder("td"); 35: td.InnerHtml = GenerateRadioHtml(name, id, code.Description, code.Code, (stateValue != null && stateValue == code.Code), htmlAttributes); 36: tr.InnerHtml+=td.ToString(); 37: } 38: table.InnerHtml = tr.ToString(); 39: } 40: else 41: { 42: foreach (var code in codes) 43: { 44: TagBuilder tr = new TagBuilder("tr"); 45: i++; 46: string id = string.Format("{0}_{1}", name, i); 47: TagBuilder td = new TagBuilder("td"); 48: td.InnerHtml = GenerateRadioHtml(name, id, code.Description, code.Code, (stateValue != null && stateValue == code.Code), htmlAttributes); 49: tr.InnerHtml = td.ToString(); 50: table.InnerHtml += tr.ToString(); 51: } 52: } 53: return new MvcHtmlString(table.ToString()); 54: } 55: 56: private static string GenerateRadioHtml(string name, string id, string labelText, string value, bool isChecked, IDictionary<string, object> htmlAttributes) 57: { 58: StringBuilder sb = new StringBuilder(); 59: 60: TagBuilder label = new TagBuilder("label"); 61: label.MergeAttribute("for", id); 62: label.SetInnerText(labelText); 63: 64: TagBuilder input = new TagBuilder("input"); 65: input.GenerateId(id); 66: input.MergeAttribute("name", name); 67: input.MergeAttribute("type", "radio"); 68: input.MergeAttribute("value", value); 69: input.MergeAttributes(htmlAttributes); 70: if (isChecked) 71: { 72: input.MergeAttribute("checked", "checked"); 73: } 74: sb.AppendLine(input.ToString()); 75: sb.AppendLine(label.ToString()); 76: return sb.ToString(); 77: } 78: }