How can I override the currency formatting for the

2020-04-12 07:15发布

问题:

The currency decimal and thousand separators for the en-ZA region are ',' and ' ' respectively, but the separators in common use are '.' for decimal, plus my user wants ',' for the thousands separator. I wish to set these globally, so that I only have to use the {0:C} format string for all my currency fields, without having to any explicit Format or ToString calls.

I would prefer to be able to do this without changing the culture settings on the web server, as I also need to set the decimal places for currency to zero, as cents are not wanted when reporting on estimates of R100k and up etc. I wouldn't want to arbitrarily set the whole culture to zero places, just one for this application.

In comments to his answer on this question, Jon Skeet suggests cloning the current culture and setting and changing the required settings. I have done this as follows:

void Application_Start(object sender, EventArgs e)
{
    var newCulture = (CultureInfo)CultureInfo.CurrentCulture.Clone();
    newCulture.NumberFormat.CurrencyDecimalSeparator = ".";
    newCulture.NumberFormat.CurrencyGroupSeparator = ",";
}

However, how to I activate that new culture for all requests the application handles from this point on? Is there another way to achieve what I wish to do?

回答1:

You can use Application_BeginRequest event to set the culture for every request. Inside event:

var newCulture = (CultureInfo)CultureInfo.CurrentCulture.Clone();
newCulture.NumberFormat.CurrencyDecimalSeparator = ".";
newCulture.NumberFormat.CurrencyGroupSeparator = ",";

System.Threading.Thread.CurrentThread.CurrentCulture = newCulture;
System.Threading.Thread.CurrentThread.CurrentUICulture = newCulture;


回答2:

Having asked many questions and done many experiments, I have decided that it's safe to state that the only way of doing this is to use controls derived from the out of box controls and do your own formatting using a customised culture object. Derive your control from e.g. BoundField and provide your own FormatProvider:

public class BoundReportField : BoundField
{
    protected virtual string GetDefaultFormatString(FieldFormatTypes formatType)
    {
        var prop = typeof(FormatStrings).GetProperty(formatType.ToString()).GetValue(null, null);
        return prop.ToString();
    }

    protected virtual IFormatProvider GetFormatProvider(FieldFormatTypes formatType)
    {
        var info = (CultureInfo)CultureInfo.CurrentCulture.Clone();
        info.NumberFormat.CurrencyDecimalDigits = 0;
        info.NumberFormat.CurrencySymbol = "R";
        info.NumberFormat.CurrencyGroupSeparator = ",";
        info.NumberFormat.CurrencyDecimalSeparator = ".";
        return info;
    }

    private FieldFormatTypes _formatType;
    public virtual FieldFormatTypes FormatType
    {
        get { return _formatType; }
        set
        {
            _formatType = value;
            DataFormatString = GetDefaultFormatString(value);
        }
    }

    protected override string FormatDataValue(object dataValue, bool encode)
    {
        // TODO Consider the encode flag.
        var formatString = DataFormatString;
        var formatProvider = GetFormatProvider(_formatType);
        if (string.IsNullOrWhiteSpace(formatString))
        {
            formatString = GetDefaultFormatString(_formatType);
        }
        return string.Format(formatProvider, formatString, dataValue);
    }
}

I will publish an article later with all the gory details.