5

次のようなビュー モデルが与えられた場合:

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

私がこのように使用する場合Html.LabelFor

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

次のような出力が得られます。

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

私が実際に望んでいるのは、生成されたラベルがDisplayNameオブジェクトEGに適用された属性を使用することです

[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 つの個別のメソッドに分割することにしました。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;
    }
}
4

1 に答える 1