2013-04-12 16 views
5

的給定視圖模型是這樣的:Html.Labelfor使用DisplayName的對象不是財產

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

如果我使用Html.LabelFor這樣的:

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

我會得到一個輸出這樣的:

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

我真正想要的是,雖然是生成的標籤使用DisplayName屬性應用於對象E.G.

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

用的輸出:

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

據我所知,Html.LabelFor方法需要一個期望的性質的表達式,它會檢查是否有與該屬性而不是對象本身的屬性DisplayName

我已經創建了一個HTML輔助的方法來達到我想要的東西,看起來像這樣:

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

而不是採取一種表達,幫助我需要一個Func<TModel, object>返回,我要檢查的對象DisplayName屬性。

我是當我試圖調用此方法在剃刀這樣的第一個問題:

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

我收到以下錯誤:

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

因此,我調用該方法是這樣而不是:

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

但沒有任何東西被渲染。如果我使用調試器來遍歷我的幫助器方法,那麼會生成標籤標籤,但是當我的頁面呈現時,不會顯示任何內容。

所以我的問題是:

  • 我該如何解決這個問題,生成的標籤我的看法?
  • 我需要做些什麼才能推斷泛型參數?
  • 有什麼辦法可以編寫Html幫助器來做同樣的事情,但使用表達式嗎?我沒有使用表達式的經驗,所以不知道從哪裏開始。

更新

我想我會發布最終的代碼,因爲我做了一些小的改動。首先,我查看了MVC源代碼中的助手,並決定將該方法拆分爲三個獨立的方法,與提供的示例一致。我也刪除了所有TagBuilder的東西,因爲我真正需要的是生成要在<legend></legend>標籤之間注入的文本。最終的代碼如下。再次感謝Sylon幫助我解決這個問題。

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

回答

2

我剛剛創建自定義的HTML幫手的標籤,你想要做什麼:

的HTML幫助:

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

我的示例模型:

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

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

查看:

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

謝謝你,精美的作品。我知道我必須從表達式中得到實際的對象,但不知道如何去做,所以這行'expression.Compile()。Invoke(html.ViewData.Model)'特別有用。 –