如何在HtmlHelper的扩展方法中获取传递的lambda表达式的属性值是最简单的方式?

54

我正在编写一个 HtmlHelper 的扩展方法,以便我可以像使用 HtmlHelper.WysiwygFor(lambda) 这样的语法来显示 CKEditor。

目前我已经使其能够工作,但感觉比我想象的更加繁琐。希望有更简单直接的方法。

以下是我目前的代码:

public static MvcHtmlString WysiwygFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
    return MvcHtmlString.Create(string.Concat("<textarea class=\"ckeditor\" cols=\"80\" id=\"",
                                        expression.MemberName(), "\" name=\"editor1\" rows=\"10\">", 
                                        GetValue(helper, expression),
                                        "</textarea>"));
}

private static string GetValue<TModel, TProperty>(HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
    MemberExpression body = (MemberExpression)expression.Body;
    string propertyName = body.Member.Name;
    TModel model = helper.ViewData.Model;
    string value = typeof(TModel).GetProperty(propertyName).GetValue(model, null).ToString();
    return value;
}

private static string MemberName<T, V>(this Expression<Func<T, V>> expression)
{
    var memberExpression = expression.Body as MemberExpression;
    if (memberExpression == null)
            throw new InvalidOperationException("Expression must be a member expression");

    return memberExpression.Member.Name;
}

谢谢!


我测试了你的Getvalue方法,当helper是selectlist时,结果很好。其他方法在这种情况下失败了。 - calterras
6个回答

85

试试这样:

public static MvcHtmlString Try<TModel, TProperty>(
    this HtmlHelper<TModel> htmlHelper, 
    Expression<Func<TModel, TProperty>> expression
)
{
    var builder = new TagBuilder("textarea");
    builder.AddCssClass("ckeditor");
    builder.MergeAttribute("cols", "80");
    builder.MergeAttribute("name", "editor1");
    builder.MergeAttribute("id", expression.Name); // not sure about the id - verify
    var value = ModelMetadata.FromLambdaExpression(
        expression, htmlHelper.ViewData
    ).Model;
    builder.SetInnerText(value.ToString());
    return MvcHtmlString.Create(builder.ToString());
}

6
我对以上内容中的这行代码非常感兴趣: var value = ModelMetadata.FromLambdaExpression( expression, htmlHelper.ViewData ).Model; 它运行得很好!不过我发现你所提取的表达式名称 expression.Name 返回 null。此外,我将其替换为我的 MemberName 方法,以使 ID 能够正常工作。另外,我还发现了一个用于添加 ID 属性的 TagBuilder.GenerateId() 方法。谢谢。我一定会在我的即将出版的书(ASP.NET MVC Cookbook - http://groups.google.com/group/aspnet-mvc-2-cookbook-review)中提到你。 - Andrew Siemer
嗨,达林,你能解释一下这行代码吗?var value = ModelMetadata.FromLambdaExpression( expression, htmlHelper.ViewData ).Model; - Carlos Miguel Colanta

26
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
Object value = metadata.Model;
String name = metadata.PropertyName;

这对我有效,并通过PropertyName正确获取属性名称,而expression.Name返回null。编辑以添加 - 我正在运行MVC5,如果有影响的话。 - Nick Van Brunt

10

我知道这是一个旧的帖子,但以防万一有人正在寻找,生成id / name属性的方法也是这样:

System.Web.Mvc.ExpressionHelper.GetExpressionText(expression);

我在我的扩展程序中使用它,从未遇到任何问题。它也可以很好地处理嵌套属性。


2
这将获取属性的名称,但不包括其值。 - bjan

5

最简单的方法是将所有内容封装在扩展方法中:

public static class ExtensionMethods
{   

    public static object Value<TModel, TProperty>(this Expression<Func<TModel, TProperty>> expression, ViewDataDictionary<TModel> viewData)
    {
        return ModelMetadata.FromLambdaExpression(expression, viewData).Model;
    }  

}

因此,调用语法为:
expression.Value(htmlHelper.ViewData)

2

1

虽然Peter和BigMomma的回答都没有涉及到这个问题,但是这个方法结合了两者。如果你正在从控制器方法中调用此方法,而你又无法访问HtmlHelper实例,只需创建一个基础控制器方法:

public ModelMetadata GetModelMetadata<TModel, TProperty>( TModel model, Expression<Func<TModel, TProperty>> expression )
{
    ViewData.Model = model; //model is null in Controller; you must set it here (or earlier) in order to extract values from the returned ModelMetadata.
    return ModelMetadata.FromLambdaExpression( expression, new ViewDataDictionary<TModel>( ViewData ) );
}

然后,您可以像往常一样从模型元数据中读取所需内容;
var mm = GetModelMetaData( model, m => m.SomeProperty );
string name = mm.PropertyName;
object value = mm.Model;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接