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.
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.
Make string the amount
public string Amount{ get; set;}
Create a method that converts to string with currency exact
private string LocalizedAmount(decimal theAmount, string cultureName)
{
return theAmount.ToString("c",CultureInfo.CreateSpecificCulture(cultureName));
}
If you are stored in your database two fields or columns one for value and one for culture.
And in the Repository or the controller:
Amount = LocalizedAmount(Convert.ToDecimal(reader[0]),reader[1].ToString());
You need to assing the culture:
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureName);
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(cultureName);
Inside this Controller overrided methods:
protected override void OnActionExecuted(ActionExecutedContext filterContext)
protected override void ExecuteCore()
Read the whole technique in this NadeemAfana's blog ASP.NET MVC 3 Internationalization