2010-03-16 20 views
5

Me gustaría crear una funcionalidad de enlace de modelo para que un usuario pueda ingresar ',' '.' etc. para los valores de moneda que se unen a un valor doble de mi ViewModel.asp.net enlace de modelo de moneda MVC 1.0 y 2.0

Pude hacer esto en MVC 1.0 creando una carpeta de modelo personalizada, sin embargo, desde la actualización a MVC 2.0 esta funcionalidad ya no funciona.

¿Alguien tiene alguna idea o mejor solución para realizar esta funcionalidad? Una mejor solución sería usar alguna anotación de datos o un atributo personalizado.

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

Una solución preferida sería algo como esto ...

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

A continuación es mi solución para la unión de MVC 1.0 modelo.

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; 
    } 
} 

Respuesta

7

Es posible probar algo entre las líneas:

// 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(); 
    } 
} 

ACTUALIZACIÓN:

Aquí es una mejora del aglutinante (ver TODO sección de código anterior):

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

In Para manejar colecciones tendrá que registrar el aglutinante en Application_Start medida que ya no será capaz de decorar la lista con el ModelBinderAttribute:

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

Y entonces su acción podría tener este aspecto:

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

Resumiendo la parte importante:

bindingContext.ValueProvider.GetValue(bindingContext.ModelName) 

Un paso mejora adicional de esta carpeta sería para manejar validación (AddModelErro r/SetModelValue)

+0

Si está tratando con una lista de MyViewModel ¿eso cambia el ModelBinder para la acción? public ActionResult Índice ([ModelBinder (typeof (CurrencyBinder))] IList modelo) – David

+0

Por favor, consulte mi actualización. –

Cuestiones relacionadas