tutorial net mvc español curso asp c# asp.net asp.net-mvc

c# - net - ¿Cómo se muestra el valor del atributo DisplayAttribute.Description?



asp.net mvc tutorial español (11)

Tengo una clase modelo, con una propiedad como esta:

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

Mostrar una etiqueta y renderizar un cuadro de texto para la entrada en mi vista es bastante fácil:

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

Pero ¿cómo puedo representar el valor del atributo de anotación Descripción, es decir, "Hola mundo!"


... y si prefiere tener la descripción como información sobre herramientas en la etiqueta del formulario, agregue un Tag Helper como este:

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); } }

Eso creará un nuevo atributo de title con la propiedad Description de la anotación de datos del modelo DisplayAttribute .

¡La parte más bonita es que no necesitas tocar tus vistas andamiadas generadas! ¡Porque Tag Helper está apuntando al atributo asp-for del elemento de label que ya está allí!


Además de la gran respuesta de Jakob Gade''a:

Si necesita DisplayAttribute un DisplayAttribute DescriptionAttribute lugar de un DisplayAttribute de DisplayAttribute , su gran solución aún funciona si DisplayAttribute metadatos:

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; } }

Esto necesita ser registrado en el Método Application_Start en Global.asax.cs :

ModelMetadataProviders.Current = new ExtendedModelMetadataProvider();


En ASP.NET MVC Core puede usar los nuevos Tag Helpers, que hacen que su HTML se vea como ... HTML :)

Me gusta esto:

<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>

Nota 1: Puede usar el atributo aria-describedby en el elemento de entrada ya que ese id. Se creará automáticamente en el elemento span con el atributo asp-description-for .

Nota 2: en Bootstrap 4, las clases form-text y text-muted reemplazan la clase v3 help-block por texto de ayuda de nivel de bloque.

Para que esta magia suceda, solo necesitas crear un nuevo 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; } } }

Y haga que sus Tag Helpers estén disponibles para todas nuestras vistas Razor. Agregue la directiva Views/_ViewImports.cshtml archivo Views/_ViewImports.cshtml :

@addTagHelper "*, YourAssemblyName"

Nota 1: YourAssemblyName con el nombre de conjunto de su proyecto.

Nota 2: ¡Solo necesita hacer esto una vez, para todos sus Tag Helpers!

Más información sobre Tag Helpers aquí: https://docs.asp.net/en/latest/mvc/views/tag-helpers/intro.html

¡Eso es! ¡Diviértete con los nuevos Tag Helpers!


Estaba a punto de utilizar la respuesta aceptada , pero no funcionó para ASP.NET Core 1/2 (también conocido como MVC 6) porque ModelMetadata.FromLambdaExpression ya no existe y se ha movido a ExpressionMetadataProvider (también se ha modificado ligeramente el uso).

Este es un método de extensión actualizado que puede usar con ASP.NET Core 1.1 y 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

Para ASP.NET Core 1, el mismo código funciona, pero necesitará diferentes usings espacio de nombres:

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

Uso

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


La respuesta de HANDL, actualizada para 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); } }


Siempre puedes crear tu propia extensión personalizada como esta:

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()); }

y puede verlo de esta manera:

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


Solo por inspección (es decir, no lo he probado), pero:

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


Tendría que escribir un ayudante personalizado que refleje en su modelo para dar el valor del atributo Descripción.


Terminé con un ayudante como este:

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)); } }

Gracias a quienes me guiaron en la dirección correcta. :)


Usando la técnica de este artículo sobre cómo mostrar sugerencias visuales para los campos en su formulario , puede acceder al valor a través de lo siguiente:

@Html.TextBoxFor( model => model.Email , new { title = ModelMetadata.FromLambdaExpression<RegisterModel , string>( model => model.Email , ViewData ).Description } )


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

Entonces, si usabas bootstrap, algo como

<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>