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

在 ASP.NET MVC 期货中有一个 Html.RadioButtonList扩展方法。有人找到强类型版本 RadioButtonListFor<T>的代码吗。在视图中看起来是这样的:

<%= Html.RadioButtonListFor(model=>model.Item,Model.ItemList) %>
66499 次浏览

I have implemented something similar in MVC 1.0. See if this will be helpful for you:

    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();
}

In the controller, you can return the result as follows:

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

or

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

And use it in the View as follows:

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

You can also replace the &nbsp; with <BR /> to display them in seperate lines.

Hope this helps.

Regards Naweed Akram naweed@xgeno.com

Here is the usage in the aspx page

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

Here is the view model

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}
}
};
}
}

Here's the view model used for radio button lists

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();
}
}

Here's the extension methods for 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);
}
}

Okay, I'm aware this isn't a direct answer to your question, but this may be a better way to doing most inputs anyway (and it was fun to make). I have only just completed this and ran a small amount of testing against it, so I can't vouch for this being perfect in every situation.

I got this idea from Jimmy Bogard's post here. Take a look because there's a heap of really cool ideas there.

What I have done is created an "InputFor" helper which tries its best to work out what input you're asking for and outputs it accordingly. This will do radio buttons, but will default to a drop down if there is more than two, you should be able to change this functionality quite easily.

The below code allows you to make calls such as <%= Html.InputFor(m => m.Gender) %> or <%Html.InputFor(m => m.Gender, Model.GenderList)%>. There is a cool little bit at the end which allows you to do coding by convention, but we'll get to that later.

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);
}
}

Coding by Convention

The below code allows this to be done with convention over configuration in mind. An example of this is if you have a model object which contains the property you want to list (Gender) and a dictionary with the same name but appended with "List" (GenderList) then it will use this list by default.

e.g. <%= Html.InputFor(m => m.Gender) %> can make a full drop down list/radio button group, but these default values can be overridden by making a call like <%= 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);
}

Now a slight disclaimer:

  • This isn't the fastest code in the world (due to the reflection and other things), in my situation this isn't really relevant as it's all user driven, if you're planning on doing something crazy stupid.
  • This code is in its infancy, I will be testing this more thoroughly and adding to it over the next few days, open to any suggestions to improve the code.

I hope this code is useful to someone, I know I'll be using it over the next couple of weeks to try and cut down time. Cutting this down to just do the radio button should be a trivial task, good luck :)

Jay

Here's a slighly 'slimmer' answer in good ol' VB. Works for me, but it's not a complete solution.

<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

MVC 3 example which creates 3 radio buttons with validation to ensure 1 option is selected. And if the form fails validation (e.g. on other fields) the chosen radio option is preselected when the form is reshown.

View

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

Model

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; }
}

Controller Actions

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);
}

Here is the extension:

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());
}
}

I modified Mac's solution and replaced Enum type by database table, my table is:

enter image description here

In my application I am renting room according Gender preferences. My model with GenderRadios property:

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

In room controller, I am preparing Radios:

   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)});
}

finally, I use it in the view for creating room:

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

and for editing room:

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

Create room html will look like:

<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>

When room created:

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

Here is the helpers class:

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. i.e. 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);
}
}

Based in Jon post, a small improve to generate the radio button list as ul with HTMLAttributtes

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
// e.g. <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);
}