Adding your own HtmlHelper in ASP.NET MVC 3
To use the custom helper method in your Razor views you will need to bring it into scope. There are two possible ways to do this:
- Add a
@using SomeNamespace
in the top of your view with the namespace where the static class containing the helper is defined In
~/Views/web.config
, add:<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="SomeNamspace" />
</namespaces>
</pages>
</system.web.webPages.razor>
Once the custom helper is brought into scope in the view, Intellisense should be able to pick it and you could use it:
@Html.StateDropDownList()
Now you helper method needs to do something useful. You could either call existing helpers:
public static class ExtensionMethods
{
public static MvcHtmlString StateDropDownList(this HtmlHelper html)
{
return html.TextBox("foo")
}
}
or return some custom data:
public static class ExtensionMethods
{
public static MvcHtmlString StateDropDownList(this HtmlHelper html)
{
return MvcHtmlString.Create("Hello world");
}
}
If you have a strongly typed view and you wanted to use an expression:
using System.Web.Mvc;
using System.Web.Mvc.Html;
public static class ExtensionMethods
{
public static MvcHtmlString StateDropDownList(
this HtmlHelper<MyViewModel> html
)
{
var stateList = new SelectList(new[]
{
new { Key = "Alabama", Value = "Alabama" },
new { Key = "Idaho", Value = "Idaho" },
new { Key = "California", Value = "California" }
}, "Key", "Value");
return Html.DropDownListFor(
x => x.State, stateList, "-- Select a state --"
);
}
}
and then:
@Html.StateDropDownList()
Adding a custom HTML Helper to MVC Project
You should include the namespace in your view:
@using MyWebApp
Or you can import this namespace for all of the views from web.config.
<system.web.webPages.razor>
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization" />
<add namespace="System.Web.Routing" />
<add namespace="MyWebApp" />
</namespaces>
</pages>
</system.web.webPages.razor>
Using custom html helpers in an ASP.NET MVC 3 Application
Like someone pointed out in the comments, there was something different about my setup. In my solution, I had changed the Output path for my projects - including the MVC project - from the default bin\
to something like ..\Library\Build\
. No crime there since that setup has worked fine so far.
But, that is what got me into trouble. After I restored the default output path and rebuilt my project the Html helper worked. It continued to work even after I restored back my preferred output path - obviously because the dll in the bin
folder got updated.
This would mean that the statement @using MVCUI.Extensibility;
in my .cshtml file and <add namespace="MVCUI.Extensibility" />
in the Web.config were referencing an old outdated dll in the bin folder that didn't have the HtmlHelper defined. This bothers still. How would I have them reference the dll in my desired output path?
Anyway, I just thought I should post about the experience and the lessons just in case other people find themselves in similar trouble. Thanks people.
How create Custom HTML Helper in Asp.Net MVC
If I were you, I would not name it as the original name from the Html Helper. I would try something like:
public static string CustomLabel(this HtmlHelper helper, string target, string text)
{
return String.Format("<label for='{0}'>{1}</label>", target, text);
}
After it, just include the namespace (not the entire path to the class), for sample:
@using MvcPractise.Extension
And as the extension method, use it on Html
property:
@Html.CustomLabel("firstName", "First Name:")
create a html helper for mvc
Seems like an excellent scenario for a custom html helper:
public class RoundedCorner : IDisposable
{
private readonly ViewContext _viewContext;
private bool _disposed = false;
public RoundedCorner(ViewContext viewContext)
{
_viewContext = viewContext;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
_viewContext.Writer.Write(
@"<div class=""Clear"">
</div>
</div>
</div>
<div class=""bottom"">
<div class=""right"">
</div>
</div>
</div>
</div>"
);
}
}
}
public static class HtmlExtensions
{
public static RoundedCorner RoundedCorner(this HtmlHelper htmlHelper)
{
htmlHelper.ViewContext.Writer.Write(
@"<div class=""rounded"">
<div class=""top"">
<div class=""right"">
</div>
</div>
<div class=""middle"">
<div class=""right"">
<div class=""content"">"
);
return new RoundedCorner(htmlHelper.ViewContext);
}
}
and in your view simply:
@using (Html.RoundedCorner())
{
<div>Some how allow me to insert data into here</div>
}
which would generate (I know, what an ugly formatting but perfectly valid HTML, I am too lazy to fix it at the moment):
<div class="rounded">
<div class="top">
<div class="right">
</div>
</div>
<div class="middle">
<div class="right">
<div class="content"> <div>Some how allow me to insert data into here</div>
<div class="Clear">
</div>
</div>
</div>
<div class="bottom">
<div class="right">
</div>
</div>
</div>
</div>
Making my own HtmlHelper extension for input that works with model binding
You are right, those attributes (and specially the name attribute) are critical for the model binding.
Say you want to create a custom helper like
public static MvcHtmlString CustomHelperFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
First you can use var fieldName = ExpressionHelper.GetExpressionText(expression);
to get the field name.
Then use var fullBindingName = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(fieldName);
in order to get the full name, taking care of nested views.
Finally you can transform this into an id attribute using var fieldId = TagBuilder.CreateSanitizedId(fullBindingName);
.
So a simple custom helper that creates a textbox could be written as:
public static MvcHtmlString CustomHelperFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
var fieldName = ExpressionHelper.GetExpressionText(expression);
var fullBindingName = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(fieldName);
var fieldId = TagBuilder.CreateSanitizedId(fullBindingName);
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
var value = metadata.Model;
TagBuilder tag = new TagBuilder("input");
tag.Attributes.Add("name", fullBindingName);
tag.Attributes.Add("id", fieldId);
tag.Attributes.Add("type", "text");
tag.Attributes.Add("value", value == null ? "" : value.ToString());
var validationAttributes = html.GetUnobtrusiveValidationAttributes(fullBindingName, metadata);
foreach (var key in validationAttributes.Keys)
{
tag.Attributes.Add(key, validationAttributes[key].ToString());
}
return new MvcHtmlString(tag.ToString(TagRenderMode.SelfClosing));
}
You can use it in a view like:
@Html.CustomHelperFor(model => model.ParentDropDown.SelectedValue)
And it will produce the following html:
<input id="ParentDropDown_SelectedValue" name="ParentDropDown.SelectedValue" type="text" value="4">
Hope it helps!
ASP.NET MVC 3 Custom HTML Helpers- Best Practices/Uses
Well in the case of formatting the DisplayFormat attribute could be a nice solution:
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime Date { get; set; }
and then simply:
@Html.DisplayFor(x => x.Date)
As far as truncating string is concerned a custom HTML helper is a good solution.
UPDATE:
Concerning your EDIT, a custom HTML helper might work in this situation but there's also an alternative approach which I like very much: view models. So if in this particular view you are always going to show the concatenation of the names then you could define a view model:
public class PersonViewModel
{
public string FullName { get; set; }
}
Now the controller is going to query the repository to fetch the model and then map this model to a view model which will be passed to the view so that the view could simply @Html.DisplayFor(x => x.FullName)
. The mapping between models and view models could be simplified with frameworks like AutoMapper.
How to create HtmlHelper extensions with Razor?
Helpers create normal page methods, not extension methods:
@IncludeJS("")
If you want to create an Html
extension method, you'll need to create a normal extension method (in a .cs
file) for the HtmlHelper
class.
If you do that, you can use the TagBuilder
class.
EDIT: The Views\Helpers
feature was dropped before RTM.
MVC 3 Razor, Helpers with custom markup/section
Well, here's a "standard" way of doing something close to it, using an HTML helper extension. A very simple version of what Html.BeginForm()
does.
Approach: Simply return an IDisposable, and let the using
statement take care of the rest.
This is just an example of the concept (although it works). Not intended for immediate reuse. Written quickly with lots of shortcuts, not production code, plenty of opportunities for improvement and optimization, may have silly mistakes, could use TagBuilder etc. etc. Could easily be modified to reuse the Wrapper class for different... wrappings (there may even be a generic one already in ASP.NET MVC - haven't had a need for one).
public static class WrapHelper
{
private class Wrapper : IDisposable
{
private bool disposed;
private readonly TextWriter writer;
public Wrapper(HtmlHelper html)
{
this.writer = html.ViewContext.Writer;
}
public void Dispose()
{
if (disposed) return;
disposed = true;
writer.WriteLine(" </div>");
writer.WriteLine("</div>");
}
}
public static IDisposable Wrap(this HtmlHelper html, string title)
{
TextWriter writer = html.ViewContext.Writer;
writer.WriteLine("<div class=\"module\">");
writer.WriteLine(" <h3>" + html.Encode(title) + "</h3>");
writer.WriteLine(" <div>");
return new Wrapper(html);
}
}
Usage:
@using (Html.Wrap("Title"))
{
<p>My very own markup.</p>
}
Related Topics
How Do Prefix (++X) and Postfix (X++) Operations Work
ASP.NET MVC 2 - Bind a Model's Property to a Different Named Value
C# "Parameter Is Not Valid." Creating New Bitmap
How to Check If Wpf Is Currently Executing in Design Mode or Not
Dynamically Created Controls Losing Data After Postback
Stop Tabcontrol from Recreating Its Children
Sharing Memory Between Two Applications
Running Scripts in HTMLagilitypack
Predefined Type 'System.Valuetuple'2' Is Not Defined or Imported
How to Handle Key Press Event in Console Application
Ignore Mapping One Property with Automapper
Passing Data Between Different Controller Action Methods
How to Correctly Cast a Class to an Abstract Class When Using Type Generics