0

MVC4, Code First, C# project

When populating a money field with either a explicit value or from a table read the TextBoxFor displays the value with 2 decimal places. If the field is populated from a money field in another class it displays 4 decimal places.

  public class Class1
  {
    [Column(TypeName = "money")]
    public decimal Field1 { get; set; }
  }

  public class Class2
  {
    [Column(TypeName = "money")]
    public decimal Field1 { get; set; }
  }

  public class Table1
  {
    public int Id { get; set; } public decimal Value { get; set; }
  }

Scenario 1:

Class1.Field1 = 14.95M;

Scenario 2:

Class2.Field1 = Table1.Value;

Scenario 3:

Class1.Field1 = Class2.Field1

View

@Html.TextBoxFor(m => m.Class1.Field1, new { style = "width:70px;" })

For Scenario 1 & 2 the TextBoxFor correctly displays 2 decimal places, with Scenario 3 it displays 4 decimal places in the edit box. I need to use TextBoxFor so I can pass html attributes.

The instance of Class2 is itself pre-populated from values in a Table generated by Class2. I've examined everything with SSMS [all the applicable fields in the tables are (money, not null)] and in debug and cannot find any discrepancies.

Why does the TextBoxFor incorrectly display the money format for Scenario 3 (I understand that SQL stores decimals with 4 decimal precision)?

More importantly how do I get my edit box to always display money values with 2 decimals?

Joe
  • 4,143
  • 8
  • 37
  • 65

2 Answers2

0

In my MVC app I wanted a text box to display like $4.95. I used editor template.

@if(Model != null && Model.GetType() == typeof(string))
{
    @Html.TextBox(
    "",
    string.Format("{0:c}", (decimal)decimal.Parse(Model))
    )
}

@if(Model != null && Model.GetType() == typeof(decimal))
{
    @Html.TextBox(
        "",
       string.Format("{0:c}", (decimal) Model),new {@class="no-number-validation"}
         )
}

@if(Model == null)
{
    @Html.TextBox("",null,new {@class="no-number-validation"})
}

And then obviously I wanted to be able to send back $4.95 to the server and have the Model Binder still handle it automatically. This example handles % signs too.

public class DecimalModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext,
                            ModelBindingContext bindingContext)
    {
        ValueProviderResult valueResult = bindingContext.ValueProvider
            .GetValue(bindingContext.ModelName);
        ModelState modelState = new ModelState { Value = valueResult };
        object actualValue = null;
        try
        {
            if (valueResult.AttemptedValue.StartsWith("$"))
            {
                actualValue = decimal.Parse(valueResult.AttemptedValue, NumberStyles.Currency);
            }

            if (valueResult.AttemptedValue.EndsWith("%"))
            {
                actualValue = decimal.Parse(valueResult.AttemptedValue.Replace("%", "").Trim(),
                                            CultureInfo.CurrentCulture);
            }

            if (actualValue == null)
                actualValue = Convert.ToDecimal(valueResult.AttemptedValue,
                                                CultureInfo.CurrentCulture);
        }
        catch (FormatException e)
        {
            modelState.Errors.Add(e);
        }

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

This is nice because I don't have to use a string as my property type for the currency textbox in order to handle the $ sign. By the time the object gets populated there is no money sign and the value gets assigned right into the decimal type.

The Muffin Man
  • 19,585
  • 30
  • 119
  • 191
  • Thanks, Nick. That may all work but I came up with a much simpler solution. Please see my answer. – Joe Jul 03 '13 at 01:12
0

You can round to 2 decimals (original values are 2 decimal precision so I'm not worrying about rounding errors).

decimal.Round(Value, 2)

Class1.Field1 = decimal.Round(Class2.Field1,2)

It can then be implemented through an extension method.

public static decimal dR2(this decimal ip) { return decimal.Round(ip, 2); }

Class1.Field1 = Class2.Field1.dR2();
Joe
  • 4,143
  • 8
  • 37
  • 65