如何显示DisplayAttribute.Description属性值?


71

我有一个模型类,具有这样的属性:

[Display(Name = "Phone", Description="Hello World!")]
public string Phone1 { get; set; }

在我的视图中显示标签并渲染文本框以供输入非常简单:

@Html.LabelFor(model => model.Organization.Phone1)
@Html.EditorFor(model => model.Organization.Phone1)
@Html.ValidationMessageFor(model => model.Organization.Phone1)

但是,如何渲染Description注释属性的值,即“ Hello World!”?


Answers:


84

我最终得到了这样的帮手:

using System;
using System.Linq.Expressions;
using System.Web.Mvc;

public static class MvcHtmlHelpers
{
    public static MvcHtmlString DescriptionFor<TModel, TValue>(this HtmlHelper<TModel> self, Expression<Func<TModel, TValue>> expression)
    {
        var metadata = ModelMetadata.FromLambdaExpression(expression, self.ViewData);
        var description = metadata.Description;

        return MvcHtmlString.Create(string.Format(@"<span>{0}</span>", description));
    }
}

感谢那些引导我朝着正确方向前进的人。:)


4
为了消除多余的跨度,我将返回值更改为 return string.IsNullOrWhiteSpace(description) ? MvcHtmlString.Empty : MvcHtmlString.Create(string.Format(@"<span>{0}</span>", description));
ScottCate 2013年

4
从您的示例开始,我将进一步扩展此功能,并具有覆盖span标记和(可选)添加cssClass的功能。gist.github.com/4469809
ScottCate 2013年

1
@ScottCate,cssClass您可以采用htmlAttributes参数,而不是采用参数,就像在中所做的那样EditorFor
弗雷德

如果您需要DescriptionAttribute代替DisplayAttribute请参阅我的其他信息
Christian Gollhardt

2
我不知道为什么如果我们必须编写扩展名只是为了吐出它们,“说明”和关于该问题的提示作为该属性的属性存在?
Scott Fraley


23

我将使用已接受的答案,但不适用于ASP.NET Core 1/2(又名MVC 6),因为它ModelMetadata.FromLambdaExpression已不存在且已移至ExpressionMetadataProvider(用法也有所更改)。

这是可以与ASP.NET Core 1.1和2一起使用的更新的扩展方法:

using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;

public static class HtmlExtensions
{
    public static IHtmlContent DescriptionFor<TModel, TValue>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
    {
        if (html == null) throw new ArgumentNullException(nameof(html));
        if (expression == null) throw new ArgumentNullException(nameof(expression));

        var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, html.ViewData, html.MetadataProvider);
        if (modelExplorer == null) throw new InvalidOperationException($"Failed to get model explorer for {ExpressionHelper.GetExpressionText(expression)}");

        return new HtmlString(modelExplorer.Metadata.Description);
    }
}

ASP.NET Core 1

对于ASP.NET Core 1,可以使用相同的代码,但是您需要使用不同的名称空间usings

using System;
using System.Linq.Expressions;
using Microsoft.AspNet.Html.Abstractions;
using Microsoft.AspNet.Mvc.ViewFeatures;

用法

@Html.DescriptionFor(model => model.Phone1)

因此,我要使用的ASP.NET Core 1.1的命名空间如下:Microsoft.AspNetCore.Html Microsoft.AspNetCore.Mvc.ViewFeatures.Internal Microsoft.AspNetCore.Mvc.Rendering
Serge

如何为“ ShortName”属性制定解决方案?没有这样的事情modelExplorer.Metadata.ShortName
Serge

如果是IEnumerable

任何人都无法使用Microsoft.AspNetCore.Mvc.ViewFeatures.Internal名称空间?尽管安装了“ Microsoft.AspNetCore.Mvc.ViewFeatures”程序包,但该程序仍显示为红色。我使用的是.NET Core 3.0
Ciaran Gallagher

1
原来,这个库是不与核心ASP.NET 3.0任何更长的时间,它必须由不同类型的通过依赖注入检索兼容:github.com/dotnet/aspnetcore/issues/...
夏兰加拉格尔

19

在ASP.NET MVC Core中,您可以使用新的Tag Helpers,这会使您的HTML看起来像... HTML :)

像这样:

<div class="form-group row">
    <label asp-for="Name" class="col-md-2 form-control-label"></label>
    <div class="col-md-10">
        <input asp-for="Name" class="form-control" aria-describedby="Name-description" />
        <span asp-description-for="Name" class="form-text text-muted" />
        <span asp-validation-for="Name" class="text-danger" />
    </div>
</div>

注意1:您可以aria-describedby在输入元素中使用属性,因为ID将在带有asp-description-for属性的span元素中自动创建。

注2:在引导4类form-texttext-muted替换V3help-block块级帮助文本类。

为了使这种魔术发生,您只需要创建一个新的Tag Helper:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;

/// <summary>
/// <see cref="ITagHelper"/> implementation targeting &lt;span&gt; elements with an <c>asp-description-for</c> attribute.
/// Adds an <c>id</c> attribute and sets the content of the &lt;span&gt; with the Description property from the model data annotation DisplayAttribute.
/// </summary>
[HtmlTargetElement("span", Attributes = DescriptionForAttributeName)]
public class SpanDescriptionTagHelper : TagHelper
{
    private const string DescriptionForAttributeName = "asp-description-for";

    /// <summary>
    /// Creates a new <see cref="SpanDescriptionTagHelper"/>.
    /// </summary>
    /// <param name="generator">The <see cref="IHtmlGenerator"/>.</param>
    public SpanDescriptionTagHelper(IHtmlGenerator generator)
    {
        Generator = generator;
    }

    /// <inheritdoc />
    public override int Order
    {
        get
        {
            return -1000;
        }
    }

    [HtmlAttributeNotBound]
    [ViewContext]
    public ViewContext ViewContext { get; set; }

    protected IHtmlGenerator Generator { get; }

    /// <summary>
    /// An expression to be evaluated against the current model.
    /// </summary>
    [HtmlAttributeName(DescriptionForAttributeName)]
    public ModelExpression DescriptionFor { get; set; }

    /// <inheritdoc />
    /// <remarks>Does nothing if <see cref="DescriptionFor"/> is <c>null</c>.</remarks>
    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (output == null)
        {
            throw new ArgumentNullException(nameof(output));
        }

        var metadata = DescriptionFor.Metadata;

        if (metadata == null)
        {
            throw new InvalidOperationException(string.Format("No provided metadata ({0})", DescriptionForAttributeName));
        }

        output.Attributes.SetAttribute("id", metadata.PropertyName + "-description");

        if( !string.IsNullOrWhiteSpace( metadata.Description))
        {
            output.Content.SetContent(metadata.Description);
            output.TagMode = TagMode.StartTagAndEndTag;
        }
    }
}

并使您的标签助手对我们所有的Razor视图都可用。将addTagHelper指令添加到Views/_ViewImports.cshtml文件中:

@addTagHelper "*, YourAssemblyName"

注意1:替换YourAssemblyName为项目的程序集名称。

注意2:对于您的所有标记助手,您只需执行一次即可!

有关标签帮助程序的更多信息,请访问:https : //docs.asp.net/zh/latest/mvc/views/tag-helpers/intro.html

而已!享受新的Tag Helpers的乐趣!


问题不是关于ASP.NET MVC核心的(在提出问题时它甚至不存在)

11
嗨,斯蒂芬!这就是为什么我从“ In ASP.NET MVC Core ...”开始。这个问题对于两个世界都是完全相同的,尽管这是一个古老的问题,但我相信像我一样,人们今天也在为这个问题而绊脚石!我今天早些时候一直在这里寻找答案,经过几个小时的研究,我决定分享我对梨的结论。希望有人觉得有用!
Filipe Carneiro

然后,如果您认为有用,请创建自己的问题和带有asp.net-core-mvc标签的自我回答。

2
对我最好的解决方案。由于HtmlHelpers可以被认为是一种过时的方法,过时的TagHelpers是处理此问题的首选方法。这个答案值得更多的赞扬!
亚历山大·克里斯托夫

6

如果有人想知道如何使用接受的答案

1-在您的解决方案资源管理器中>添加新文件夹>将其命名为“ Helpers”,例如
2-添加一个新类,将其命名为“ CustomHtmlHelpers”,例如
3-粘贴代码:

public static class MvcHtmlHelpers
{
    public static string DescriptionFor<TModel, TValue>(this HtmlHelper<TModel> self, Expression<Func<TModel, TValue>> expression)
    {
        var metadata = ModelMetadata.FromLambdaExpression(expression, self.ViewData);
        var description = metadata.Description;

        return string.IsNullOrWhiteSpace(description) ? "" : description;
    }
}

4-在您使用它的模型或viewModel中:

[Display(Name = "User Name", Description = "Enter your User Name")]
public string FullName { get; set; }

5-在Razor视图中的@model之后,键入以下行

@using YOUR_PROJECT.Helpers 

6-显示如下描述:

@Html.DescriptionFor(m => m.FullName) 

7-您可能希望使用描述在输入占位符中显示文本:

@Html.DisplayNameFor(m => m.FullName)
@Html.TextBoxFor(m => m.FullName, new { @class = "form-control", placeholder = Html.DescriptionFor(m => m.FullName) })

谢谢


3
var attrib = (DisplayAttribute)Attribute.GetCustomAttribute(
             member, typeof(DisplayAttribute));
var desc = attrib == null ? "" : attrib.GetDescription()

作品。我需要使用DescriptionAttribute代替DisplayAttribute ..
vvvv4d

1
@ vvvv4d然后...这样做吗?请DescriptionAttribute改用,然后访问.Description最终值或.DescriptionValue原始字符串
Marc Gravell


2
@ViewData.ModelMetadata.Properties
   .Where(m => m.PropertyName == "Phone1").FirstOrDefault().Description

因此,如果您使用的是引导程序,

<div class="form-group col-sm-6">
   @Html.LabelFor(m => m.Organization.Phone1)
   @Html.EditorFor(m => m.Organization.Phone1)
   <p class="help-block">
      @ViewData.ModelMetadata.Properties
         .Where(m => m.PropertyName == "DayCount").FirstOrDefault().Description
   </p>
</div>

@ ViewData.ModelMetadata.Properties.FirstOrDefault(m => m.PropertyName == nameof(ServiceRequestPageViewModel.AcceptTerms))?. DisplayName仅是带有null检查和nameof的更新,以避免运行时错误
EthR

1

...并且如果您希望将描述作为表单标签中的工具提示,请添加如下的Tag Helper:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;

/// <summary>
/// <see cref="ITagHelper"/> implementation targeting &lt;label&gt; elements with an <c>asp-for</c> attribute.
/// Adds a <c>title</c> attribute to the &lt;label&gt; with the Description property from the model data annotation DisplayAttribute.
/// </summary>
[HtmlTargetElement("label", Attributes = ForAttributeName)]
public class LabelTitleTagHelper : TagHelper
{
    private const string ForAttributeName = "asp-for";

    /// <summary>
    /// Creates a new <see cref="LabelTitleTagHelper"/>.
    /// </summary>
    /// <param name="generator">The <see cref="IHtmlGenerator"/>.</param>
    public LabelTitleTagHelper(IHtmlGenerator generator)
    {
        Generator = generator;
    }

    /// <inheritdoc />
    public override int Order
    {
        get
        {
            return -1000;
        }
    }

    [HtmlAttributeNotBound]
    [ViewContext]
    public ViewContext ViewContext { get; set; }

    protected IHtmlGenerator Generator { get; }

    /// <summary>
    /// An expression to be evaluated against the current model.
    /// </summary>
    [HtmlAttributeName(ForAttributeName)]
    public ModelExpression TitleFor { get; set; }

    /// <inheritdoc />
    /// <remarks>Does nothing if <see cref="TitleFor"/> is <c>null</c>.</remarks>
    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (output == null)
        {
            throw new ArgumentNullException(nameof(output));
        }

        var metadata = TitleFor.Metadata;

        if (metadata == null)
        {
            throw new InvalidOperationException(string.Format("No provided metadata ({0})", ForAttributeName));
        }

        if (!string.IsNullOrWhiteSpace(metadata.Description))
            output.Attributes.SetAttribute("title", metadata.Description);
    }
}

这将title使用Description模型数据注释中的属性创建一个新属性DisplayAttribute

美丽的部分是您不需要触摸生成的支架视图!因为此标记助手针对的是已经存在asp-forlabel元素的属性!


我喜欢这种方式如何利用现有的as-for,省去了我修改网站上每个标签的麻烦。真好!
Mike

0

除了Jakob Gade'的好答案之外:

如果您需要支持DescriptionAttribute而不是a DisplayAttribute,那么如果我们重写MetadataProvider ,那么他的出色解决方案仍然有效:

public class ExtendedModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
    protected override ModelMetadata CreateMetadata(IEnumerable<System.Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
        //Possible Multiple Enumerations on IEnumerable fix
        var attributeList = attributes as IList<System.Attribute> ?? attributes.ToList();

        //Default behavior
        var data = base.CreateMetadata(attributeList, containerType, modelAccessor, modelType, propertyName);

        //Bind DescriptionAttribute
        var description = attributeList.SingleOrDefault(a => typeof(DescriptionAttribute) == a.GetType());
        if (description != null)
        {
            data.Description = ((DescriptionAttribute)description).Description;
        }

        return data;
    }
}

这需要在以下Application_Start方法中注册Global.asax.cs

ModelMetadataProviders.Current = new ExtendedModelMetadataProvider();

-1

HANDL的答案,已针对ASP.NET Core 2.0更新

using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;

public static class HtmlExtensions
{
    public static IHtmlContent DescriptionFor<TModel, TValue>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
    {
        if (html == null) throw new ArgumentNullException(nameof(html));
        if (expression == null) throw new ArgumentNullException(nameof(expression));

        var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, html.ViewData, html.MetadataProvider);
        if (modelExplorer == null) throw new InvalidOperationException($"Failed to get model explorer for {ExpressionHelper.GetExpressionText(expression)}");

        return new HtmlString(modelExplorer.Metadata.Description);
    }
}

这个答案已经涵盖了。命名空间更新应在原始答案中完成,或者通过添加注释而不是发布重复项来完成。
huysentruitw

-2

您总是可以像这样创建自己的自定义扩展名:

    public static MvcHtmlString ToolTipLabel (string resourceKey, string text, bool isRequired, string labelFor = "", string labelId = "",string className="")
    {
        string tooltip = string.Empty;

        StringBuilder sb = new StringBuilder();

        if (!string.IsNullOrEmpty(resourceKey))
        {
            var resources = GetAllResourceValues();

            if (resources.ContainsKey(resourceKey))
            {
                tooltip = resources[resourceKey].Value;
            }
        }

        sb.Append("<label");

        if (!string.IsNullOrEmpty(labelFor))
        {
            sb.AppendFormat(" for=\"{0}\"", labelFor);
        }

        if (!string.IsNullOrEmpty(labelId))
        {
            sb.AppendFormat(" Id=\"{0}\"", labelId);
        }

        if (!string.IsNullOrEmpty(className))
        {
            sb.AppendFormat(" class=\"{0}\"", className);
        }

        if (!string.IsNullOrEmpty(tooltip))
        {

            sb.AppendFormat(" data-toggle='tooltip' data-placement='auto left' title=\"{0}\"",tooltip);

        }
        if (isRequired)
        {
            sb.AppendFormat("><em class='required'>*</em> {0} </label></br>", text);
        }
        else
        {
            sb.AppendFormat(">{0}</label></br>", text);
        }
        return MvcHtmlString.Create(sb.ToString());
    }

并可以这样查看它:

@HtmlExtension.ToolTipLabel(" "," ",true," "," "," ")

问题是如何从视图的显示属性中获取描述,我看不出这段代码有什么帮助。
huysentruitw
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.