[c#] EditorFor() and html properties

Asp.Net MVC 2.0 preview builds provide helpers like

Html.EditorFor(c => c.propertyname)

If the property name is string, the above code renders a texbox.

What if I want to pass in MaxLength and Size properties to the text box or my own css class property?

Do I need to create one template for each size and length combinations in my application? If so, that doesn't make the default templates that usable.

This question is related to c# asp.net-mvc asp.net-mvc-2

The answer is


I really liked @tjeerdans answer which utilizes the EditorTemplate named String.ascx in the /Views/Shared/EditorTemplates folder. It seems to be the most straight-forward answer to this question. However, I wanted a template using Razor syntax. In addition, it seems that MVC3 uses the String template as a default (see the StackOverflow question "mvc display template for strings is used for integers") so you need to set the model to object rather than string. My template seems to be working so far:

@model object 

@{  int size = 10; int maxLength = 100; }

@if (ViewData["size"] != null) {
    Int32.TryParse((string)ViewData["size"], out size); 
} 

@if (ViewData["maxLength"] != null) {
    Int32.TryParse((string)ViewData["maxLength"], out maxLength); 
}

@Html.TextBox("", Model, new { Size = size, MaxLength = maxLength})

The problem is, your template can contain several HTML elements, so MVC won't know to which one to apply your size/class. You'll have to define it yourself.

Make your template derive from your own class called TextBoxViewModel:

public class TextBoxViewModel
{
  public string Value { get; set; }
  IDictionary<string, object> moreAttributes;
  public TextBoxViewModel(string value, IDictionary<string, object> moreAttributes)
  {
    // set class properties here
  }
  public string GetAttributesString()
  {
     return string.Join(" ", moreAttributes.Select(x => x.Key + "='" + x.Value + "'").ToArray()); // don't forget to encode
  }

}

In the template you can do this:

<input value="<%= Model.Value %>" <%= Model.GetAttributesString() %> />

In your view you do:

<%= Html.EditorFor(x => x.StringValue) %>
or
<%= Html.EditorFor(x => new TextBoxViewModel(x.StringValue, new IDictionary<string, object> { {'class', 'myclass'}, {'size', 15}}) %>

The first form will render default template for string. The second form will render the custom template.

Alternative syntax use fluent interface:

public class TextBoxViewModel
{
  public string Value { get; set; }
  IDictionary<string, object> moreAttributes;
  public TextBoxViewModel(string value, IDictionary<string, object> moreAttributes)
  {
    // set class properties here
    moreAttributes = new Dictionary<string, object>();
  }

  public TextBoxViewModel Attr(string name, object value)
  {
     moreAttributes[name] = value;
     return this;
  }

}

   // and in the view
   <%= Html.EditorFor(x => new TextBoxViewModel(x.StringValue).Attr("class", "myclass").Attr("size", 15) %>

Notice that instead of doing this in the view, you may also do this in controller, or much better in the ViewModel:

public ActionResult Action()
{
  // now you can Html.EditorFor(x => x.StringValue) and it will pick attributes
  return View(new { StringValue = new TextBoxViewModel(x.StringValue).Attr("class", "myclass").Attr("size", 15) });
}

Also notice that you can make base TemplateViewModel class - a common ground for all your view templates - which will contain basic support for attributes/etc.

But in general I think MVC v2 needs a better solution. It's still Beta - go ask for it ;-)


This may not be the slickest solution, but it is straightforward. You can write an extension to the HtmlHelper.EditorFor class. In that extension, you can supply an options parameter that will write the options to the ViewData for the helper. Here's some code:

First, the extension method:

public static MvcHtmlString EditorFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, TemplateOptions options)
{
    return helper.EditorFor(expression, options.TemplateName, new
    {
        cssClass = options.CssClass
    });
}

Next, the options object:

public class TemplateOptions
{
    public string TemplateName { get; set; }
    public string CssClass { get; set; }
    // other properties for info you'd like to pass to your templates,
    // and by using an options object, you avoid method overload bloat.
}

And finally, here's the line from the String.ascx template:

<%= Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { @class = ViewData["cssClass"] ?? "" }) %>

Frankly, I think this is straightforward and clear to the poor soul who has to maintain your code down the road. And it is easy to extend for various other bits of info you'd like to pass to your templates. It's working well so far for me in a project where I'm trying to wrap as much as I can in a set of template to help standardize the surrounding html, a la http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-5-master-page-templates.html.


This is the cleanest and most elegant/simple way to get a solution here.

Brilliant blog post and no messy overkill in writing custom extension/helper methods like a mad professor.

http://geekswithblogs.net/michelotti/archive/2010/02/05/mvc-2-editor-template-with-datetime.aspx


UPDATE: hm, obviously this won't work because model is passed by value so attributes are not preserved; but I leave this answer as an idea.

Another solution, I think, would be to add your own TextBox/etc helpers, that will check for your own attributes on model.

public class ViewModel
{
  [MyAddAttribute("class", "myclass")]
  public string StringValue { get; set; }
}

public class MyExtensions
{
  public static IDictionary<string, object> GetMyAttributes(object model)
  {
     // kind of prototype code...
     return model.GetType().GetCustomAttributes(typeof(MyAddAttribute)).OfType<MyAddAttribute>().ToDictionary(
          x => x.Name, x => x.Value);
  }
}

<!-- in the template -->
<%= Html.TextBox("Name", Model, MyExtensions.GetMyAttributes(Model)) %>

This one is easier but not as convinient/flexible.


As at MVC 5, if you wish to add any attributes you can simply do

 @Html.EditorFor(m => m.Name, new { htmlAttributes = new { @required = "true", @anotherAttribute = "whatever" } })

Information found from this blog


You can define attributes for your properties.

[StringLength(100)]
public string Body { get; set; }

This is known as System.ComponentModel.DataAnnotations. If you can't find the ValidationAttribute that you need you can allways define custom attributes.

Best Regards, Carlos


I solved it!!
For Razor the syntax is:
@Html.TextAreaFor(m=>m.Address, new { style="Width:174px" }) this adjusts the text area width to the width that i defined in the style parameter.
For ASPx the syntax is:
<%=Html.TextAreaFor(m => m.Description, new { cols = "20", rows = "15", style="Width:174px" })%>
this will do the trick


I don't know why it does not work for Html.EditorFor but I tried TextBoxFor and it worked for me.

@Html.TextBoxFor(m => m.Name, new { Class = "className", Size = "40"})

...and also validation works.


Because the question is for EditorFor not TextBoxFor WEFX's suggestion doesn't work.

For changing individual input boxes, you can process the output of the EditorFor method:

<%: new HtmlString(Html.EditorFor(m=>m.propertyname).ToString().Replace("class=\"text-box single-line\"", "class=\"text-box single-line my500pxWideClass\"")) %>

It is also possible to change ALL your EditorFors as it turns out MVC sets the class of EditorFor text boxes with .text-box, therefore you can just override this style, in your stylesheet or on the page.

.text-box {
    width: 80em;
}

Additionally, you could set the style for

input[type="text"] {
    width: 200px;
}
  • this overrides .text-box and will change all input text boxes, EditorFor or otherwise.

None of the answers in this or any other thread on setting HTML attributes for @Html.EditorFor were much help to me. However, I did find a great answer at

Styling an @Html.EditorFor helper

I used the same approach and it worked beautifully without writing a lot of extra code. Note that the id attribute of the html output of Html.EditorFor is set. The view code

<style type="text/css">
#dob
{
   width:6em;
}
</style>

@using (Html.BeginForm())
{
   Enter date: 
   @Html.EditorFor(m => m.DateOfBirth, null, "dob", null)
}

The model property with data annotation and date formatting as "dd MMM yyyy"

[Required(ErrorMessage= "Date of birth is required")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd MMM yyyy}")]
public DateTime DateOfBirth { get; set; }

Worked like a charm without writing a whole lot of extra code. This answer uses ASP.NET MVC 3 Razor C#.


One way you could get round it is by having delegates on the view model to handle printing out special rendering like this. I've done this for a paging class, I expose a public property on the model Func<int, string> RenderUrl to deal with it.

So define how the custom bit will be written:

Model.Paging.RenderUrl = (page) => { return string.Concat(@"/foo/", page); };

Output the view for the Paging class:

@Html.DisplayFor(m => m.Paging)

...and for the actual Paging view:

@model Paging
@if (Model.Pages > 1)
{
    <ul class="paging">
    @for (int page = 1; page <= Model.Pages; page++)
    {
        <li><a href="@Model.RenderUrl(page)">@page</a></li>
    }
    </ul>
}

It could be seen as over-complicating matters but I use these pagers everywhere and couldn't stand seeing the same boilerplate code to get them rendered.


I solved this by creating an EditorTemplate named String.ascx in my /Views/Shared/EditorTemplates folder:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<string>" %>
<% int size = 10;
   int maxLength = 100;
   if (ViewData["size"] != null)
   {
       size = (int)ViewData["size"];
   }
   if (ViewData["maxLength"] != null)
   {
       maxLength = (int)ViewData["maxLength"];
   }
%>
<%= Html.TextBox("", Model, new { Size=size, MaxLength=maxLength }) %>

In my view, I use

<%= Html.EditorFor(model => model.SomeStringToBeEdited, new { size = 15, maxLength = 10 }) %>

Works like a charm for me!


I think using CSS is the way to go. I wish I could do more with .NET coding, like in XAML, but in the browser CSS is king.

Site.css

#account-note-input { 
  width:1000px; 
  height:100px; 
} 

.cshtml

<div class="editor-label"> 
  @Html.LabelFor(model => model.Note) 
</div> 
<div class="editor-field"> 
  @Html.EditorFor(model => model.Note, null, "account-note-input", null) 
  @Html.ValidationMessageFor(model => model.Note) 
</div>

Joe


I also had issue with setting the width of TextBox in MVC3, while setting the Clsss attribute worked for TextArea control but not for TextBoxFor control or EditorFor control:

I tried following & that worked for me:

@Html.TextBoxFor(model => model.Title, new { Class = "textBox", style = "width:90%;" })

also in this case Validations are working perfectly.


In MVC3, you can set width as follows:

@Html.TextBoxFor(c => c.PropertyName, new { style = "width: 500px;" })

In my practice I found that it is best to use EditorTemplates with only one HtmlHelper in it - TextBox that is in most cases. If I want a template for more complex html structure, I'll write a separate HtmlHelper.

Given that we can stick the whole ViewData object in place of htmlAttributes of the TextBox. In addition we can write some customization code for some of the properties of the ViewData if they need special treatment:

@model DateTime?
@*
    1) applies class datepicker to the input;
    2) applies additionalViewData object to the attributes of the input
    3) applies property "format" to the format of the input date.
*@
@{
    if (ViewData["class"] != null) { ViewData["class"] += " datepicker"; }
    else { ViewData["class"] = " datepicker"; }
    string format = "MM/dd/yyyy";
    if (ViewData["format"] != null)
    {
        format = ViewData["format"].ToString();
        ViewData.Remove("format");
    }
}

@Html.TextBox("", (Model.HasValue ? Model.Value.ToString(format) : string.Empty), ViewData)

Below are the examples of the syntax in the view and the outputted html:

@Html.EditorFor(m => m.Date)
<input class="datepicker" data-val="true" data-val-required="&amp;#39;Date&amp;#39; must not be empty." id="Date" name="Date" type="text" value="01/08/2012">
@Html.EditorFor(m => m.Date, new { @class = "myClass", @format = "M/dd" })
<input class="myClass datepicker" data-val="true" data-val-required="&amp;#39;Date&amp;#39; must not be empty." id="Date" name="Date" type="text" value="1/08">

May want to look at Kiran Chand's Blog post, he uses custom metadata on the view model such as:

[HtmlProperties(Size = 5, MaxLength = 10)]
public string Title { get; set; }

This is combined with custom templates that make use of the metadata. A clean and simple approach in my opinion but I would like to see this common use case built-in to mvc.


I'm surprised no one mentioned passing it in "additionalViewData" and reading it on the other side.

View (with line breaks, for clarity):

<%= Html.EditorFor(c => c.propertyname, new
    {
        htmlAttributes = new
        {
            @class = "myClass"
        }
    }
)%>

Editor template:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<string>" %>

<%= Html.TextBox("", Model, ViewData["htmlAttributes"])) %>

Examples related to c#

How can I convert this one line of ActionScript to C#? Microsoft Advertising SDK doesn't deliverer ads How to use a global array in C#? How to correctly write async method? C# - insert values from file into two arrays Uploading into folder in FTP? Are these methods thread safe? dotnet ef not found in .NET Core 3 HTTP Error 500.30 - ANCM In-Process Start Failure Best way to "push" into C# array

Examples related to asp.net-mvc

Using Lato fonts in my css (@font-face) Better solution without exluding fields from Binding Vue.js get selected option on @change You must add a reference to assembly 'netstandard, Version=2.0.0.0 How to send json data in POST request using C# VS 2017 Metadata file '.dll could not be found The default XML namespace of the project must be the MSBuild XML namespace How to create roles in ASP.NET Core and assign them to users? The model item passed into the dictionary is of type .. but this dictionary requires a model item of type How to use npm with ASP.NET Core

Examples related to asp.net-mvc-2

Better solution without exluding fields from Binding How to set the value of a hidden field from a controller in mvc Making a Simple Ajax call to controller in asp.net mvc ASP.Net MVC - Read File from HttpPostedFileBase without save Session state can only be used when enableSessionState is set to true either in a configuration Using Tempdata in ASP.NET MVC - Best practice Getting index value on razor foreach Current date and time - Default in MVC razor Url.Action parameters? How can I get all element values from Request.Form without specifying exactly which one with .GetValues("ElementIdName")