使用RadioButtonFor编码一组无线电按钮的正确方法

Jon*_*ood 11 checkbox asp.net-mvc radio-button radiobuttonfor

根据我的研究,在MVC中编码复选框的正确方法如下.

@Html.CheckBoxFor(m => m.RememberMe)
@Html.LabelFor(m => m.RememberMe)
Run Code Online (Sandbox Code Playgroud)

这将呈现一个复选框,为复选框呈现标签,并使标签可单击,这意味着单击标签也会切换复选框.

但是对于单个视图模型字段有多个复选框的情况呢?(例如,也许每个复选框代表一个整数值内的位.)或者,更好的是,单选按钮怎么样,哪里总有几个选项与同一个字段相关联?

在这些情况下,上面的代码似乎存在问题.要么不是所有元素都与相同的字段相关联,要么呈现具有相同ID的多个元素.

我一直在研究这个问题.我找到了很多解决方案; 然而,它们似乎都没有可点击的标签,或者它们需要一堆自定义和/或非标准编码.有些人完全避免使用RadioButtonFor/LabelFor,而是选择编写原始HTML.

编写原始HTML很好,但是MVC的设计者没有预料到我们可能需要为同一个字段使用多个单选按钮吗?而且,如果他们这样做了,他们是如何打算编码的呢?

编辑:

在研究了这个之后,下面的代码是我发现编码单选按钮的最佳方法.这段代码假设我为每个无线电选项定义了一个枚举.

@Html.RadioButtonFor(m => m.Gender, Gender.Male, new { id = "gender-male" })
@Html.LabelFor(m => m.Gender, Gender.Male.ToString(), new { @for = "gender-male" })
@Html.RadioButtonFor(m => m.Gender, Gender.Female, new { id = "gender-female" })
@Html.LabelFor(m => m.Gender, Gender.Female.ToString(), new { @for = "gender-female" })
Run Code Online (Sandbox Code Playgroud)

也许这是我能想到的最好的,但它仍然让我感到麻烦.

  1. 为什么要对ID这样的基本内容进行如此多的定制?再一次,微软是否预计我们需要与同一领域相关联的多个单选按钮?

  2. 如果枚举名称与单选按钮标签所需的描述不同,则此方法似乎不支持字段属性,例如[Display(Name = "")].

(我想多个复选框的正确处理将推迟到另一个问题.)

Ser*_*gio 5

好的,我们做一些研究.首先,您不需要通过单击标签来切换复选框/无线电的唯一ID.其实你根本不需要id!您所要做的就是将输入包装在<label>标签内:

<label>
    <input type="radio" name="radio" value="1" /> Radio 1   
</label>
Run Code Online (Sandbox Code Playgroud)

到目前为止,ASP.NET MVC为您提供了编写自己的html帮助程序的能力,让我们为Enum模型字段编写一个!

想象一下,我们有一些注册模型:

public class RegisterViewModel
{
    [Required]
    [EmailAddress]
    [Display(Name = "Email")]
    public string Email { get; set; }

    [Required]
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; } 
}
Run Code Online (Sandbox Code Playgroud)

该死的!我只是复制粘贴的教程模型o_O.我们现在想要的是获取用户的性别,因此我们添加一个Gender枚举:

public enum Gender
{
    Iamparanoic = 0,

    Male = 1,

    Female = 2,
}
Run Code Online (Sandbox Code Playgroud)

并为我们的模型添加类型Gender字段Gender(希望,c#有一个Gender访问修饰符!)

public Gender Gender { get; set; }
Run Code Online (Sandbox Code Playgroud)

现在,时间使用一些asp.net魔术并编写我们的html助手:

public static MvcHtmlString RadioButtonsListForEnum<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression)
{
    var sb = new StringBuilder();
    sb.Append("<ul>"); //we want it to look cool, dont' we?
    foreach (var value in Enum.GetValues(typeof(TEnum)))
    {
        var radio = htmlHelper.RadioButtonFor(expression, value).ToHtmlString(); //you can generage any id and pass it to helper, or use a tagbuilder

        sb.AppendFormat(
            "<li><label>{0} {1}</label></li>",
            radio,
            ((Enum)value).GetEnumName() //instead of it you can grab e.g. Display/Description attribute value or w/e else
        );
    }
    sb.Append("</ul");
    return MvcHtmlString.Create(sb.ToString());
}
Run Code Online (Sandbox Code Playgroud)

并使用它看起来像:

@Html.RadioButtonsListForEnum(m => m.Gender)
Run Code Online (Sandbox Code Playgroud)

太漂亮了,不是吗?当然你可以添加hella lot of customization,比如渲染具有独特mvc风格id的radiobuttons,所有html属性的kindes等等,但这只是一个样本,屁股踢到正确的方式.

接下来,我们还要渲染checkboxlist!

public static MvcHtmlString CheckBoxListForEnum<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, IDictionary<string, object> htmlAttributes = null)
{
    var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
    var enumValue = Convert.ToInt32(metadata.Model);

    var sb = new StringBuilder();
    foreach (var value in Enum.GetValues(typeof (TEnum)))
    {
        var val = Convert.ToInt32(value);
        var checkbox = new TagBuilder("input");

        checkbox.MergeAttribute("type", "checkbox");
        checkbox.MergeAttribute("value", value.ToString());
        checkbox.MergeAttribute("name", htmlHelper.NameFor(expression).ToString());

        if ((enumValue & val) == val)
            checkbox.MergeAttribute("checked", "checked");
        if (htmlAttributes != null)
            checkbox.MergeAttributes(htmlAttributes);

        var label = new TagBuilder("label") { InnerHtml = checkbox + ((Enum)value).GetEnumName() };

        sb.Append(label);
        sb.Append("<br/>");
    }
    return new MvcHtmlString(sb.ToString());
}
Run Code Online (Sandbox Code Playgroud)

再一次,简单的用法:

@Html.CheckBoxListForEnum(m => m.Gender)
Run Code Online (Sandbox Code Playgroud)

顺便说一下,它只针对标有Flags属性的枚举,当然你会遇到模型绑定的麻烦 - 它需要自定义绑定器.但这是另一个问题,我希望Jon Skeet可以回答:)