Matthias Matthias - 1 month ago 15
C# Question

ASP.NET MVC3 double validation (comma, point, null)

My controller looks like this:

public class PortefeuilleController : Controller
{
public ActionResult Create()
{
return View(new PortefeuilleViewModel{Saldo = 0.0});
}
}


My create view looks like this:

@model PortefeuilleViewModel

@{
ViewBag.Title = "Create";
}

<h2>Create</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>PortefeuilleViewModel</legend>

<div class="editor-label">
@Html.LabelFor(model => model.Naam)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Naam)
@Html.ValidationMessageFor(model => model.Naam)
</div>

<div class="editor-label">
@Html.LabelFor(model => model.Saldo)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Saldo)
@Html.ValidationMessageFor(model => model.Saldo)
</div>

<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}


And my PortefeuilleViewModel looks like this:

public class PortefeuilleViewModel
{
[DisplayName("Naam")]
[Required(ErrorMessage = "Gelieve een naam in te voeren")]
public string Naam { get; set; }

[DisplayName("Saldo")]
public double Saldo { get; set; }
}


My problem lies with the "Saldo" field. I want to validate it as follows:

Leaving the field empty should be valid (because my code-behind will change "" into "0.0" and save that value in the database). But, for example, both '19,99' and '19.99' should also be passed as being valid. All the rest is invalid.

I don't really know how I can pull this off. I already found this article: http://haacked.com/archive/2011/03/19/fixing-binding-to-decimals.aspx
But that didn't solve (the second part of) my problem at all...

Any help would be greatly appreciated. Thanks.

Answer

OK, so you have validation activated at 2 levels: server side and client side. Let's first deal with the server side validation issue.

The first thing when working with money (which is what I suppose the Saldo field represents is to use decimals, not doubles). So the first change would be:

[DisplayName("Saldo")]
public decimal Saldo { get; set; }

OK, now let's adapt a little the Haacked model binder to your situation (accepting . and , as decimal separator as well as empty values)

public class DecimalModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (string.IsNullOrEmpty(valueResult.AttemptedValue))
        {
            return 0m;
        }
        var modelState = new ModelState { Value = valueResult };
        object actualValue = null;
        try
        {
            actualValue = Convert.ToDecimal(
                valueResult.AttemptedValue.Replace(",", "."), 
                CultureInfo.InvariantCulture
            );
        }
        catch (FormatException e)
        {
            modelState.Errors.Add(e);
        }

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

which of course will be registered in Application_Start:

ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());

At this stage we have solved the server side validation issue. As far as the client side validation is concerned you could use the jQuery globalization plugin that Microsoft released. Scott Hanselman has also blogged about it. So once you install it you could force the client side culture and override the $.validator.methods.number method which is responsible for performing the client side validation:

$.validator.methods.number = function (value, element) {
    // TODO: return true or false if value is a valid decimal
}