2010-03-16 4 views
5

Je voudrais créer une fonctionnalité de liaison de modèle afin qu'un utilisateur puisse entrer ',' '.' etc pour les valeurs de devises qui se lient à une double valeur de mon ViewModel.asp.net liaison de modèle de devise MVC 1.0 et 2.0

J'ai été en mesure de le faire dans MVC 1.0 en créant un classeur de modèle personnalisé, mais depuis la mise à niveau vers MVC 2.0, cette fonctionnalité ne fonctionne plus.

Est-ce que quelqu'un a des idées ou de meilleures solutions pour effectuer cette fonctionnalité? Une meilleure solution consisterait à utiliser des annotations de données ou des attributs personnalisés.

public class MyViewModel 
{ 
    public double MyCurrencyValue { get; set; } 
} 

Une solution préférée serait quelque chose comme ça ...

public class MyViewModel 
{ 
    [CurrencyAttribute] 
    public double MyCurrencyValue { get; set; } 
} 

Ci-dessous est ma solution pour le modèle obligatoire dans MVC 1.0.

public class MyCustomModelBinder : DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     object result = null; 

     ValueProviderResult valueResult; 
     bindingContext.ValueProvider.TryGetValue(bindingContext.ModelName, out valueResult); 
     bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueResult); 

     if (bindingContext.ModelType == typeof(double)) 
     { 
      string modelName = bindingContext.ModelName; 
      string attemptedValue = bindingContext.ValueProvider[modelName].AttemptedValue; 

      string wantedSeperator = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator; 
      string alternateSeperator = (wantedSeperator == "," ? "." : ","); 

      try 
      { 
       result = double.Parse(attemptedValue, NumberStyles.Any); 
      } 
      catch (FormatException e) 
      { 
       bindingContext.ModelState.AddModelError(modelName, e); 
      } 
     } 
     else 
     { 
      result = base.BindModel(controllerContext, bindingContext); 
     } 

     return result; 
    } 
} 

Répondre

7

Vous pouvez essayer quelque chose entre les lignes:

// Just a marker attribute 
public class CurrencyAttribute : Attribute 
{ 
} 

public class MyViewModel 
{ 
    [Currency] 
    public double MyCurrencyValue { get; set; } 
} 


public class CurrencyBinder : DefaultModelBinder 
{ 
    protected override object GetPropertyValue(
     ControllerContext controllerContext, 
     ModelBindingContext bindingContext, 
     PropertyDescriptor propertyDescriptor, 
     IModelBinder propertyBinder) 
    { 
     var currencyAttribute = propertyDescriptor.Attributes[typeof(CurrencyAttribute)]; 
     // Check if the property has the marker attribute 
     if (currencyAttribute != null) 
     { 
      // TODO: improve this to handle prefixes: 
      var attemptedValue = bindingContext.ValueProvider 
       .GetValue(propertyDescriptor.Name).AttemptedValue; 
      return SomeMagicMethodThatParsesTheAttemptedValue(attemtedValue); 
     } 
     return base.GetPropertyValue(
      controllerContext, 
      bindingContext, propertyDescriptor, 
      propertyBinder 
     ); 
    } 
} 

public class HomeController: Controller 
{ 
    [HttpPost] 
    public ActionResult Index([ModelBinder(typeof(CurrencyBinder))] MyViewModel model) 
    { 
     return View(); 
    } 
} 

MISE À JOUR:

est ici une amélioration du liant (voir section TODO dans le code précédent):

if (!string.IsNullOrEmpty(bindingContext.ModelName)) 
{ 
    var attemptedValue = bindingContext.ValueProvider 
     .GetValue(bindingContext.ModelName).AttemptedValue; 
    return SomeMagicMethodThatParsesTheAttemptedValue(attemtedValue); 
} 

Dans afin de gérer les collections, vous devez enregistrer le liant dans Application_Start que vous ne serez plus en mesure de décorer la liste avec le ModelBinderAttribute:

protected void Application_Start() 
{ 
    AreaRegistration.RegisterAllAreas(); 
    RegisterRoutes(RouteTable.Routes); 
    ModelBinders.Binders.Add(typeof(MyViewModel), new CurrencyBinder()); 
} 

Et puis votre action pourrait ressembler à ceci:

[HttpPost] 
public ActionResult Index(IList<MyViewModel> model) 
{ 
    return View(); 
} 

pour résumer la partie importante:

bindingContext.ValueProvider.GetValue(bindingContext.ModelName) 

Une étape supplémentaire d'amélioration de ce liant serait de gérer la validation (AddModelErro r/SetModelValue)

+0

Si vous traitez une liste de MyViewModel cela change-t-il le ModelBinder pour l'action? public ActionResult Index ([ModelBinder (typeof (CurrencyBinder))] IList modèle ) – David

+0

S'il vous plaît voir ma mise à jour. –

Questions connexes