A super-duper RadioButtonList helper
MVC comes with plenty of helpers, but often I need them to do more. In this instance, I needed to create a Radio Button list that:
- Used bootstrap mark-up
- Utilised the Display Name attribute for friendlier button labels
- Appended the value of any Description attribute, to describe the button.
Using this article as a starting point, I expanded the functionality of the helper to add the above three requirements.
First, create your enum and decorate it with [Display] and [Description] attributes like so:
public enum ProfileTypes { [Display(Name = "Custom")] [Description("Search and add members to this profile")] Custom = 0, [Display(Name = "Rule")] [Description("Set rules to define profile members")] Rule = 1 }
Add an instance of the enum to your view model. Add the following helper to the view:
@Html.RadioButtonsWithDescriptionForEnum(m => m.ProfileType)
Finally, examining the function below:
- Retrieve all metadata for the property via the calling view expression
- Derive the enum type and iterate the options.
- Attempt to parse both a [Display(Name = "")] attribute and a [Description("")] attribute for each enum option.
- Format each radio button using the display name and description values. This utilises the regular RadioButtonFor() helper to benefit from two-way binding.
- Wrap all the radio buttons in bootstrap compatible mark-up and return to the view.
public static MvcHtmlString RadioButtonsWithDescriptionForEnum<TModel, TProperty>( this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression ) { var radios = new StringBuilder(); var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); //enum options var names = Enum.GetNames(metaData.ModelType); foreach (var name in names) { var displayName = name; var description = " "; //display name and description attributes var memInfo = metaData.ModelType.GetMember(name); if (memInfo != null) { var attributes = memInfo.Select(e => e.GetCustomAttributes(typeof(DisplayAttribute), false)).FirstOrDefault(); if (attributes != null && attributes.Length > 0) displayName = ((DisplayAttribute)attributes[0]).Name; var descriptionAttributes = memInfo.Select(e => e.GetCustomAttributes(typeof(DescriptionAttribute), false)).FirstOrDefault(); if (descriptionAttributes != null && descriptionAttributes.Length > 0) description = ((DescriptionAttribute)descriptionAttributes[0]).Description; } //build the radio button var id = string.Format("{0}_{1}_{2}", htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix, metaData.PropertyName, name); radios.AppendFormat( "<div class=\"radio well\"><label>{2}<span class=\"text\"><strong>{1}</strong></span></label> <br/><span>{3}</span></div>", id, HttpUtility.HtmlEncode(displayName), htmlHelper.RadioButtonFor(expression, name, new { @id = id }).ToHtmlString(), description.ToString() ); } //containers var div = new TagBuilder("div"); div.AddCssClass("control-group"); var lbl = new TagBuilder("label"); lbl.AddCssClass("control-label"); lbl.Attributes.Add("for", metaData.PropertyName); lbl.SetInnerText(metaData.DisplayName); //flatten it all div.InnerHtml = string.Concat(lbl.ToString(), radios.ToString()); return MvcHtmlString.Create(div.ToString()); }
If you need to perform any client-side interactions, the following jQuery can be adapted easily to accomodate your property name:
<script type="text/javascript"> $(document).ready(function () { $("input[type=radio][name=ProfileType]").change(function (e) { //the enum string value alert($(this).val()); }); }); </script>