Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC Attributes on abstract properties

I have an abstract Model

public abstract class Treasure {
    public abstract int Value { get; }
    public abstract string Label { get; }
}

and an implementation

public class Coins : Treasure {
    [DisplayName("Coin Value")]
    public override int Value {
        get { ... }
    }

    [DisplayName("Coins")]
    public override string Label {
        get { ... }
    }

My coins object does not show "Coins" as its label in my view when I use Html.LabelFor on it, it shows "Label". If I move the DisplayName attribute into Treasure, it works... but I need to be able to change the label for different implementations of the Treasure class. Is this possible?

like image 726
Eric B Avatar asked Oct 03 '22 19:10

Eric B


1 Answers

I was able to get this to work fine if the model in the view is Coins. However if the model is Treasure, it fails. Why? Because when the Html Helper renders the Html it only looks at the model type that is specified in the view, not the object type of the actual object. When it goes to get the attribute it will only get the attribute for Treasure and not Coins. I think you would have to write your own Html helper for this.

internal static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string labelText, IDictionary<string, object> htmlAttributes, ModelMetadataProvider metadataProvider)
{
  return LabelExtensions.LabelHelper((HtmlHelper) html, ModelMetadata.FromLambdaExpression<TModel, TValue>(expression, html.ViewData, metadataProvider), ExpressionHelper.GetExpressionText((LambdaExpression) expression), labelText, htmlAttributes);
}

Under the covers, MVC uses ModelMetadata.FromLambdaExpression<TModel, TValue> to find the "DisplayName" and when it fails to find one on the type passed in... it returns the PropertyName.

internal static MvcHtmlString LabelHelper(HtmlHelper html, ModelMetadata metadata, string htmlFieldName, string labelText = null, IDictionary<string, object> htmlAttributes = null)
{
  string str = labelText;
  if (str == null)
  {
    string displayName = metadata.DisplayName;
    if (displayName == null)
    {
      string propertyName = metadata.PropertyName;
      if (propertyName == null)
        str = Enumerable.Last<string>((IEnumerable<string>) htmlFieldName.Split(new char[1]
        {
          '.'
        }));
      else
        str = propertyName;
    }
    else
      str = displayName;
  }
  string innerText = str;
  if (string.IsNullOrEmpty(innerText))
    return MvcHtmlString.Empty;
  TagBuilder tagBuilder1 = new TagBuilder("label");
  tagBuilder1.Attributes.Add("for", TagBuilder.CreateSanitizedId(html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName)));
  tagBuilder1.SetInnerText(innerText);
  TagBuilder tagBuilder2 = tagBuilder1;
  bool flag = true;
  IDictionary<string, object> attributes = htmlAttributes;
  int num = flag ? 1 : 0;
  tagBuilder2.MergeAttributes<string, object>(attributes, num != 0);
  return TagBuilderExtensions.ToMvcHtmlString(tagBuilder1, TagRenderMode.Normal);
}
like image 177
CrazyDart Avatar answered Oct 07 '22 18:10

CrazyDart