HTML CheckBoxListFor helper for Enum and Dictionary


Download source code : https://github.com/Azadehkhojandi/CheckboxListFor
Related Posts


As a developer who is working in presentation team I encounter requirements that I haven’t thought about before. When I wrote a ‘Checkbox list’ helpers  for Enum  for a first time I never thought one day I need to write a custom attribute to check that at least one of the checkboxes should be checked .  So I have changed my helper to automatically inject unobtrusive validation. Then, last week I faced to new issue that somehow we want to have checkboxes but the type is not Enum. What if we read some data from data base or we don’t want to show all the possible Enum values to client.
The easiest way is add another optional parameter, so we can pass a dictionary of possible list we want to show, it should have description which is shown near check box and value that will be returned if is selected by client.

public enum Mytype
    {
        type1,
        type2,
        type3,
        type4
    }
    public class testViewModel
    {
        public testViewModel()
        {
            Validtypes = new Dictionary<string, Mytype>();
            Validtypes.Add("my type 1 ", Mytype.type1);
            Validtypes.Add("my type 2 ", Mytype.type2);
        }
        public IList<Mytype> Selectedtypes {get;set;}
        public IList<Mytype> Selectedtypes1 { get; set; }
        public Dictionary<string, Mytype> Validtypes { get; set; }
    }


<div>
        @Html.CheckBoxListFor (x => x.Selectedtypes)
        @Html.CheckBoxListFor (x => x.Selectedtypes1, x => x.Validtypes)
</div>



public static MvcHtmlString CheckBoxListFor<TModel, T>
(this HtmlHelper<TModel> htmlHelper,



Expression<Func<TModel, IEnumerable<T>>> selectedValuesExpression,
Expression<Func<TModel, Dictionary<string, T>>> listOfOptionsExpression=null,
string ulClass = null)
        {
            ModelMetadata metadataselectedValues = ModelMetadata.FromLambdaExpression (selectedValuesExpression, htmlHelper.ViewData);
           
            var unobtrusiveValidationAttributes = htmlHelper.GetUnobtrusiveValidationAttributes 
(metadataselectedValues.PropertyName,                                                                                                  metadataselectedValues);

            var html = new TagBuilder("ul");
            if (!String.IsNullOrEmpty(ulClass))
                html.MergeAttribute("class", ulClass);

            string innerhtml = "";
            var selectedValues = metadataselectedValues.Model as IEnumerable<T>;
            Dictionary<string, T> listOfOptions = null  ;
            if (typeof(T).BaseType != typeof(Enum) && listOfOptions == null)
            {
                throw new Exception("please provide list of possible checkboxes");
            }
            //check if is enum and we don't have any list
            else if (typeof(T).BaseType == typeof(Enum) && listOfOptions == null)
            {
                listOfOptions = new Dictionary<string, T>();
                listOfOptions = Enum.GetValues(typeof(T)).Cast<T>().ToDictionary(currentItem => Enum.GetName(typeof(T), currentItem));
            }
            else
            {
                ModelMetadata metadatalistOfOptions = ModelMetadata.FromLambdaExpression (listOfOptionsExpression, htmlHelper.ViewData);
                listOfOptions = metadatalistOfOptions.Model as Dictionary<string, T>;
            }
            foreach (var item in listOfOptions)
            {

                bool ischecked = (selectedValues == null) ? false : selectedValues.Any(x => x.ToString() == item.Value.ToString());
                var itemId = metadataselectedValues.PropertyName + "_" + item.Value;
                var liBuilder = new TagBuilder("li");

                var inputBuilder = new TagBuilder("input");
                inputBuilder.MergeAttribute("type", "checkbox");
                inputBuilder.MergeAttribute("name", metadataselectedValues.PropertyName, true);
                inputBuilder.MergeAttribute("id", itemId, true);
                inputBuilder.MergeAttribute("value", item.Value.ToString(), true);
                inputBuilder.MergeAttributes(unobtrusiveValidationAttributes);
                if (ischecked)
                {
                    inputBuilder.MergeAttribute("checked", "'checked'");
                }

                liBuilder.InnerHtml = inputBuilder.ToString() + htmlHelper.Label(itemId,                          item.Key);
                innerhtml = innerhtml + liBuilder;

            }
            html.InnerHtml = innerhtml;
            return new MvcHtmlString(html.ToString());


        }






Comments

Sebastiaan said…
Almost works, but this:
if (typeof(T).BaseType != typeof(Enum) && listOfOptions == null)

Should be:
if (typeof(T).BaseType != typeof(Enum) && listOfOptionsExpression == null)

Then it works as expected :-)

Also, to refactor a bit:
else if (typeof(T).BaseType == typeof(Enum) && listOfOptions == null)

Can be:
if (typeof(T).BaseType == typeof(Enum))

And you don't need to initialize listOfOptions again:
listOfOptions = new Dictionary();

This line can be removed.

Thanks a lot though for this solution!!
Azadeh Khojandi said…
Thanks Sebastiaan for your comment :)