tags:

views:

85

answers:

2

I have a bunch of forms where currency values are entered and I want them to be able to enter "$1,234.56". By default, the model binders won't parse that into a decimal.

What I am thinking of doing is creating a custom model binder the inherits DefaultModelBinder, override the BindProperty method, check if the property descriptor type is decimal and if it is, just strip out the $ and , from the values.

Is this the best approach?

Code:

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

Update

This is what I ended up doing:

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  A: 

It's reasonable to do it in the binder. However, I think that Decimal.Parse with the currency format provider or number style (see the docs) would be more reliable than stripping the "$" and calling base. For starters, it would handle non-US currency, which might be an issue for you some day.

Craig Stuntz
Wow. Didn't know Decimal.Parse had formatting that will accept that by default. You'd think there would be a way for the model binders to accept that by default then. In fact, on this example http://www.asp.net/%28S%28ywiyuluxr3qb2dfva1z5lgeg%29%29/learn/mvc/tutorial-39-cs.aspx they even have a decimal type with a regex validator that says a $ is ok... If I have a decimal type, a value of $1,234 or $1234 comes through as 0, and the model state isn't valid.
Josh Close
I looked through the MVC source and the DataAnnotationsModelBinder source, and I think it would be best to call base. There is a lot of stuff going on, including handling the errors and model state. I think doing what I was doing before, but using the decimal.Parse function instead will work nicely.
Josh Close
A: 

You could create your own ValidationAttribute which checks if value has correct format. Then you could look if property is decorated with this attribute and bind it in proper way. Attribute doesn't need to be ValidationAttibute, but it seems like good idea.

LukLed