Simon Miller Team : Web Development Tags : Web Development MVC

A super-duper RadioButtonList helper

Simon Miller Team : Web Development Tags : Web Development MVC

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:

  1. Used bootstrap mark-up
  2. Utilised the Display Name attribute for friendlier button labels
  3. 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:

  1. Retrieve all metadata for the property via the calling view expression
  2. Derive the enum type and iterate the options.
  3. Attempt to parse both a [Display(Name = "")] attribute and a [Description("")] attribute for each enum option.
  4. Format each radio button using the display name and description values. This utilises the regular RadioButtonFor() helper to benefit from two-way binding.
  5. 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 = "&nbsp;";
        //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>