开发者

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.开发者_如何学Go


In MVC3, you can set width as follows:

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


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!


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#.


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"])) %>


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 ;-)


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


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


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.


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


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.


I wrote a blog entry to answer my own question

Adding html attributes support for Templates - ASP.Net MVC 2.0 Beta


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">


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.


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.


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.


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.


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


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})


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜