web-dev-qa-db-fra.com

Acceptez la virgule et le point comme séparateur décimal

La liaison de modèle dans ASP.NET MVC est excellente, mais elle suit les paramètres régionaux. Dans ma région, le séparateur décimal est une virgule (','), mais les utilisateurs utilisent également le point ('.'), Car ils sont paresseux pour changer de disposition. Je veux que cela soit implémenté en un seul endroit pour tous les champs decimal dans mes modèles.

Dois-je implémenter mon propre fournisseur de valeur (ou classeur de modèle d'événement) pour le type decimal ou j'ai manqué un moyen simple de le faire?

22
artvolk

Le moyen le plus propre consiste à implémenter votre propre classeur de modèle

public class DecimalModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

        return valueProviderResult == null ? base.BindModel(controllerContext, bindingContext) : Convert.ToDecimal(valueProviderResult.AttemptedValue);
        // of course replace with your custom conversion logic
    }    
}

Et enregistrez-le dans Application_Start ():

ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());

Crédits: le classeur de modèle ASP.NET MVC 3 par défaut ne lie pas les propriétés décimales

38
mathieu

Pour gérer correctement le séparateur de groupe, remplacez simplement

Convert.ToDecimal(valueProviderResult.AttemptedValue);

dans la réponse sélectionnée avec

Decimal.Parse(valueProviderResult.AttemptedValue, NumberStyles.Currency);
5
Miguel Veloso

Grâce à la réponse acceptée, je me suis retrouvé avec l'implémentation suivante pour gérer flottant, double et décimal.

public abstract class FloatingPointModelBinderBase<T> : DefaultModelBinder
{
    protected abstract Func<string, IFormatProvider, T> ConvertFunc { get; }

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (valueProviderResult == null) return base.BindModel(controllerContext, bindingContext);
        try
        {
            return ConvertFunc.Invoke(valueProviderResult.AttemptedValue, CultureInfo.CurrentUICulture);
        }
        catch (FormatException)
        {
            // If format error then fallback to InvariantCulture instead of current UI culture
            return ConvertFunc.Invoke(valueProviderResult.AttemptedValue, CultureInfo.InvariantCulture);
        }
    }
}

public class DecimalModelBinder : FloatingPointModelBinderBase<decimal>
{
    protected override Func<string, IFormatProvider, decimal> ConvertFunc => Convert.ToDecimal;
}

public class DoubleModelBinder : FloatingPointModelBinderBase<double>
{
    protected override Func<string, IFormatProvider, double> ConvertFunc => Convert.ToDouble;
}

public class SingleModelBinder : FloatingPointModelBinderBase<float>
{
    protected override Func<string, IFormatProvider, float> ConvertFunc => Convert.ToSingle;
}

Ensuite, il vous suffit de définir vos ModelBinders sur Application_Start méthode

ModelBinders.Binders[typeof(float)] = new SingleModelBinder();
ModelBinders.Binders[typeof(double)] = new DoubleModelBinder();
ModelBinders.Binders[typeof(decimal)] = new DecimalModelBinder();
3
labilbe
var nfInfo = new System.Globalization.CultureInfo(lang, false)
{
    NumberFormat =
    {
        NumberDecimalSeparator = "."
    }
};
Thread.CurrentThread.CurrentCulture = nfInfo;
Thread.CurrentThread.CurrentUICulture = nfInfo;
2
Denis