2013-04-12 11 views
5

Biorąc pod uwagę model widok tak:Html.Labelfor korzystania z obiektu nie DisplayName własności

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

Jeśli używam Html.LabelFor tak:

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

chciałbym uzyskać moc tak:

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

Tym, czego naprawdę chcę, jest wygenerowana etykieta, aby użyć atrybutu DisplayName zastosowanego do obiekt E.G.

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

o mocy:

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

Rozumiem, że metoda Html.LabelFor bierze wyrażenie oczekuje nieruchomości i będzie wyglądał dla atrybutu DisplayName na tej nieruchomości, a nie samego obiektu.

I stworzyli metodę pomocnika HTML, aby osiągnąć to, co chcę, który wygląda tak:

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

Zamiast podejmować wyrażenie, mój pomocnik bierze Func<TModel, object> która zwraca obiekt, który chcę, aby sprawdzić, czy Atrybut DisplayName.

Pierwszym problemem było to, gdy próbowałem wywołać tę metodę w maszynce tak:

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

pojawia się następujący błąd:

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

więc wywołujemy metodę tak zamiast :

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

ale nic nie zostanie naprawione. Jeśli użyję debuggera do przejścia przez moją metodę pomocnika, tag etykiety jest generowany, ale nic nie jest wyświetlane, gdy moja strona jest renderowana.

Więc moje pytania to:

  • Jak mogę rozwiązać ten problem, aby wygenerować etykietę moim zdaniem?
  • Co muszę zrobić, aby można było wywnioskować ogólny parametr?
  • Czy istnieje sposób, aby napisać helper HTML, aby zrobić to samo, ale za pomocą wyrażenia? Nie mam doświadczenia w korzystaniu z wyrażeń, więc nie wiem od czego zacząć.

Aktualizacja

pomyślałem, że pisać ostateczny kod jak zrobiłem kilka drobnych zmian. Przede wszystkim rzuciłem okiem na pomocników w kodzie źródłowym MVC i postanowiłem podzielić tę metodę na trzy osobne metody zgodnie z podanymi przykładami. Usunąłem też wszystkie rzeczy, które naprawdę potrzebowałem, aby wygenerować tekst, który miał zostać wtryśnięty pomiędzy znacznikami <legend></legend>. Ostateczny kod znajduje się poniżej. Jeszcze raz dzięki Sylonowi za pomoc w tym.

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

Odpowiedz

2

Właśnie stworzyliśmy własny html pomocnika na etykiecie, że robi to, co chcesz:

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

Mój przykład model:

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

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

Widok:

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

Dziękuję, działa pięknie. Wiedziałem, że muszę pobrać rzeczywisty obiekt z wyrażenia, ale nie byłem pewien jak to zrobić, więc ten wiersz 'expression.Compile(). Invoke (html.ViewData.Model)' jest szczególnie pomocny. –

Powiązane problemy