有没有人为ASP.NET MVC实现RadioButtonListFor <T>?

ASP.NET MVC Futures中有一个Html.RadioButtonList扩展方法。 有没有人find强types的RadioButtonListFor<T> 。 这样看来是这样的:

 <%= Html.RadioButtonListFor(model=>model.Item,Model.ItemList) %> 

这是aspx页面的用法

  <%= Html.RadioButtonListFor(m => m.GenderRadioButtonList)%> 

这是视图模型

 public class HomePageViewModel { public enum GenderType { Male, Female } public RadioButtonListViewModel<GenderType> GenderRadioButtonList { get; set; } public HomePageViewModel() { GenderRadioButtonList = new RadioButtonListViewModel<GenderType> { Id = "Gender", SelectedValue = GenderType.Male, ListItems = new List<RadioButtonListItem<GenderType>> { new RadioButtonListItem<GenderType>{Text = "Male", Value = GenderType.Male}, new RadioButtonListItem<GenderType>{Text = "Female", Value = GenderType.Female} } }; } } 

这是用于单选button列表的视图模型

 public class RadioButtonListViewModel<T> { public string Id { get; set; } private T selectedValue; public T SelectedValue { get { return selectedValue; } set { selectedValue = value; UpdatedSelectedItems(); } } private void UpdatedSelectedItems() { if (ListItems == null) return; ListItems.ForEach(li => li.Selected = Equals(li.Value, SelectedValue)); } private List<RadioButtonListItem<T>> listItems; public List<RadioButtonListItem<T>> ListItems { get { return listItems; } set { listItems = value; UpdatedSelectedItems(); } } } public class RadioButtonListItem<T> { public bool Selected { get; set; } public string Text { get; set; } public T Value { get; set; } public override string ToString() { return Value.ToString(); } } 

这里是RadioButtonListFor的扩展方法

 public static class HtmlHelperExtensions { public static string RadioButtonListFor<TModel, TRadioButtonListValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, RadioButtonListViewModel<TRadioButtonListValue>>> expression) where TModel : class { return htmlHelper.RadioButtonListFor(expression, null); } public static string RadioButtonListFor<TModel, TRadioButtonListValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, RadioButtonListViewModel<TRadioButtonListValue>>> expression, object htmlAttributes) where TModel : class { return htmlHelper.RadioButtonListFor(expression, new RouteValueDictionary(htmlAttributes)); } public static string RadioButtonListFor<TModel, TRadioButtonListValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, RadioButtonListViewModel<TRadioButtonListValue>>> expression, IDictionary<string, object> htmlAttributes) where TModel : class { var inputName = GetInputName(expression); RadioButtonListViewModel<TRadioButtonListValue> radioButtonList = GetValue(htmlHelper, expression); if (radioButtonList == null) return String.Empty; if (radioButtonList.ListItems == null) return String.Empty; var divTag = new TagBuilder("div"); divTag.MergeAttribute("id", inputName); divTag.MergeAttribute("class", "radio"); foreach (var item in radioButtonList.ListItems) { var radioButtonTag = RadioButton(htmlHelper, inputName, new SelectListItem{Text=item.Text, Selected = item.Selected, Value = item.Value.ToString()}, htmlAttributes); divTag.InnerHtml += radioButtonTag; } return divTag + htmlHelper.ValidationMessage(inputName, "*"); } public static string GetInputName<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression) { if (expression.Body.NodeType == ExpressionType.Call) { var methodCallExpression = (MethodCallExpression)expression.Body; string name = GetInputName(methodCallExpression); return name.Substring(expression.Parameters[0].Name.Length + 1); } return expression.Body.ToString().Substring(expression.Parameters[0].Name.Length + 1); } private static string GetInputName(MethodCallExpression expression) { // p => p.Foo.Bar().Baz.ToString() => p.Foo OR throw... var methodCallExpression = expression.Object as MethodCallExpression; if (methodCallExpression != null) { return GetInputName(methodCallExpression); } return expression.Object.ToString(); } public static string RadioButton(this HtmlHelper htmlHelper, string name, SelectListItem listItem, IDictionary<string, object> htmlAttributes) { var inputIdSb = new StringBuilder(); inputIdSb.Append(name) .Append("_") .Append(listItem.Value); var sb = new StringBuilder(); var builder = new TagBuilder("input"); if (listItem.Selected) builder.MergeAttribute("checked", "checked"); builder.MergeAttribute("type", "radio"); builder.MergeAttribute("value", listItem.Value); builder.MergeAttribute("id", inputIdSb.ToString()); builder.MergeAttribute("name", name + ".SelectedValue"); builder.MergeAttributes(htmlAttributes); sb.Append(builder.ToString(TagRenderMode.SelfClosing)); sb.Append(RadioButtonLabel(inputIdSb.ToString(), listItem.Text, htmlAttributes)); sb.Append("<br>"); return sb.ToString(); } public static string RadioButtonLabel(string inputId, string displayText, IDictionary<string, object> htmlAttributes) { var labelBuilder = new TagBuilder("label"); labelBuilder.MergeAttribute("for", inputId); labelBuilder.MergeAttributes(htmlAttributes); labelBuilder.InnerHtml = displayText; return labelBuilder.ToString(TagRenderMode.Normal); } public static TProperty GetValue<TModel, TProperty>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) where TModel : class { TModel model = htmlHelper.ViewData.Model; if (model == null) { return default(TProperty); } Func<TModel, TProperty> func = expression.Compile(); return func(model); } } 

MVC 3的例子,创build3个单选button进行validation,以确保select1选项。 如果表单validation失败(例如,在其他字段中),当表单被重新显示时,select的无线电选项被预先select。

视图

 @Html.RadioButtonForSelectList(m => m.TestRadio, Model.TestRadioList) @Html.ValidationMessageFor(m => m.TestRadio) 

模型

 public class aTest { public Int32 ID { get; set; } public String Name { get; set; } } public class LogOnModel { public IEnumerable<SelectListItem> TestRadioList { get; set; } [Required(ErrorMessage="Test Error")] public String TestRadio { get; set; } [Required] [Display(Name = "User name")] public string UserName { get; set; } } 

控制器操作

 public ActionResult LogOn() { List<aTest> list = new List<aTest>(); list.Add(new aTest() { ID = 1, Name = "Line1" }); list.Add(new aTest() { ID = 2, Name = "Line2" }); list.Add(new aTest() { ID = 3, Name = "Line3" }); SelectList sl = new SelectList(list, "ID", "Name"); var model = new LogOnModel(); model.TestRadioList = sl; return View(model); } [HttpPost] public ActionResult LogOn(LogOnModel model, string returnUrl) { if (ModelState.IsValid) { .... } // If we got this far, something failed, redisplay form List<aTest> list = new List<aTest>(); list.Add(new aTest() { ID = 1, Name = "Line1" }); list.Add(new aTest() { ID = 2, Name = "Line2" }); list.Add(new aTest() { ID = 3, Name = "Line3" }); SelectList sl = new SelectList(list, "ID", "Name"); model.TestRadioList = sl; return View(model); } 

这里是扩展名:

 public static class HtmlExtensions { public static MvcHtmlString RadioButtonForSelectList<TModel, TProperty>( this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> listOfValues) { var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); var sb = new StringBuilder(); if (listOfValues != null) { foreach (SelectListItem item in listOfValues) { var id = string.Format( "{0}_{1}", metaData.PropertyName, item.Value ); var radio = htmlHelper.RadioButtonFor(expression, item.Value, new { id = id }).ToHtmlString(); sb.AppendFormat( "<label for=\"{0}\">{1}</label> {2}", id, HttpUtility.HtmlEncode(item.Text), radio ); } } return MvcHtmlString.Create(sb.ToString()); } } 

好的,我知道这不是直接回答你的问题,但这可能是一个更好的方式来做大部分投入(这是很有趣的做法)。 我刚刚完成了这个testing,并且对它进行了一些testing,所以我不能保证在任何情况下都是完美的。

我从Jimmy Bogard的post中得到了这个想法。 看一下,因为那里有一堆非常酷的想法。

我所做的是创build一个“InputFor”助手,它将尽最大努力找出你所要求的input并相应地输出。 这将做单选button,但如果有两个以上的时候默认为下拉菜单,你应该可以很容易地改变这个function。

以下代码允许您调用诸如<%= Html.InputFor(m => m.Gender) %><%Html.InputFor(m => m.Gender, Model.GenderList)%> 。 最后有个很酷的地方可以让你按照惯例进行编码,但是我们稍后会做到这一点。

 public static MvcHtmlString InputFor<TModel>(this HtmlHelper<TModel> helper, Expression<Func<TModel, object>> field, Dictionary<string, string> listing) where TModel : class { string property_name = GetInputName(field); PropertyDescriptor descriptor = TypeDescriptor.GetProperties(helper.ViewData.Model).Find(property_name, true); string property_type = descriptor.PropertyType.Name; var func = field.Compile(); var value = func(helper.ViewData.Model); //Add hidden element if required if (descriptor.Attributes.Contains(new HiddenInputAttribute())) { return helper.Hidden(property_name, value); } if (property_type == "DateTime" || property_type == "Date") { return helper.TextBox(property_name, value, new { @class = "date_picker" }); } if (listing != null) { if (listing.Count <= 2) { //This is a good length for a radio button string output = ""; foreach (KeyValuePair<string, string> pair in listing) { TagBuilder label = new TagBuilder("label"); label.MergeAttribute("for", property_name); label.SetInnerText(pair.Value); output += helper.RadioButton(property_name, pair.Key, (value == pair.Key)).ToHtmlString(); output += label.ToString(); } return MvcHtmlString.Create(output); } else { //too big for a radio button, lets make a drop down return helper.DropDownList(property_name, new SelectList(listing, "Key", "Value"), value); } } else { if (property_type == "Boolean") { listing = new Dictionary<string, string>(); listing.Add("true", "Yes"); listing.Add("false", "No"); SelectList select_values = new SelectList(listing, "Key", "Value", ((bool)value ? "Yes" : "No")); return helper.DropDownList(property_name, select_values); } return helper.TextBox(property_name, value); } } 

按公约编码

下面的代码允许通过configuration来实现这一点。 一个例子是,如果你有一个模型对象,其中包含你想列出的属性(性别)和一个同名的字典,但附加了“列表”(GenderList),那么它将默认使用这个列表。

例如<%= Html.InputFor(m => m.Gender) %>可以完全下拉列表/单选button组,但是这些默认值可以通过调用像<%= Html.InputFor(m => m.Gender, alternate_list) %>

 public static MvcHtmlString InputFor<TModel>(this HtmlHelper<TModel> helper, Expression<Func<TModel, object>> field) where TModel : class { string property_name = GetInputName(field) + "List"; PropertyDescriptor list_descriptor = TypeDescriptor.GetProperties(helper.ViewData.Model).Find(property_name, true); Dictionary<string, string> listing = null; if (list_descriptor != null) { //Found a match for PropertyNameList, try to pull it out so we can use it PropertyInfo temp = helper.ViewData.Model.GetType().GetProperty(property_name); listing = (Dictionary<string, string>)temp.GetValue(helper.ViewData.Model, null); } return InputFor(helper, field, listing); } 

现在稍微声明一下:

  • 这不是世界上最快的代码(由于反思和其他事情),在我的情况下,这不是真正的相关,因为它是所有的用户驱动,如果你打算做一些疯狂的愚蠢。
  • 这段代码还处于起步阶段,我将在未来几天内对此进行更彻底的testing,并对其进行改进,并提出改进代码的build议。

我希望这个代码对某个人有用,我知道在接下来的几个星期里我会用它来缩短时间。 切割这个只是做单选button应该是一个微不足道的任务,祝你好运:)

松鸦

基于Jon post ,使用HTMLAttribute生成单选button列表ul的小改进

 public static MvcHtmlString RadioButtonListFor<TModel, TProperty>( this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> listOfValues, IDictionary<string, object> radioHtmlAttributes = null, string ulClass = null) { ModelMetadata metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); if (radioHtmlAttributes == null) radioHtmlAttributes = new RouteValueDictionary(); TagBuilder ulTag = new TagBuilder("ul"); if (!String.IsNullOrEmpty(ulClass)) ulTag.MergeAttribute("class", ulClass); if (listOfValues != null) { // Create a radio button for each item in the list foreach (SelectListItem item in listOfValues) { // Generate an id to be given to the radio button field var id = string.Format("{0}_{1}", metaData.PropertyName, item.Value); if (!radioHtmlAttributes.ContainsKey("id")) radioHtmlAttributes.Add("id", id); else radioHtmlAttributes["id"] = id; // Create and populate a radio button using the existing html helpers var label = htmlHelper.Label(id, HttpUtility.HtmlEncode(item.Text)); var radio = htmlHelper.RadioButtonFor(expression, item.Value, radioHtmlAttributes).ToHtmlString(); // Create the html string that will be returned to the client // eg <input data-val="true" data-val-required="You must select an option" id="TestRadio_1" name="TestRadio" type="radio" value="1" /><label for="TestRadio_1">Line1</label> ulTag.InnerHtml += string.Format("<li>{0}{1}</li>", radio, label); } } return MvcHtmlString.Create(ulTag.ToString(TagRenderMode.Normal)); } public static MvcHtmlString RadioButtonListFor<TModel, TProperty>( this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> listOfValues, object radioHtmlAttributes = null, string ulClass = null) { return RadioButtonListFor<TModel, TProperty>(htmlHelper, expression, listOfValues, new RouteValueDictionary(radioHtmlAttributes), ulClass); } 

我在MVC 1.0中实现了类似的东西。 看看这是否会对你有所帮助:

  public static string RadioButtonList2(this HtmlHelper _helper, string _name, IEnumerable<SelectListItem> _items, string _selectedValue, string _seperator) { return RadioButtonList2(_helper, _name, _items, _selectedValue, _seperator, null); } public static string RadioButtonList2(this HtmlHelper _helper, string _name, IEnumerable<SelectListItem> _items, string _selectedValue, string _seperator, IDictionary<string, object> _htmlAttributes) { StringBuilder _outputScript = new StringBuilder(); foreach (var item in _items) { var optionField = new TagBuilder("input"); optionField.MergeAttribute("name", _name); optionField.MergeAttribute("id", _name); optionField.MergeAttribute("class", _name); optionField.MergeAttribute("value", item.Value); optionField.MergeAttribute("type", "radio"); // Check to see if it's checked if (item.Value == _selectedValue) optionField.MergeAttribute("checked", "checked"); if (_htmlAttributes != null) optionField.MergeAttributes(_htmlAttributes); _outputScript.Append(optionField.ToString(TagRenderMode.SelfClosing)); _outputScript.Append("<label style=\"display:inline;\">"); _outputScript.Append(item.Text); _outputScript.Append("</label>" + _seperator); } return _outputScript.ToString(); } 

在控制器中,你可以返回结果如下:

  ViewData["GenderList"] = new SelectList(new[] { new { Value = "M", Text = "Male" }, new { Value = "F", Text = "Female" }, new { Value = "A", Text = "All" } }, "Value", "Text"); 

要么

  ViewData["GenderList"] = new SelectList(_resultFromSomeLinqQuery, "GenderID", "GenderName"); 

并在视图中使用它如下:

 <%= Html.RadioButtonList2("Sex", ViewData["GenderList"] as SelectList, ViewData["SelectedSex"].ToString(), "&nbsp;")%> 

您也可以replace&nbsp;<BR />给他们单独显示。

希望这可以帮助。

关心Naweed Akram naweed@xgeno.com

这是一个很好的“VB”中的一个轻微的“瘦”的答案。 适用于我,但这不是一个完整的解决scheme。

 <Extension()> _ Public Function RadioButtonListFor(Of TModel, TProperty)(ByVal htmlHelper As System.Web.Mvc.HtmlHelper(Of TModel), ByVal expression As System.Linq.Expressions.Expression(Of System.Func(Of TModel, TProperty)), ByVal selectList As System.Collections.Generic.IEnumerable(Of System.Web.Mvc.SelectListItem), ByVal htmlAttributes As Object) As System.Web.Mvc.MvcHtmlString 'Return htmlHelper.DropDownListFor(expression, selectList, htmlAttributes) If selectList Is Nothing OrElse selectList.Count = 0 Then Return MvcHtmlString.Empty Dim divTag = New TagBuilder("div") divTag.MergeAttributes(New RouteValueDictionary(htmlAttributes)) Dim name = CType(expression.Body, System.Linq.Expressions.MemberExpression).Member.Name Dim value = expression.Compile()(htmlHelper.ViewData.Model) Dim sb As New StringBuilder() For Each item In selectList sb.AppendFormat("<input id=""{0}_{1}"" type=""radio"" name=""{0}"" value=""{1}"" {2} />", name, item.Value, If(item.Value = value.ToString, """checked""", "")) sb.AppendFormat("<label for=""{0}_{1}"">{2}</label>", name, item.Value, item.Text) Next divTag.InnerHtml = sb.ToString Return MvcHtmlString.Create(divTag.ToString) End Function 

我修改了Mac的解决scheme,并通过数据库表replace枚举types,我的表是:

在这里输入图像描述

在我的申请中,我根据性别偏好租用房间。 我与GenderRadios属性的模型:

 public partial class Room { public RadioButtonListViewModel GenderRadios { get; set; } //... } 

在房间控制器中,我正在准备收音机:

  private void fillRadios(Room room) { List<Gender> genders = fre.Genders.ToList(); room.GenderRadios= new RadioButtonListViewModel(); room.GenderRadios.ListItems = new List<RadioButtonListItem>(); foreach (Gender gender in genders) room.GenderRadios.ListItems.Add(new RadioButtonListItem { Text = gender.Name, Value = gender.Id, Selected= (room.GenderId == gender.Id)}); } 

最后,我用它来创造空间:

 <tr> <td>Gender</td> <%= Html.RadioButtonListFor(m => m.GenderRadios, "GenderRadiosForRoomCreate")%> </tr> 

和编辑室:

 <tr> <td>Gender</td> <%= Html.RadioButtonListFor(m => m.GenderRadios, "GenderRadiosForRoomEdit")%> </tr> 

创build房间的HTML将如下所示:

 <td id="GenderRadisoForRoomCreate_Container"> <input id="GenderRadisoForRoomCreate_Any" name="GenderRadisoForRoomCreate_value" value="1" type="radio"><label for="GenderRadisoForRoomCreate_Any">Any</label> <input id="GenderRadisoForRoomCreate_Female" name="GenderRadisoForRoomCreate_value" value="2" type="radio"><label for="GenderRadisoForRoomCreate_Female">Female</label> <input id="GenderRadisoForRoomCreate_Male" name="GenderRadisoForRoomCreate_value" value="3" type="radio"><label for="GenderRadisoForRoomCreate_Male">Male</label> </td> 

创build房间时:

 [HttpPost] public ActionResult RoomCreate(Room room, FormCollection formValues, int? GenderRadiosForRoomCreate_value, int? SmokingRadiosForRoomCreate_value) { room.GenderId = GenderRadiosForRoomCreate_value; room.SmokingId = SmokingRadiosForRoomCreate_value; //... } 

这里是helpers类:

 public class RadioButtonListViewModel { public int Id { get; set; } private int selectedValue; public int SelectedValue { get { return selectedValue; } set { selectedValue = value; UpdatedSelectedItems(); } } private void UpdatedSelectedItems() { if (ListItems == null) return; ListItems.ForEach(li => li.Selected = Equals(li.Value, SelectedValue)); } private List<RadioButtonListItem> listItems; public List<RadioButtonListItem> ListItems { get { return listItems; } set { listItems = value; UpdatedSelectedItems(); } } } public class RadioButtonListItem { public bool Selected { get; set; } public string Text { get; set; } public int Value { get; set; } public override string ToString() { return Value.ToString(); } } public static class HtmlHelperExtensions { /* tagBase: I used tagBase string for building other tag's Id or Name on this. ie for tagBase="GenderRadiosForRoomCreate" <td id="GenderRadisoForRoomCreate_Container"> <input id="GenderRadisoForRoomCreate_Any" name="GenderRadisoForRoomCreate_value" value="1" type="radio"><label for="GenderRadisoForRoomCreate_Any">Any</label> <input id="GenderRadisoForRoomCreate_Female" name="GenderRadisoForRoomCreate_value" value="2" type="radio"><label for="GenderRadisoForRoomCreate_Female">Female</label> <input id="GenderRadisoForRoomCreate_Male" name="GenderRadisoForRoomCreate_value" value="3" type="radio"><label for="GenderRadisoForRoomCreate_Male">Male</label> </td> */ public static string RadioButtonListFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, RadioButtonListViewModel>> expression, String tagBase) where TModel : class { return htmlHelper.RadioButtonListFor(expression, tagBase, null); } public static string RadioButtonListFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, RadioButtonListViewModel>> expression, String tagBase, object htmlAttributes) where TModel : class { return htmlHelper.RadioButtonListFor(expression, tagBase, new RouteValueDictionary(htmlAttributes)); } public static string RadioButtonListFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, RadioButtonListViewModel>> expression, String tagBase, IDictionary<string, object> htmlAttributes) where TModel : class { var inputName = tagBase; RadioButtonListViewModel radioButtonList = GetValue(htmlHelper, expression); if (radioButtonList == null) return String.Empty; if (radioButtonList.ListItems == null) return String.Empty; var containerTag = new TagBuilder("td"); containerTag.MergeAttribute("id", inputName + "_Container"); foreach (var item in radioButtonList.ListItems) { var radioButtonTag = RadioButton(htmlHelper, inputName, new SelectListItem{Text=item.Text, Selected = item.Selected, Value = item.Value.ToString()}, htmlAttributes); containerTag.InnerHtml += radioButtonTag; } return containerTag.ToString(); } public static string RadioButton(this HtmlHelper htmlHelper, string name, SelectListItem listItem, IDictionary<string, object> htmlAttributes) { var inputIdSb = new StringBuilder(); inputIdSb.Append(name); var sb = new StringBuilder(); var builder = new TagBuilder("input"); if (listItem.Selected) builder.MergeAttribute("checked", "checked"); builder.MergeAttribute("type", "radio"); builder.MergeAttribute("value", listItem.Value); builder.MergeAttribute("id", inputIdSb.ToString() + "_" + listItem.Text); builder.MergeAttribute("name", name + "_value"); builder.MergeAttributes(htmlAttributes); sb.Append(builder.ToString(TagRenderMode.SelfClosing)); sb.Append(RadioButtonLabel(inputIdSb.ToString(), listItem.Text, htmlAttributes)); return sb.ToString(); } public static string RadioButtonLabel(string inputId, string displayText, IDictionary<string, object> htmlAttributes) { var labelBuilder = new TagBuilder("label"); labelBuilder.MergeAttribute("for", inputId + "_" + displayText); labelBuilder.MergeAttributes(htmlAttributes); labelBuilder.InnerHtml = displayText; return labelBuilder.ToString(TagRenderMode.Normal); } public static TProperty GetValue<TModel, TProperty>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) where TModel : class { TModel model = htmlHelper.ViewData.Model; if (model == null) { return default(TProperty); } Func<TModel, TProperty> func = expression.Compile(); return func(model); } }