2009-12-16 8 views
9

Ho un mucchio di moduli in cui sono inseriti i valori di valuta e voglio che siano in grado di inserire "$ 1,234.56". Per impostazione predefinita, i raccoglitori di modelli non li analizzeranno in un decimale.Come filtrare i dati del modulo con il raccoglitore modello personalizzato

Quello che sto pensando di fare è creare un raccoglitore modello personalizzato che erediti DefaultModelBinder, sovrascrivi il metodo BindProperty, controlla se il tipo di descrittore di proprietà è decimale e, in caso affermativo, basta estrarre $ e, dai valori.

È questo l'approccio migliore?

Codice:

public class CustomModelBinder : DefaultModelBinder 
{ 
protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor) 
{ 
    if(propertyDescriptor.PropertyType == typeof(decimal) || propertyDescriptor.PropertyType == typeof(decimal?)) 
    { 
    var newValue = Regex.Replace(bindingContext.ValueProvider[propertyDescriptor.Name].AttemptedValue, @"[$,]", "", RegexOptions.Compiled); 
    bindingContext.ValueProvider[propertyDescriptor.Name] = new ValueProviderResult(newValue, newValue, bindingContext.ValueProvider[propertyDescriptor.Name].Culture); 
    } 

    base.BindProperty(controllerContext, bindingContext, propertyDescriptor); 
} 
} 

Aggiornamento

Questo è quello che ho finito per fare:

public class CustomModelBinder : DataAnnotationsModelBinder 
{ 
    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor) 
    { 
     if(propertyDescriptor.PropertyType == typeof(decimal) || propertyDescriptor.PropertyType == typeof(decimal?)) 
     { 
      decimal newValue; 
      decimal.TryParse(bindingContext.ValueProvider[propertyDescriptor.Name].AttemptedValue, NumberStyles.Currency, null, out newValue); 
      bindingContext.ValueProvider[propertyDescriptor.Name] = new ValueProviderResult(newValue, newValue.ToString(), bindingContext.ValueProvider[propertyDescriptor.Name].Culture); 
     } 
     base.BindProperty(controllerContext, bindingContext, propertyDescriptor); 
    } 
} 
+1

un'occhiata a questo post da Haacked: http://haacked.com/archive/2011/03/ 19/fixing-binding-to-decimals.aspx – VinnyG

risposta

5

E 'ragionevole farlo nel legante. Tuttavia, penso che lo Decimal.Parse con il fornitore di formato di valuta o lo stile di numero (see the docs) sia più affidabile dello stripping del "$" e chiamando lo base. Per i principianti, gestirà la valuta non statunitense, che potrebbe essere un problema per voi un giorno.

+1

Wow. Non sapevo che Decimal.Parse avesse una formattazione che lo accetterebbe di default. Penseresti che ci sarebbe un modo per i raccoglitori di modelli di accettarlo automaticamente. In effetti, in questo esempio http://www.asp.net/%28S%28ywiyuluxr3qb2dfva1z5lgeg%29%29/learn/mvc/tutorial-39-cs.aspx hanno persino un tipo decimale con un validatore di espressioni regolari che dice $ è ok ... Se ho un tipo decimale, un valore di $ 1,234 o $ 1234 passa come 0, e lo stato del modello non è valido. –

+0

Ho controllato l'origine MVC e l'origine DataAnnotationsModelBinder, e penso che sarebbe meglio chiamare la base. C'è un sacco di cose in corso, compresa la gestione degli errori e dello stato del modello. Penso che fare quello che stavo facendo prima, ma usare la funzione decimal.Parse funzionerà bene. –

1

È possibile creare il proprio ValidationAttribute che verifica se il valore ha il formato corretto. Quindi potresti controllare se la proprietà è decorata con questo attributo e vincolarla in modo appropriato. L'attributo non ha bisogno di essere ValidationAttibute, ma sembra una buona idea.

5

In MVC3 è possibile solo registrare un modelbinder personalizzato che implementa l'interfaccia di IModelBinder in modo specifico per i tipi decimali e quindi dirgli di gestire la valuta o il decimale utilizzando la proprietà ModelMetaData.DataTypeName sul bindingContext.

Ho modificato l'esempio fornito da Phil Haack in his article per dimostrare come potrebbe essere fatto:

public class DecimalModelBinder : IModelBinder 
    { 

     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
     { 
      var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 
      var modelState = new ModelState { Value = valueResult }; 

      decimal actualValue = 0; 
      try 
      { 

       if(bindingContext.ModelMetadata.DataTypeName == DataType.Currency.ToString()) 
        decimal.TryParse(valueResult.AttemptedValue, NumberStyles.Currency, null, out actualValue); 
       else 
        actualValue = Convert.ToDecimal(valueResult.AttemptedValue,CultureInfo.CurrentCulture); 


      } 
      catch (FormatException e) 
      { 
       modelState.Errors.Add(e); 
      } 

      bindingContext.ModelState.Add(bindingContext.ModelName, modelState); 
      return actualValue; 
     } 
    } 
Problemi correlati