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

Ben*_*ale 5 c# razor asp.net-mvc-3

鉴于这样的视图模型:

public class ParentViewModel
{
    public object ChildViewModel { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

如果我这样使用Html.LabelFor:

@Html.LabelFor(model => model.ChildViewModel)
Run Code Online (Sandbox Code Playgroud)

我会得到这样的输出:

<label for="Model_ChildViewModel">ChildViewModel</label>
Run Code Online (Sandbox Code Playgroud)

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

[DisplayName("My Custom Label")]
public class ChildViewModel
{
}
Run Code Online (Sandbox Code Playgroud)

输出:

<label for="Model_ChildViewModel">My Custom Label</label>
Run Code Online (Sandbox Code Playgroud)

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

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

public static IHtmlString CreateLabel<TModel>(this HtmlHelper html, Func<TModel, object> 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());
    }
Run Code Online (Sandbox Code Playgroud)

我的助手不使用表达式,而是使用a Func<TModel, object>返回我想要检查DisplayName属性的对象.

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

@Html.CreateLabel(model => model.ChildObject)
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

The type arguments for method 'CreateLabel<TModel>(this HtmlHelper,
Func<TModel, object>) cannot be inferred from usage. Try specifying
the arguments explicitly.'
Run Code Online (Sandbox Code Playgroud)

所以我调用这样的方法代替:

 @{ Html.CreateLabel<ChildViewModel>(model => model.ChildObject); }
Run Code Online (Sandbox Code Playgroud)

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

所以我的问题是:

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

更新

我想我会发布最终代码,因为我做了一些小改动.首先,我查看了MVC源代码中的帮助程序,并决定将该方法拆分为三个单独的方法,与提供的示例一致.我也删除了所有的TagBuilder东西,因为我真正需要的是生成要在<legend></legend>标签之间注入的文本.最终代码如下.再次感谢Sylon帮助我解决这个问题.

public static IHtmlString LegendTextFor<TModel, TObject>(this HtmlHelper<TModel> html, Expression<Func<TModel, TObject>> expression)
{
    return LegendTextHelper(html,
        ModelMetadata.FromLambdaExpression(expression, html.ViewData),
        ExpressionHelper.GetExpressionText(expression),
        expression.Compile().Invoke(html.ViewData.Model));
}

private static IHtmlString LegendTextHelper<TModel, TObject>(this HtmlHelper<TModel> 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<T>(this T obj)
{
    if (obj != null)
    {
        var attribute = obj.GetType()
            .GetCustomAttributes(typeof(DisplayNameAttribute), false)
            .Cast<DisplayNameAttribute>()
            .FirstOrDefault();

        return attribute != null ? attribute.DisplayName : null;
    }
    else
    {
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

lah*_*rah 2

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

Html 助手:

public static class HtmlHelperExtensions
{

    public static MvcHtmlString LabelForCustom<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> 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<DisplayNameAttribute>()
           .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));
    }
}
Run Code Online (Sandbox Code Playgroud)

我的示例模型:

public class Parent
{
    public object Child { get; set; }
}

[DisplayName("yo")]
public class Child 
{
   public int Id { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

看法:

@Html.LabelForCustom(m => m.Child)  @*prints yo*@
Run Code Online (Sandbox Code Playgroud)