DisplayFormat for TextBoxFor in MVC - asp.net-mvc

I need to round off 4 digit decimal to 2 digits and show in MVC 3 UI
Something like this 58.8964 to 58.90
Tried following this How should I use EditorFor() in MVC for a currency/money type? but not working.
As i am using TextBoxFor=> i removed ApplyFormatInEditMode here. Even
i tried with ApplyFormatInEditMode , but nothing works. Still showing
me 58.8964.
MyModelClass
[DisplayFormat(DataFormatString = "{0:F2}")]
public decimal? TotalAmount { get; set; }
#Html.TextBoxFor(m=>m.TotalAmount)
How can i achieve this round off?
I can't use EditorFor(m=>m.TotalAmount) here, as i need to pass some htmlAttributes
Edit:
After debugging with MVC source code, they internally use
string valueParameter = Convert.ToString(value, CultureInfo.CurrentCulture);
in MvcHtmlString InputHelper() method of InputExtension.cs that takes object value as parameter and converting. They are not using any display format there. How could we fix?
I managed to fix in this way. As i have a custom helper, i can able to manage with the below code
if (!string.IsNullOrEmpty(modelMetaData.DisplayFormatString))
{
string formatString = modelMetaData.DisplayFormatString;
string formattedValue = String.Format(CultureInfo.CurrentCulture, formatString, modelMetaData.Model);
string name = ExpressionHelper.GetExpressionText(expression);
string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
return htmlHelper.TextBox(fullName, formattedValue, htmlAttributes);
}
else
{
return htmlHelper.TextBoxFor(expression, htmlAttributes);
}

This works in MVC5
#Html.TextBoxFor(m => m.TotalAmount, "{0:0.00}")

You should use Html.EditorFor instead of Html.TextBoxFor if you want the custom format to be taken into account:
#Html.EditorFor(m => m.TotalAmount)
Also make sure that you have set ApplyFormatInEditMode to true:
[DisplayFormat(DataFormatString = "{0:F2}", ApplyFormatInEditMode = true)]
public decimal? TotalAmount { get; set; }
The DisplayFormat attribute is intended to be used only with templated helpers such as EditorFor and DisplayFor. This is the recommended approach instead of using TextBoxFor.

Try like this:
#{
var format = String.Format("{0:0.00}", Model.TotalAmount);
}
#Html.TextBoxFor(m => m.TotalAmount, format)
Hope it helps.

If you need more control of the field being displayed (vs. a built in EditorFor template) create a custom EditorFor template yourself. Inside an EditorFor template, the built in Html Helpers like #Html.TextBox() can be used with automatic client side validation and Display attributes which are usually only available to EditorFor and DisplayFor.
For example looping through a List of items. The input name has to have an unbroken index.
// Optional, if you require a custom input name for binding
String fieldName = String.Format("FieldNameForBinding[{0}].Property", index)
#Html.EditorFor(m => m.Property, "MyCustomEditorTemplate", fieldName)
Then you can setup your model
[CustomValidation(..optional..)]
[DisplayFormat(DataFormatString = "{0:F2}", ApplyFormatInEditMode = true)]
public decimal? TotalAmount { get; set; }
The EditorFor template (in e.g. ~/Views/Shared/EditorFor/MyCustomEditorTemplate.cshtml)
Note the name is left empty, it comes from the fieldName automatically. You can see it in the ViewData.TemplateInfo.HtmlFieldPrefix. Now you have complete control over the display of the field.
#model object
#{
Decimal? actualValue = (Decimal?)Model;
}
// The TextBox and ValidationMessage "names" are empty, they are filled
// from the htmlFieldName given via the #Html.EditorFor() call.
#Html.TextBox("", actualValue, new { #class = "cssClass" })
#Html.ValidationMessage("")
The idea is that you can customize the input field however you would like, and use e.g. #Html.TextBox() which outside of a custom EditorFor template would not utilize the built in client-side validation. You don't need to use the custom naming of the input field, that was simply an example of the usefulness of this solution. You can customize the way the data is presented (CSS, etc.) instead of relying on the built in EditorFor templates.

I solve that issue in this way:
#Html.TextBoxFor(model => model.dtArrivalDate, ModelMetadata.FromLambdaExpression(model => model.dtArrivalDate, ViewData).EditFormatString)
or create next extension:
public static class HtmlExtensions
{
public static MvcHtmlString TextBoxWithFormatFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
{
return htmlHelper.TextBoxFor(expression, ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).EditFormatString, htmlAttributes);
}
}
But you need to set ApplyFormatInEditMode=true in DisplayFormatAttribute on your field.

Related

MVC5 / Razor TextAreaFor null value

I have a very basic #Html.TextAreaFor() on a form, and for some reason, the text area's value never makes it to my controller.
VIEW
<p class="SmallText">(Multiple Entries Allowed)</p>
#Html.TextAreaFor(x => x.quickSearch, new { cols = 30, #rows = 5 })
VIEW-MODEL
public String quickSearch;
CONTROLLER
public ActionResult FindRecord(FindRecordViewModel Model)
{
var ImNotCrazy = Model.quickSearch;
}
The problem is that when I debug Model.quickSearch is always null and never has a value.
Your view model has to have Properties, not fields, to work properly with model binding. So change public String quickSearch; to public String quickSearch { get; set; }.
Also, you should use standard naming conventions and change the name of the field to QuickSearch

Calling IEnumerable overload of DisplayNameFor [duplicate]

This question already has answers here:
DisplayNameFor() From List<Object> in Model
(5 answers)
Closed 6 years ago.
This works for grabbing the headers(NOT VALUES):
#model IEnumerable<SomeModel>
...
<th>#Html.DisplayNameFor(m => m.SomeModelProperty)</th>
Which if SomeModelProperty were:
[Display(Name = "An Excellent Header")]
SomeModelProperty { get; set; }
Then it would display "An Excellent Header" in the header th element.
You would think this wouldn't work because the model is IEnumerable, which wouldn't have a m.SomeModelProperty, but it works because HtmlHelper has a HtmlHelper<IEnumerable<TModel>> such that the parameter of the lambda is TModel, not IEnumerable<TModel>. Since this just uses metadata, there is no need for an item from the collection. (Although intellisense on m. will lie to you and make you think it's a collection). I'm not sure when this cool overload was added, but is quite handy for Index.cshtml and does away with funky things like #Html.DisplayNameFor(m => #Model.FirstOrDefault().SomeModelProperty) which I want to avoid.
http://msdn.microsoft.com/en-us/library/hh833697(v=vs.108).aspx
However, I can't figure out how to get this to work when my model is not IEnumerable, but instead contains IEnumerable as a property, such as:
public class SomeList
{
public List<SomeModel> SomeModels { get; set; }
public int Page { get; set; }
public DateTime CurrentAsOf { get; set; }
}
I was hoping to be explicit with the generic type parameters, but I think the type parameters are specified by the engine that trickles down from the HtmlHelper created with the page. Can I declare a new HtmlHelper in the page, or somehow specify the type parameters explicitly?
Index.cshtml:
#model SomeList
//No idea how to do this:
#Html.DisplayNameFor<IEnumerable<SomeModel>>(m => m.SomeModelProperty)
Another similar workaround that works even if there are no rows could be:
...
#{var dummy = Model.FirstOrDefault(); }
<tr>
<th>
#Html.DisplayNameFor(model => dummy.SomeModelProperty)
</th>
...
I have exactly the same issue because I am using ViewModels so I have a ViewModel with an IEnumerable of actual objects as a property.
I did come across this post where if you check the answer the guy has created his own HTMLHelper for it to solve this issue http://forums.asp.net/t/1783733.aspx. His version is:
public static MvcHtmlString DisplayColumnNameFor<TModel, TClass, TProperty>(
this HtmlHelper<TModel> helper, IEnumerable<TClass> model,
Expression<Func<TClass, TProperty>> expression)
{
var name = ExpressionHelper.GetExpressionText(expression);
name = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
var metadata = ModelMetadataProviders.Current.GetMetadataForProperty(
() => Activator.CreateInstance<TClass>(), typeof(TClass), name);
return new MvcHtmlString(metadata.DisplayName);
}
You have to pass two arguments enumeration and expression rather than the normal just expression so you may prefer #franz answer. I can't see there being anyway round having to pass 2 arguments since it needs to know which property of the view model you are applying the expression to.

DisplayFormat.DataFormatString for a phone number or social security number

Is there a way I can use the DisplayFormat attribute on a view model property to apply a DataFormatString format for a social security number or a phone number? I know I could do this with javascript, but would prefer to have the model handle it, if possible.
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "???????")]
public string Ssn { get; set; }
The following should work, however notice the type difference for the Ssn property.
[DisplayFormat(DataFormatString = "{0:###-###-####}")]
public long Phone { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:###-##-####}")]
public long Ssn { get; set; }
Note, that in order for the formatting to be applied you would need to use the following html helper in your view:
#Html.DisplayFor(m => m.Property)
The accepted answer is great if the type is an integer, but a lot of ids wind up being typed as a string to prevent losing leading zeros. You can format a string by breaking it up into pieces with Substring and make it reusable by sticking it in a DisplayTemplate.
Inside /Shared/DisplayTemplates/, add a template named Phone.vbhtml:
#ModelType String
#If Not IsNothing(Model) AndAlso Model.Length = 10 Then
#<span>#String.Format("({0}) {1}-{2}",
Model.Substring(0, 3),
Model.Substring(3, 3),
Model.Substring(6, 4))</span>
Else
#Model
End If
You can invoke this in a couple ways:
Just annotate the property on your model with a data type of the same name:
<DataType("Phone")> _
Public Property Phone As String
And then call using a simple DisplayFor:
#Html.DisplayFor(Function(model) model.Phone)
Alternatively, you can specify the DisplayTemplate you'd like to use by name:
#Html.DisplayFor(Function(model) model.VimsOrg.Phone, "Phone")

ASP.NET MVC 3 Custom HTML Helpers- Best Practices/Uses

New to MVC and have been running through the tutorials on the asp.net website.
They include an example of a custom html helper to truncate long text displayed in a table.
Just wondering what other solutions people have come up with using HTML helpers and if there are any best practices or things to avoid when creating/using them.
As an example, I was considering writing a custom helper to format dates that I need to display in various places, but am now concerned that there may be a more elegant solution(I.E. DataAnnotations in my models)
Any thoughts?
EDIT:
Another potential use I just thought of...String concatenation.
A custom helper could take in a userID as input and return a Users full name...
The result could be some form of (Title) (First) (Middle) (Last) depending on which of those fields are available. Just a thought, I have not tried anything like this yet.
I use HtmlHelpers all the time, most commonly to encapsulate the generation of boilerplate HTML, in case I change my mind. I've had such helpers as:
Html.BodyId(): generates a conventional body id tag for referencing when adding custom css for a view.
Html.SubmitButton(string): generates either an input[type=submit] or button[type=submit] element, depending on how I want to style the buttons.
Html.Pager(IPagedList): For generating paging controls from a paged list model.
etc....
One of my favorite uses for HtmlHelpers is to DRY up common form markup. Usually, I have a container div for a form line, one div for the label, and one label for the input, validation messages, hint text, etc. Ultimately, this could end up being a lot of boilerplate html tags. An example of how I have handled this follows:
public static MvcHtmlString FormLineDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string labelText = null, string customHelpText = null, object htmlAttributes = null)
{
return FormLine(
helper.LabelFor(expression, labelText).ToString() +
helper.HelpTextFor(expression, customHelpText),
helper.DropDownListFor(expression, selectList, htmlAttributes).ToString() +
helper.ValidationMessageFor(expression));
}
public static MvcHtmlString FormLineEditorFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string templateName = null, string labelText = null, string customHelpText = null, object htmlAttributes = null)
{
return FormLine(
helper.LabelFor(expression, labelText).ToString() +
helper.HelpTextFor(expression, customHelpText),
helper.EditorFor(expression, templateName, htmlAttributes).ToString() +
helper.ValidationMessageFor(expression));
}
private static MvcHtmlString FormLine(string labelContent, string fieldContent, object htmlAttributes = null)
{
var editorLabel = new TagBuilder("div");
editorLabel.AddCssClass("editor-label");
editorLabel.InnerHtml += labelContent;
var editorField = new TagBuilder("div");
editorField.AddCssClass("editor-field");
editorField.InnerHtml += fieldContent;
var container = new TagBuilder("div");
if (htmlAttributes != null)
container.MergeAttributes(new RouteValueDictionary(htmlAttributes));
container.AddCssClass("form-line");
container.InnerHtml += editorLabel;
container.InnerHtml += editorField;
return MvcHtmlString.Create(container.ToString());
}
public static MvcHtmlString HelpTextFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string customText = null)
{
// Can do all sorts of things here -- eg: reflect over attributes and add hints, etc...
}
Once you've done this, though, you can output form lines like this:
<%: Html.FormLineEditorFor(model => model.Property1) %>
<%: Html.FormLineEditorFor(model => model.Property2) %>
<%: Html.FormLineEditorFor(model => model.Property3) %>
... and BAM, all your labels, inputs, hints, and validation messages are on your page. Again, you can use attributes on your models and reflect over them to get really smart and DRY. And of course this would be a waste of time if you can't standardize on your form design. However, for simple cases, where css can supply all the customization you need, it works grrrrrrrrreat!
Moral of the story -- HtmlHelpers can insulate you from global design changes wrecking hand crafted markup in view after view. I like them. But you can go overboard, and sometimes partial views are better than coded helpers. A general rule of thumb I use for deciding between helper vs. partial view: If the chunk of HTML requires a lot of conditional logic or coding trickery, I use a helper (put code where code should be); if not, if I am just outputting common markup without much logic, I use a partial view (put markup where markup should be).
Hope this gives you some ideas!
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.
public static HtmlString OwnControlName<T, U>(this HtmlHelper<T> helper, Expression<Func<T, U>> expression, string label_Name = "", string label_Title = "", Attr attr = null)
{
TemplateBuilder tb = null;
string template = null;
if (expression == null) throw new ArgumentException("expression");
obj = helper.ViewData.Model;
tb.Build(obj, expression.Body as MemberExpression, typeof(T), new SimpleTemplate(new TextArea()), label_Name, label_Title, attr);
template = tb.Get();
return new MvcHtmlString(template);
}

ASP.NET MVC2 and DateTime Format

I am using ASP.NET MVC2 with MvcContrib.FluentHtml to do form binding.
I want to bind a DateTime format to a textbox with specific datetime format.
<%=this.TextBox(c => c.date_a).Class("readonly text-box") %>
// PS. c.date_a is a DateTime
gives me
<input type="text" value="5/9/2009 12:00:00 AM" name="date_a" id="datea" class="readonly text-box">
However, I'd like to override the default datetime format.
e.g.
value="2009-5-9"
value="9-5-09"
value="09May9"
I know I can override the value by specific a value, however, the date should also bind to the object class on POST the form.
How to do "minimum" code to override the default datetime format of a specific field on UI?
I don't know if this work with MvcContrib MvcContrib.FluentHtml but without it, it is very simple, add to your model property
[DisplayFormat(DataFormatString="{0:MM/dd/yyyy}", ApplyFormatInEditMode=true)]
and in your view
m.DateProperty) %>
I Don't know if MvcContrib uses Attributes but if it doesn't, it should, that way, you'll always have your date the same format, specifying the format only once...
hope this help
First, add this extension for getting property path:
public static string GetPropertyPath<TEntity, TProperty>(Expression<Func<TEntity, TProperty>> property)
{
Match match = Regex.Match(property.ToString(), #"^[^\.]+\.([^\(\)]+)$");
return match.Groups[1].Value;
}
Than add this extensions for HtmlHalper:
public static MvcHtmlString DateBoxFor<TEntity>(
this HtmlHelper helper,
TEntity model,
Expression<Func<TEntity, DateTime?>> property,
object htmlAttributes)
{
DateTime? date = property.Compile().Invoke(model);
var value = date.HasValue ? date.Value.ToShortDateString() : string.Empty;
var name = ExpressionParseHelper.GetPropertyPath(property);
return helper.TextBox(name, value, htmlAttributes);
}
Also you should add this jQuery code:
$(function() {
$("input.datebox").datepicker();
});
datepicker is a jQuery plugin.
And now you can use it:
<%= Html.DateBoxFor(Model, (x => x.Entity.SomeDate), new { #class = "datebox" }) %>
I often run into situations where it's impractical to modify the model (with attributes etc) so being able to implement a solution like Сергій's is important. To Dennis Cheung point though, you can more tightly bind this solution as follows:
public static MvcHtmlString DateBoxFor<TModel>(this HtmlHelper<TModel> helper, Expression<Func<TModel, DateTime?>> property)
{
return helper.DateBoxFor(property, "d", null);
}
public static MvcHtmlString DateBoxFor<TModel>(this HtmlHelper<TModel> helper, Expression<Func<TModel, DateTime?>> property, string format, object htmlAttributes)
{
var viewData = helper.ViewContext.ViewData;
var date = property.Compile().Invoke(viewData.Model);
var value = date.HasValue ? date.Value.ToString(format) : string.Empty;
var name = viewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(property));
return helper.TextBox(name, value, htmlAttributes);
}
Then you call it like this:
<%:Html.DateBoxFor(m => m.SomeDate)%>
Or like this:
<%:Html.DateBoxFor(m => m.SomeDate, "d", null)%>
With FluentHtml you can do like this:
<%=this.TextBox(c => c.date_a).Class("readonly text-box").Format("yyyy-M-d") %>
I am not sure whether this would cause you problems when the value is posted back but the following syntax should allow you to change the format with which the date value is displayed.
<%=this.TextBox(c => c.date_a.ToString('yyyy-M-d')).Class("readonly text-box") %>
You will find further information on how to construct the format strings here.

Resources