Is there any way to get UpdateModel or TryUpdateModel to parse a money or currency formatted value such as $1,200.00 into a decimal without blowing chunks?
A:
Are you able to parse the value up front before calling either of these methods? If so, you could use the following method to do so
var provider = (NumberFormatInfo)CultureInfo.InvariantCulture.NumberFormat.Clone();
provider.CurrencySymbol = "$";
var x = decimal.Parse(
"$1,200",
NumberStyles.AllowCurrencySymbol | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands,
provider);
AdamRalph
2009-11-22 23:12:51
This i think would be great as a html helper
griegs
2009-11-22 23:28:50
Well parsing it normally isn't a problem, but I have a number of "money" fields and I'd rather not junk of my controllers parsing around TryUpdateModel, if at all possible.
cadmium
2009-11-23 01:15:51
@cadmium use a custom model binder, see the link in my answer.
eglasius
2009-11-25 16:14:27
Not as simple or elegant as I was hoping, but with some tweaking it actually worked, so thank you. I will posted my tweaked binder.
cadmium
2009-12-01 21:48:56
+1
A:
Answer was awarded to Freddy Rios since his link provided me with the base to do this, but the code needed some fixing up:
// http://www.crydust.be/blog/2009/07/30/custom-model-binder-to-avoid-decimal-separator-problems/
public class MoneyParsableModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
object result = null;
// Added support for decimals and nullable types - c.
if (
bindingContext.ModelType == typeof(double)
|| bindingContext.ModelType == typeof(decimal)
|| bindingContext.ModelType == typeof(double?)
|| bindingContext.ModelType == typeof(decimal?)
)
{
string modelName = bindingContext.ModelName;
string attemptedValue = bindingContext.ValueProvider[modelName].AttemptedValue;
// Depending on cultureinfo the NumberDecimalSeparator can be "," or "."
// Both "." and "," should be accepted, but aren't.
string wantedSeperator = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
string alternateSeperator = (wantedSeperator == "," ? "." : ",");
if (attemptedValue.IndexOf(wantedSeperator) == -1
&& attemptedValue.IndexOf(alternateSeperator) != -1)
{
attemptedValue = attemptedValue.Replace(alternateSeperator, wantedSeperator);
}
// If SetModelValue is not called it may result in a null-ref exception if the model is resused - c.
bindingContext.ModelState.SetModelValue(modelName, bindingContext.ValueProvider[modelName]);
try
{
// Added support for decimals and nullable types - c.
if (bindingContext.ModelType == typeof(double) || bindingContext.ModelType == typeof(double?))
{
result = double.Parse(attemptedValue, NumberStyles.Any);
}
else
{
result = decimal.Parse(attemptedValue, NumberStyles.Any);
}
}
catch (FormatException e)
{
bindingContext.ModelState.AddModelError(modelName, e);
}
}
else
{
result = base.BindModel(controllerContext, bindingContext);
}
return result;
}
}
It ain't pretty, but it works.
cadmium
2009-12-01 21:52:39