2013-04-12 6 views
5

eine Ansicht Modell wie folgt gegeben:Html.Labelfor verwenden Display des Objekts nicht Eigentum

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

Wenn ich Html.LabelFor wie folgt verwenden:

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

ich eine Ausgabe wie diese bekommen würde:

<label for="Model_ChildViewModel">ChildViewModel</label> 

Was ich eigentlich will, ist, dass das generierte Etikett das Attribut DisplayName verwendet, auf das angewendet wird das Objekt E.G.

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

mit einer Leistung von:

<label for="Model_ChildViewModel">My Custom Label</label> 

Ich verstehe, dass die Html.LabelFor Methode einen Ausdruck kommt, das eine Eigenschaft erwartet und es wird auf dieser Eigenschaft für das DisplayName Attribut suchen, anstatt das Objekt selbst.

Ich habe eine HTML-Helper-Methode zu erreichen, was geschaffen Ich möchte, dass dies wie folgt aussieht:

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

Statt einen Ausdruck zu nehmen, mein Helfer ein Func<TModel, object> nimmt, die das Objekt zurückgibt, die ich für die überprüfen möchten DisplayName Attribut.

Das erste Problem, das ich hatte, war, als ich so diese Methode in Rasierapparat zu rufen versucht:

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

bekomme ich folgende Fehlermeldung:

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

wie dies So nenne ich die Methode statt :

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

aber nichts wird gerendert. Wenn ich den Debugger verwende, um meine Hilfsmethode zu durchlaufen, wird das Label-Tag generiert, aber es wird nichts angezeigt, wenn meine Seite gerendert wird.

Also meine Fragen sind:

  • Wie behebe ich das das Etikett meiner Ansicht zu generieren?
  • Was muss ich tun, damit der generische Parameter abgeleitet werden kann?
  • Gibt es eine Möglichkeit, den HTML-Helfer zu schreiben, um dasselbe zu tun, aber einen Ausdruck zu verwenden? Ich habe keine Erfahrung mit Ausdrücke, also weiß nicht, wo ich anfangen soll.

aktualisieren

Ich dachte, ich den endgültigen Code schreiben würde, wie ich ein paar kleinere Änderungen vorgenommen. Zunächst habe ich mir die Helfer im MVC-Quellcode angeschaut und beschlossen, die Methode in drei separate Methoden aufzuteilen. Ich entfernte auch alle TagBuilder Sachen, da alles, was ich wirklich brauchte, war, den Text zu erzeugen, der zwischen den <legend></legend> Tags eingespritzt werden sollte. Der endgültige Code ist unten. Noch einmal, Danke an Sylon, dass er mir dabei geholfen hat.

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

Antwort

2

Ich habe gerade einen benutzerdefinierten HTML-Helfer für das Label, das tut, was Sie wollen:

Html Helper:

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

Mein Beispiel Modell:

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

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

Ausblick:

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

Danke, funktioniert wunderbar. Ich wusste, dass ich das eigentliche Objekt aus dem Ausdruck holen musste, aber ich wusste nicht, wie ich es machen sollte, daher ist diese Zeile 'expression.Compile(). Invoke (html.ViewData.Model)' besonders hilfreich. –

Verwandte Themen