Html.Labelfor使用对象的DisplayName而不是属性

鉴于这样的视图模型:

public class ParentViewModel { public object ChildViewModel { get; set; } } 

如果我像这样使用Html.LabelFor

 @Html.LabelFor(model => model.ChildViewModel) 

我会得到这样的输出:

  

我实际想要的是生成的标签使用应用于对象EG的DisplayName属性

 [DisplayName("My Custom Label")] public class ChildViewModel { } 

输出:

  

我知道Html.LabelFor方法接受一个需要一个属性的表达式,它将在该属性上查找DisplayName属性而不是对象本身。

我创建了一个Html帮助器方法来实现我想要的样子:

 public static IHtmlString CreateLabel(this HtmlHelper html, Func func) where TModel : class { TagBuilder tb = new TagBuilder("label"); var model = html.ViewData.Model as TModel; if (model != null) { object obj = func(model); if (obj != null) { var attribute = obj.GetType().GetCustomAttributes( typeof(DisplayNameAttribute), true) .FirstOrDefault() as DisplayNameAttribute; if (attribute != null) { tb.InnerHtml = attribute.DisplayName; return MvcHtmlString.Create(tb.ToString()); } else { tb.InnerHtml = obj.ToString(); return MvcHtmlString.Create(tb.ToString()); } } } tb.InnerHtml = html.ViewData.Model.ToString(); return MvcHtmlString.Create(tb.ToString()); } 

我的助手不使用表达式,而是使用Func ,它返回我想要检查DisplayName属性的对象。

我遇到的第一个问题是当我试图用razor调用这个方法时:

 @Html.CreateLabel(model => model.ChildObject) 

我收到以下错误:

 The type arguments for method 'CreateLabel(this HtmlHelper, Func) cannot be inferred from usage. Try specifying the arguments explicitly.' 

所以我改为调用这样的方法:

  @{ Html.CreateLabel(model => model.ChildObject); } 

但什么都没有渲染。 如果我使用调试器来逐步执行我的帮助器方法,则会生成label标签,但在呈现页面时不会显示任何内容。

所以我的问题是:

  • 如何解决此问题以生成我的视图标签?
  • 我该怎么做才能推断出通用参数?
  • 有没有办法编写Html助手来做同样的事情,但使用表达式? 我没有使用表达式的经验,所以不知道从哪里开始。

更新

我想我会发布最终代码,因为我做了一些小改动。 首先,我查看了MVC源代码中的帮助程序,并决定将该方法拆分为三个独立的方法,与提供的示例一致。 我还删除了所有TagBuilder东西,因为我真正需要的是生成要在

标记之间注入的文本。 最终代码如下。 再次感谢Sylon帮助我解决这个问题。

 public static IHtmlString LegendTextFor(this HtmlHelper html, Expression<Func> expression) { return LegendTextHelper(html, ModelMetadata.FromLambdaExpression(expression, html.ViewData), ExpressionHelper.GetExpressionText(expression), expression.Compile().Invoke(html.ViewData.Model)); } private static IHtmlString LegendTextHelper(this HtmlHelper html, ModelMetadata metadata, string htmlFieldName, TObject value) { string resolvedLabelText = metadata.DisplayName ?? value.GetDisplayName() ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last(); if (String.IsNullOrEmpty(resolvedLabelText)) return MvcHtmlString.Empty; return MvcHtmlString.Create(resolvedLabelText); } private static string GetDisplayName(this T obj) { if (obj != null) { var attribute = obj.GetType() .GetCustomAttributes(typeof(DisplayNameAttribute), false) .Cast() .FirstOrDefault(); return attribute != null ? attribute.DisplayName : null; } else { return null; } } 

我刚刚为标签创建了一个自定义的Html帮助程序,可以执行您想要的操作:

Html助手:

 public static class HtmlHelperExtensions { public static MvcHtmlString LabelForCustom(this HtmlHelper html, Expression> expression) { ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData); string customDisplayName = null; var value = expression.Compile().Invoke(html.ViewData.Model); if (value != null) { var attribute = value.GetType().GetCustomAttributes(typeof(DisplayNameAttribute), false) .Cast() .FirstOrDefault(); customDisplayName = attribute != null ? attribute.DisplayName : null; } string htmlFieldName = ExpressionHelper.GetExpressionText(expression); string labelText = metadata.DisplayName ?? customDisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last(); if (String.IsNullOrEmpty(labelText)) { return MvcHtmlString.Empty; } TagBuilder tag = new TagBuilder("label"); tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName)); tag.SetInnerText(labelText); return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal)); } } 

我的示例模型:

 public class Parent { public object Child { get; set; } } [DisplayName("yo")] public class Child { public int Id { get; set; } } 

视图:

 @Html.LabelForCustom(m => m.Child) @*prints yo*@