6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ASP.NET MVCでenumを使ってRadioButtonを作るHelper

Last updated at Posted at 2015-07-17

MVCやっててEnumをドロップダウンにして上手く使いたいなぁってケースは結構あるかと思います。
っていうネタを書こうと思ったらASP.NET5.1からはEnumDropDownListForが追加されていますね。
そちらを使うのが良いですねぇ。

EnumをRadioボタンにするネタ。

まずはDescriptionAttributeの拡張
画面出力名称の他にEnumの中でも選択肢として出したくないモノとかの制御をしたりします。

PartsDescriptionAttribute.cs
    public class PartsDescriptionAttribute : System.ComponentModel.DescriptionAttribute
    {
        /// <summary>
        /// この項目が部品に表示されるかを表します。
        /// </summary>
        public bool EnableParts
        {
            get
            {
                return _enableparts;
            }
            private set
            {
                _enableparts = value;
            }
        }
        private bool _enableparts = true;
        public PartsDescriptionAttribute() { }
        public PartsDescriptionAttribute(string Descript) : base(Descript) { }
        public PartsDescriptionAttribute(string Descript, bool enableparts)
            : base(Descript)
        {
            this.EnableParts = enableparts;
        }
        public PartsDescriptionAttribute(bool enableparts)
        {
            this.EnableParts = enableparts;
        }

    }

次にHtmlHelper拡張

EnumRadioButton.cs
    public static class EnumRadioButton
    {

        private static Type GetEnumTypeFromAttribute<TModel, TValue>(Expression<Func<TModel, TValue>> expression)
        {
            MemberInfo propertyInfo =
                ((expression.Body as MemberExpression).Member as MemberInfo);
            if (propertyInfo == null)
                throw new ArgumentException("プロパティではありません。");
            EnumDataTypeAttribute attr = Attribute.GetCustomAttribute(propertyInfo,
                typeof(EnumDataTypeAttribute)) as EnumDataTypeAttribute;
            if (attr == null)
                throw new ArgumentException("EnumDataTypeが指定されていません");
            return attr.EnumType;
        }

        private static bool GetEnumPartsDescription<TValue>(TValue enumValue, Type enumType)
        {
            if (enumValue != null)
            {
                FieldInfo fi = enumType.GetField(enumType.GetEnumName(enumValue));
                PartsDescriptionAttribute[] attributes = (PartsDescriptionAttribute[])fi.GetCustomAttributes(typeof(PartsDescriptionAttribute), false);
                if ((attributes != null) && (attributes.Length > 0))
                {
                    return attributes[0].EnableParts;
                }
                return true;
            }
            else
            {
                return false;
            }
        }

        private static string GetEnumDescription<TValue>(TValue enumValue, Type enumType)
        {
            FieldInfo fi = enumType.GetField(enumType.GetEnumName(enumValue));

            PartsDescriptionAttribute[] attributes = (PartsDescriptionAttribute[])fi.GetCustomAttributes(typeof(PartsDescriptionAttribute), false);

            if ((attributes != null) && (attributes.Length > 0))
                return attributes[0].Description;
            else
                return enumType.GetEnumName(enumValue);
        }

        public static MvcHtmlString EnumRadioButtonFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, object htmlAttributes = null)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            Type enumType = GetEnumTypeFromAttribute(expression);
            IEnumerable<int> values = Enum.GetValues(enumType).Cast<int>();

            var rtnstr = string.Empty;

            foreach (var val in values.Where(x => GetEnumPartsDescription(x, enumType)))
            {
                TagBuilder radioTag = new TagBuilder("input");
                radioTag.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
                radioTag.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Radio));

                string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(
                        ExpressionHelper.GetExpressionText(expression));

                radioTag.MergeAttribute("name", fullName, true);

                ModelState modelState;
                string ModelStateValue = null;
                if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState))
                {
                    if (modelState.Value != null)
                    {
                        ModelStateValue = modelState.Value.ConvertTo(typeof(string), null) as string;
                    }
                }
                bool isChecked = false;
                bool useModelState = false;
                if (ModelStateValue != null)
                {
                    isChecked = String.Equals(ModelStateValue, val.ToString(), StringComparison.Ordinal);
                    useModelState = true;
                }
                if (!useModelState)
                {
                    if (htmlHelper.ViewData.Model != null)
                    {
                        string viewdataval = htmlHelper.ViewData.Eval(fullName).ToString();
                        isChecked = val.ToString() == viewdataval;
                    }
                }
                if (isChecked)
                {
                    radioTag.MergeAttribute("checked", "checked");
                }
                radioTag.MergeAttribute("value", val.ToString(), true);
                // id生成
                string id = fullName + val.ToString();
                radioTag.GenerateId(id);
                // label
                TagBuilder labelTag = new TagBuilder("Label");
                labelTag.InnerHtml = radioTag.ToString();
                labelTag.InnerHtml += GetEnumDescription(val, enumType);
                labelTag.MergeAttribute("for", radioTag.Attributes["id"]);

                rtnstr += labelTag.ToString();
            }



            return new MvcHtmlString(rtnstr);
        }
    }

で使い方としては

    public class HomeController : Controller
    {
        public ActionResult EnumTest(EnumTestCondition condition)
        {
            return View();
        }
    }

    public class EnumTestCondition
    {
        [System.ComponentModel.DataAnnotations.EnumDataType(typeof(EnumTestEnum))]
        [Required]
        public int? Test { get; set; }

        public enum EnumTestEnum
        {
            [TMFY.MVC.Model.PartsDescription("焼酎ハイボール(レモン)")]
            焼酎ハイボール = 1,
            [TMFY.MVC.Model.PartsDescription(false)]
            トリスハイボール = 2,                        
            メッツコーラ = 3,
            ドクターペッパー = 4,
        }

でこんな感じにすると


        @using (Html.BeginForm())
        {
           @Html.EnumRadioButtonFor(m => m.Test)
           @Html.ValidationMessageFor(m => m.Test)
            <input type="submit" value="送信" />
        }
          

こんなHtmlが出てくる


<form action="/Home/EnumTest" method="post">
<Label for="Test1"><input checked="checked" id="Test1" name="Test" type="radio" value="1"></input>焼酎ハイボール(レモン)</Label>
<Label for="Test3"><input id="Test3" name="Test" type="radio" value="3"></input>メッツコーラ</Label>
<Label for="Test4"><input id="Test4" name="Test" type="radio" value="4"></input>ドクターペッパー</Label>
<span class="field-validation-valid" data-valmsg-for="Test" data-valmsg-replace="true"></span>            
<input type="submit" value="送信" />
</form>

ModelStateの値とのからみとか色々操作してますが、基本的にはEnumをRadioButtonのリストにしてるだけです。
ただ、DropDownListと違ってラジオボタンのHtml出力って自由度が高いからその辺上手くないんですよね。
使うプロジェクトのデザインによってHtml生成部分を書き換えたりしてます。
この辺Funcとかにするのが良いのかな。

あと、Modelのプロパティが直接Enumではなくてintなのは
個人的にその方が使いやすい事が多いからです。
ちっちゃいO/Rマッパーを使うことが多いので、そのまま検索条件に使いたい的なですね。。。

おしまい|д゚)

6
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?