Introduction
The guys over at Twitter have done a serious favor for developers worldwide with their Bootstrap framework. This is especially useful for us coders with limited skills in UI design. With the Bootstrap Helpers project, I have made things even easier for .NET developers.
UPDATE (2018.05.19)
This project has been superceded by the ExtensoUI
package in my Extenso project for .NET Core. It does not have all the same helpers, but most of the important ones are there (Accordion, Tabs, Panel, Modal and some smaller things). More importantly, I made it extensible, so you can add your own provider for Bootstrap 4, Foundation 5 or 6, jQueryUI, etc. I have already created a spearate package for Kendo as well and I will likely improve upon this in future. Enjoy!
Update (2013.05.10)
Note that the Bootstrap Helpers are now available as a Nuget package! Here is the link: https://www.nuget.org/packages/VortexSoft.Bootstrap[ title="New Window">^]. Additionally, the project is hosted on GitHub now, as that is what most people seem to be using these days for open source projects. I am hoping this will help with contributors. Here is the link for that: https://github.com/gordon-matt/BootstrapHelpers[ title="New Window">^]
Usage
Here are some examples of the most useful HTML helpers using the Razor syntax.
Modal Dialog
<a class="btn" data-toggle="modal" href="#myModal" >Normal Modal</a>
@using (var modal = Html.Bootstrap().Begin(new Modal(new { id = "myModal" })))
{
using (var header = modal.BeginHeader())
{
<button type="button" class="close"
data-dismiss="modal">×</button>
<h3>Modal header</h3>
}
using (var body = modal.BeginBody())
{
<p>One fine body…</p>
}
using (var footer = modal.BeginFooter())
{
@Html.Bootstrap().Button("Close", BootstrapNamedColor.Default,
null, new { data_dismiss = "modal" })
@Html.Bootstrap().Button("Save Changes", BootstrapNamedColor.Primary, null)
}
}
Tabs
@using (var tabs = Html.Bootstrap().Begin(new Tabs()))
{
tabs.Tab("Tab One", "tab1");
tabs.Tab("Tab Two", "tab2");
tabs.Tab("Tab Three", "tab3");
using (tabs.BeginPanel())
{
<p>This is tabs panel 1 content</p>
}
using (tabs.BeginPanel())
{
<p>This is tabs panel 2 content</p>
}
using (tabs.BeginPanel())
{
<p>This is tabs panel 3 content</p>
}
}
You could also place the tabs on the left or right. Here is an example:
@using (var tabs = Html.Bootstrap().Begin(new Tabs(TabPosition.Left)))
{
}
Accordion/Collapse
@using (var accordion = Html.Bootstrap().Begin(new Accordion("accordion1")))
{
using (var panel = accordion.BeginPanel("Panel 1", "panel1"))
{
<p>This is accordion panel 1 content</p>
}
using (var panel = accordion.BeginPanel("Panel 2", "panel2"))
{
<p>This is accordion panel 2 content</p>
}
}
Carousel
@using (var carousel = Html.Bootstrap().Begin(new Carousel("carousel1")))
{
carousel.Item("~/Images/Red.png", "Red");
using (var item = carousel.ItemWithCaption("~/Images/Green.png", "Green"))
{
<h4>This is Green</h4>
<p>Green is a nice color</p>
}
carousel.Item("~/Images/Blue.png", "Blue");
}
Sub Nav
@using (var subNav = Html.Bootstrap().Begin(new SubNavBar()))
{
subNav.Item("Modal", "#demoModal");
subNav.Item("Tabs", "#demoTabs");
subNav.Item("Accordion", "#demoAccordion");
subNav.Item("Other", "#demoOther");
subNav.Item("Toolbar", "#demoToolbar");
subNav.Item("Thumbnails", "#demoThumbs");
subNav.Item("Carousel", "#demoCarousel");
subNav.Item("Code Blocks", "#demoCodeBlocks");
subNav.Item("Forms", Url.Action("DemoForm", "Home"));
subNav.DropDownItem("DropDown", new List<BootstrapListItem>
{
new BootstrapListItem { Text = "Something", Url = "#something" },
new BootstrapListItem { Text = "Something Else", Url = "#something-else" },
new BootstrapListItem { Text = "Yet Something More", Url = "#yet-something-more" }
});
}
Miscellaneous
There are also many smaller helpers which you can find, such as the Buttons. Here is an example of an action button:
@Html.Bootstrap().ActionButton("Home", BootstrapNamedColor.Primary,
"Index", "Home")
How It Was Done
When building any HTML helpers, I consider it always good practice to separate them from the rest of the standard ones. So, instead of calling:
Html.BeginBootstrapAccordion()
or something like that, we want to call:
Html.Bootstrap().Begin(new Accordion())
The way to do this would be to first create a class called Bootstrap
with only one constructor whose accessibility is set to internal
. Here is an example:
public class Bootstrap<TModel>
{
private readonly HtmlHelper<TModel> helper;
internal Bootstrap(HtmlHelper<TModel> helper)
{
this.helper = helper;
}
You of course need to pass an instance of HtmlHelper
as well and in that class is where you will add all the helper methods. Here is a simple example (with overloads):
public MvcHtmlString ActionButton
(string text, BootstrapNamedColor color, string actionName, string controllerName)
{
return ActionButton(text, color, actionName, controllerName, null);
}
public MvcHtmlString ActionButton(string text, BootstrapNamedColor color,
string actionName, string controllerName, object routeValues)
{
return ActionButton(text, color, actionName, controllerName, routeValues, null);
}
public MvcHtmlString ActionButton(string text, BootstrapNamedColor color,
string actionName, string controllerName, object routeValues, object htmlAttributes)
{
var builder = new TagBuilder("a");
builder.SetInnerText(text);
builder.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
switch (color)
{
case BootstrapNamedColor.Important: builder.AddCssClass("btn btn-danger"); break;
case BootstrapNamedColor.Default: builder.AddCssClass("btn"); break;
case BootstrapNamedColor.Info: builder.AddCssClass("btn btn-info"); break;
case BootstrapNamedColor.Inverse: builder.AddCssClass("btn btn-inverse"); break;
case BootstrapNamedColor.Primary: builder.AddCssClass("btn btn-primary"); break;
case BootstrapNamedColor.Success: builder.AddCssClass("btn btn-success"); break;
case BootstrapNamedColor.Warning: builder.AddCssClass("btn btn-warning"); break;
default: builder.AddCssClass("btn"); break;
}
var urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
builder.MergeAttribute("href", urlHelper.Action(actionName, controllerName, routeValues));
return MvcHtmlString.Create(builder.ToString());
}
Finally, you need to create an instance of the Bootstrap
class somewhere. Since it only has an internal constructor, you can only instantiate it from the project it is created in and since you want to be able to use it from any other project, what you do is create a public
extension method in that same project like so:
public static class HtmlHelperExtensions
{
public static Bootstrap<TModel> Bootstrap<TModel>(this HtmlHelper<TModel> htmlHelper)
{
return new Bootstrap<TModel>(htmlHelper);
}
}
And as easy as that, you can now add all your helper methods into the Bootstrap “we’ll call it name space; for lack of a better word”. So now I just use it like this:
@Html.Bootstrap().ActionButton("Home", BootstrapNamedColor.Primary, "Index", "Home")
Awesome! Show me more! Okay, okay, here goes: Inspect the following:
@using (Html.BeginForm()) {
@Html.AntiForgeryToken()
@Html.ValidationSummary()
<fieldset>
<legend>Registration Form</legend>
<ol>
<li>
@Html.LabelFor(m => m.UserName)
@Html.TextBoxFor(m => m.UserName)
</li>
<li>
@Html.LabelFor(m => m.Password)
@Html.PasswordFor(m => m.Password)
</li>
<li>
@Html.LabelFor(m => m.ConfirmPassword)
@Html.PasswordFor(m => m.ConfirmPassword)
</li>
</ol>
<input type="submit" value="Register" />
</fieldset>
}
The HTML helper in this case is BeginForm()
and it is different somehow... yes, it allows you to add HTML content in between the braces. Nice! Ever wondered how it’s done? Well, stop wondering.. let’s create our own. You basically need do create a class that inherits from IDisposable
like so:
public class AccordionBuilder<TModel>
{
protected readonly TextWriter textWriter;
protected readonly HtmlHelper<TModel> htmlHelper;
internal AccordionBuilder(HtmlHelper<TModel> htmlHelper, Accordion accordion)
{
this.htmlHelper = htmlHelper;
this.textWriter = htmlHelper.ViewContext.Writer;
this.textWriter.Write(@"<div class=""acccordion"">");
}
public override void Dispose()
{
this.textWriter.Write(@"</div>");
}
}
The code for the real accordion is a little more complex than the above. I have simplified the above to give you a clearer picture of what’s going on. Basically, in the constructor, we write the start of the element (a <div>
in this case or <form>
tag in the case of Html.BeginForm()
) and in the Dispose()
method, we write the end
tag. Since the using
statement automatically calls Dispose()
on an IDisposable
, then that’s all we need to do... nothing too fancy here.
Final Word
These helpers are there to benefit everyone. As such, it is my hope that there will be those who’d like to contribute and make these helpers even better for everyone. Enjoy!
Acknowledgements
I must give credit to Attila Losonc of the jQuery UI Helpers project; it is that awesome project that inspired me to create the Bootstrap Helpers.