2013-04-12 8 views
5

の表示名を使用します。Html.Labelforこのようなビューモデル指定されたオブジェクトではないプロパティ

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ソースコードのヘルパーを見て、提供されたサンプルに合わせてメソッドを3つの別々のメソッドに分割することに決めました。私が実際に必要としていたのは、<legend></legend>タグの間に挿入されるテキストを生成することだったので、TagBuilderのものもすべて削除しました。最終的なコードは以下の通りです。もう一度、これで私を助けてくれたSylonに感謝します。

Htmlのヘルパー::

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ヘルパーを作成し

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)'は特に役に立ちます。 –

関連する問題