Search code examples
asp.net-mvc-3data-annotationsglobalizationcurrency

Is it possible to force the currency for an MVC3 field with DataType as DataType.Currency


I'm writing an MVC3 application that reads in a bunch of monetary data from a database. The issue I have is that these amounts are all in different currencies.

If I set the type of a field like this:

[DataType(DataType.Currency)]
public Amount{ get; set;}

I get the decimal places and a currency symbol, which looks nice, but it defaults to the user's local currency. A US user sees $423.29 whereas a GB user sees £423.29. I can override the currency by using a <globalization culture="{something}"> in the Web.config, but this sets all currency fields globally.

What would be the easiest way of marking up a field so that it renders with the correct decimal places and currency symbol?

In an ideal world, I'd like to be able to do something like this (for USD):

[DataType(DataType.Currency, culture="en-us")]
public Amount{ get; set; }

and have that always render as $439.38, but that's not possible with the built-in annotations.


Solution

  • The way I would do this is to create a custom attribute that extends the DataType attribute and a custom html helper. It's not necessarily the easiest way of doing it but it would save time in the future.

    EDIT Incorporated CultureInfo.CreateSpecificCulture(cultureName) instead of a switch

    Custom Attribute

    public class CurrencyDisplayAttribute : DataTypeAttribute
    {
        public string Culture { get; set; }
    
        public CurrencyDisplayAttribute(string culture)
            : base(DataType.Currency)
        {
            Culture = culture;
        }
    }  
    

    Html Helper

    public static class Helpers
    {
        public static IHtmlString CurrencyDisplayFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
        {
            double value = double.Parse(expression.Compile().Invoke(helper.ViewData.Model).ToString());
            var metadata = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
    
            var prop = typeof (TModel).GetProperty(metadata.PropertyName);
    
            var attribute = prop.GetCustomAttribute(typeof (CurrencyDisplayAttribute)) as CurrencyDisplayAttribute;
    
            // this should be whatever html element you want to create
            TagBuilder tagBuilder = new TagBuilder("span");
            tagBuilder.SetInnerText(value.ToString("c", CultureInfo.CreateSpecificCulture(attribute.Culture));
    
            return MvcHtmlString.Create(tagBuilder.ToString());
        }
    }
    

    You can use the attribute in your model

    [CurrencyDisplay("en-us")]
    public double Amount { get; set; }
    

    Then in your view you can use the helper by

    @Html.CurrencyDisplayFor(x => x.Amount);
    

    Provided your model is passed in correctly.

    Obviously, you'd need to do error checking and so on.