8

I have a couple of hidden inputs on an asp.net mvc view. Their values contain objects of type double. I want them to be rendered with the InvariantCulture as they are used to be fed to an api (google maps) on the client. As it is now, they get rendered with a comma (,) as decimal separator, while the api expects a point (.) as decimal separator.

The nicest solution would be if I could specify a culture in the DisplayFormat data annotation attribute on the property on the model, but I don't think that is possible:

public class Position
{
    [DisplayFormat(DataFormatString="{0:G}, CultureInfo.InvariantCulture")]
    public double Latitude;
    ...
}

I can't also just set the CurrentCulture to InvariantCulture in my Application_Start method, as there are other values on the screen which have to be in the proper user culture.

So, is there a way to just temporarily change the current culture, right before I do an Html.HiddenFor(Model => Model.Latitude) for that specific property, and then reset it afterwards?

Or is there another better way of doing this? What is considered best practice concerning this?

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
fretje
  • 8,322
  • 2
  • 49
  • 61

3 Answers3

4

One way to achieve this would be to write a custom editor template ~/Views/Shared/EditorTemplates/InvariantDouble.cshtml:

@{
    object modelValue = string.Format(
        System.Globalization.CultureInfo.InvariantCulture,
        ViewData.ModelMetadata.DisplayFormatString, 
        ViewData.ModelMetadata.Model
    );
}
@Html.TextBox("", modelValue, new { @class = "text-box single-line" })

and then on your model:

[DisplayFormat(DataFormatString="{0:G}")]
[UIHint("InvariantDouble")]
public double Latitude;

and finally on your view:

@Html.EditorFor(x => x.Latitude)

or if you wanted all doubles in your application to behave like this use the ~/Views/Shared/EditorTemplates/Double.cshtml without any UIHint.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Thanks, but what if I also have a `[HiddenInput(DisplayValue=false)]` attribute on the property? – fretje Mar 14 '11 at 20:08
  • @fretje, then override `HiddenInput.cshtml`. Brad Wilson covers the default templates at this blog post: http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-3-default-templates.html – Darin Dimitrov Mar 14 '11 at 20:12
  • I did it a little bit different... see [my answer](http://stackoverflow.com/questions/5300525/how-do-i-render-only-some-specific-model-fields-with-an-invariantculture-while-t/5309154#5309154). – fretje Mar 15 '11 at 08:32
1

I ended up creating a template (Position.chtml) for a specific sub-object Position in my model like this:

@model Models.Position 
@{
    var latitude = string.Format(System.Globalization.CultureInfo.InvariantCulture, 
                                 "{0:G}", Model.Latitude);
    var longitude = string.Format(System.Globalization.CultureInfo.InvariantCulture, 
                                  "{0:G}", Model.Longitude); 
} 
@Html.Hidden("Latitude", latitude) 
@Html.Hidden("Longitude", longitude) 
<p /> 
<div id="mapCanvas"></div>

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script> 
<script type="text/javascript" src="@Url.Content("~/Scripts/maps.js")"></script>
<script type="text/javascript">
    $(function () {
        attachMap($('#mapCanvas'), $('#Position_Latitude'), $('#Position_Longitude'), true);
    }) 
</script>

This way, I don't need to add any attributes to the model class.

I'm wondering though... is it good practice including javascript like this? I don't know of any other way to do it (except including it in the master page, which I don't want to do as I don't need it on all pages) without having to repeat myself. I'd like to keep this DRY...

Also (I might better ask a new question for this): As it is now, I have this template 2 times, once as an editor template, once as a display template. The only difference is that the last parameter to attachMap has to be true for the editor template and false for the display template. Is there an easy way to make this DRY?

fretje
  • 8,322
  • 2
  • 49
  • 61
  • Old post but I'll put my two cents anyway... First your scripts `~/Scripts/map.js` and the google maps script will be included each time this partial view will be rendered. Could be bad if you show more than one map in a given view. Also if you have more than one map you may want to customize the `mapCanvas` id so it is unique in the page. Third, you could use another partial view that is called by both editor and display template, and receive info for the `attachMap` boolean parameters. – Johnny5 Sep 14 '11 at 16:10
0
    public static MvcHtmlString HiddenForInvariant<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
        var id = string.Format("{0}", metadata.PropertyName);
        var compile = expression.Compile();
        string value = Convert.ToString(compile(htmlHelper.ViewData.Model), CultureInfo.InvariantCulture);
        var hidden = htmlHelper.Hidden(id, value).ToHtmlString();
        return new MvcHtmlString(hidden);
    }
Gluip
  • 2,917
  • 4
  • 37
  • 46