14

So I'm using Twitter Bootstrap and most of my forms are looking fantastic with this

       <div class="control-group">
            @Html.LabelFor(m => m.Prop)
            <div class="controls">
                @Html.EditorFor(m => m.Prop)
            </div>
            @Html.ValidationMessageFor(model => model.Prop)
        </div>

The only issue is with Radio Buttons and Check Boxes. Twitter Bootstrap calls for HTML5 style labels that include the radio or checkbox input INSIDE the label tag like this

        <label class="checkbox">
           <input type="checkbox"> Check me out
        </label>

If I can't create these with a with @Html or and overload can I at least get to the text that's created by Labelfor?

        <label class="checkbox">
           @Html.EditorFor(m=> m.Prop)
           @Html.TheVariableThatContainsTheTextThatLabelForUsesFor(m => m.Prop)
        </label>
yoozer8
  • 7,361
  • 7
  • 58
  • 93
James Troutman
  • 211
  • 1
  • 2
  • 5
  • Have you looked at this post... http://stackoverflow.com/questions/5497183/how-to-create-custom-editor-display-templates-in-asp-net-mvc-3 – snoll May 16 '12 at 21:45

5 Answers5

15

Why can't you create these with an @Html. If you write your own extension method (see below) that should do it, no?


public static class HtmlHelperExtensions
    {
        public static MvcHtmlString MyCheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool>> expression, object htmlLabelAttributes = null, object htmlCheckBoxAttributes = null)
        {
            var checkbox = htmlHelper.CheckBoxFor(expression, htmlCheckBoxAttributes);

            var labelTag = new TagBuilder("label");
            labelTag.AddCssClass("checkbox");
            labelTag.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlLabelAttributes));
            labelTag.InnerHtml = checkbox.ToString();

            return new MvcHtmlString(labelTag.ToString());
        }
    }

EDIT:

How about this revised version. This replicates exactly what the label does.


public static class HtmlHelperExtensions
{
    public static MvcHtmlString MyCheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool>> expression, object htmlLabelAttributes = null, object htmlCheckBoxAttributes = null)
    {
        var checkbox = htmlHelper.CheckBoxFor(expression, htmlCheckBoxAttributes);

        var labelTag = new TagBuilder("label");
        var checkboxName = ExpressionHelper.GetExpressionText(expression);
        labelTag.AddCssClass("checkbox");
        labelTag.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlLabelAttributes));
        labelTag.InnerHtml = checkbox.ToString() + LabelHelper(ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData), checkboxName);

        return new MvcHtmlString(labelTag.ToString());
    }

    private static MvcHtmlString LabelHelper(ModelMetadata metadata, string fieldName)
    {
        string labelText;
        var displayName = metadata.DisplayName;

        if (displayName == null)
        {
            var propertyName = metadata.PropertyName;

            labelText = propertyName ?? fieldName.Split(new[] { '.' }).Last();
        }
        else
        {
            labelText = displayName;
        }

        if (string.IsNullOrEmpty(labelText))
        {
            return MvcHtmlString.Empty;
        }

        return new MvcHtmlString(labelText);
    }
}

I should note that with MVC 4 there is a DisplayNameFor Helper, so that whole label business could be simplified a bit.

Mirko
  • 4,284
  • 1
  • 22
  • 19
  • Thats really close but it's missing the contents of LabelFor, so the textbox is displayed with no text. So I guess I need to add someting like var label = htmlHelper.LabelFor(expression, htmlCheckBoxAttributes); – James Troutman May 17 '12 at 20:28
11

Touching on what Mirko mentioned about MVC 4, this is the code i'm using in the view (no custom helper).

<div class="checkbox">
    <label>
        @Html.CheckBoxFor(m => m.RememberMe) @Html.DisplayNameFor(m => m.RememberMe)
    </label>
</div>
Kramer
  • 519
  • 1
  • 5
  • 13
5

I just built a solution that mirrors the way forms work in ASP.NET MVC.

@using (Html.BeginLabelFor(m => m.CheckMe, new { @class = "checkbox" })) {
    @Html.CheckBoxFor(m => m.CheckMe)
}

This renders the nested structure, while allowing you to use the normal HTML helpers for checkboxes and radio buttons.

<label class="checkbox" for="CheckMe">
    <input type="checkbox" name="CheckMe" value="true" /> Hey, Check Me!
</label>

The BeginLabel and BeginLabelFor support the same signatures as Label and LabelFor, respectively.

https://github.com/toddlucas/bootstrap-mvc.net

I hope someone else finds this useful.

Edit:

Another alternative, which is employed in the new Visual Studio 2013 MVC templates, is to do the following:

<div class="checkbox">
    @Html.CheckBoxFor(m => m.CheckMe)
    @Html.LabelFor(m => m.CheckMe)
</div>

The new templates use Twitter Bootstrap 3.0, which follows the same convention as 2.x.

Todd
  • 12,995
  • 3
  • 30
  • 25
  • And if you need special display text for the label, either configure it as a Display attribute in your model code or use the alternate form of the LabelFor method that specifies the label text to use. – Neil Monroe Mar 24 '14 at 20:56
2

My final solution is an Custom Bootstrap Label helper

    public static MvcHtmlString BootstrapCheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool>> expression, object htmlLabelAttributes = null, object htmlCheckBoxAttributes = null)
    {
        var checkbox = htmlHelper.CheckBoxFor(expression, htmlCheckBoxAttributes);
        var label = htmlHelper.LabelFor(expression, htmlCheckBoxAttributes);
        string text = Regex.Match(label.ToString(), "(?<=^|>)[^><]+?(?=<|$)").Value;

        var labelTag = new TagBuilder("label");
        labelTag.AddCssClass("checkbox");
        labelTag.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlLabelAttributes));
        labelTag.InnerHtml = checkbox.ToString() + text;

        return new MvcHtmlString(labelTag.ToString());
    }
James Troutman
  • 211
  • 1
  • 2
  • 5
  • I added a revised version. Take a look, the LabelHelper logic is exactly what the framework does internally to come up with the label text, no need to &quotScreen Scrape" it. Hope it helps. – Mirko May 18 '12 at 00:56
2

Instead of rendering the return value of the LabelFor method directly like this:

@Html.LabelFor(m => m.Prop, "Text <a>HTML Tag</a>")

First set the output to a variable decode that and the render that decoded text:

MvcHtmlString label = Html.LabelFor(m => m.Prop, "Text <a>HTML Tag</a>");
@Html.Raw(HttpUtility.HtmlDecode(label.ToString()))
Anthony R
  • 173
  • 1
  • 8