6

I have implemented a MVC Extension to format the numbers in my application. It is based off the code found here. And is as follows

public static MvcHtmlString DecimalBoxFor<TModel>(this HtmlHelper<TModel> html, Expression<Func<TModel, double?>> expression, string format, object htmlAttributes = null)
{
    var name = ExpressionHelper.GetExpressionText(expression);   
    double? dec = expression.Compile().Invoke(html.ViewData.Model);
    var value = dec.HasValue ? (!string.IsNullOrEmpty(format) ? dec.Value.ToString(format) : dec.Value.ToString()): "";
    return html.TextBox(name, value, htmlAttributes);
}

When I call it with the following line of Razor syntax

@Html.DecimalBoxFor(model => Model.PointAttributes[i].Data.Y,"0.000", new { @class = "span1 number" })

I get an exception because the variable 'name' in my extension is an empty string. I have tried changing the var name line to this but it only gives me the property name of 'Y' and not the full 'Model.PointAttributes[i].Data.Y' that I need to bind the model back for MVC.

var name = ((expression.Body is MemberExpression ?((MemberExpression)expression.Body).Member : ((MemberExpression)((UnaryExpression)expression.Body).Operand).Member)).Name;
Community
  • 1
  • 1
PlTaylor
  • 7,345
  • 11
  • 52
  • 94

5 Answers5

9

Try using this function:

    static public string GetExpressionText(LambdaExpression p)
    {
        if (p.Body.NodeType == ExpressionType.Convert || p.Body.NodeType == ExpressionType.ConvertChecked)
        {
            p = Expression.Lambda(((UnaryExpression)p.Body).Operand,
                p.Parameters);
        }
        return ExpressionHelper.GetExpressionText(p);
    }
Nadav
  • 357
  • 3
  • 10
  • 1
    This answer led me to the solution, and I created/released [Mariuzzo.Web.Mvc.Extras](https://github.com/rmariuzzo/Mariuzzo.Web.Mvc.Extras) – Rubens Mariuzzo Feb 21 '13 at 14:36
7

This is a known behavior. I have figured out writing my own version of ExpressionHelper that handle that specific case. Now you have two option:

  1. Use the NuGet package:

    Install-Package Mariuzzo.Web.Mvc.Extras
    
  2. Or just grab the source code of the aforementioned ExpressionHelper and glue it into your project.

Rubens Mariuzzo
  • 28,358
  • 27
  • 121
  • 148
3

Here a 'hybrid' one :)

    public static void AddModelError<TModel>(this ModelStateDictionary state, Expression<Func<TModel, object>> expression, string message)
    {
        LambdaExpression lambdaExpression = null;
        string fieldName = string.Empty;

        if (expression.Body.NodeType == ExpressionType.Convert || expression.Body.NodeType == ExpressionType.ConvertChecked)
        {
            lambdaExpression = Expression.Lambda(((UnaryExpression)expression.Body).Operand, expression.Parameters);
            fieldName = ExpressionHelper.GetExpressionText(lambdaExpression);
        } else {
            fieldName = ExpressionHelper.GetExpressionText(expression);
        }

        state.AddModelError(fieldName, message);
    }

This one is more compact and probably a better solution:

https://stackoverflow.com/a/12689563/951001

Community
  • 1
  • 1
KoalaBear
  • 2,755
  • 2
  • 25
  • 29
0

If you can get away without using a nullable type it seems to work (i.e. remove the ? after double, or as in my case decimal). So

Expression<Func<TModel, double?>> 

becomes

Expression<Func<TModel, double>>.  

If you step through it with the nullable type in place you'll see the expression has a convert() function in it which seems to be the 'problem'. I'm sure like me you would be interested in how to make this function work for nullable types as well.

user1433194
  • 23
  • 1
  • 4
0

I know it's closed but for the record;

That's better handled by a template so you can specify what datatype you are using in the model and how it is represented in the template (single responsability). Also you won't need to modify the MVC framework.

MSDN UiHint attribute

Aridane Álamo
  • 324
  • 3
  • 12