8

私は自分のウェブサイトで頻繁に使用されているが、異なるフィールドで使用されている複雑なコントロールを一般化しようとしています。コントロールの機能は常に同じで、変更されるのは基になるフィールドだけです。

Expression<Func<TModel,TProperty>>さまざまなフィールドを表示する方法を実現するために、パラメーターとしてを受け入れるHTMLHelper拡張機能を作成しようとしています。これには、コントロールでの表示に必要なクラスのプロパティが含まれます。例えば:

景色:

@model Project.Core.Page
@Html.MyHelper(p => new { p.Author.Name, p.Author.Location, p.Author.Age });

これは私が問題を抱えている拡張機能です-ラムダで提供されたパラメーターを繰り返し処理して、それぞれにを提供しTextBoxFor()たり、手動でinput要素を作成してラムダパラメーターのvalueとを入力したりするにはどうすればよいですか?name

psuedoの拡張:

public static MvcHtmlString MyHelper<TModel,TProperty>(
    this HtmlHelper<TModel> helper, 
    Expression<Func<TModel,TProperty>> expression) {
    foreach (var parameter in expression.???) {
        // helper.TextBoxFor(???) 
        // TagBuilder("input").Attributes("name", expression.???)
    }
}

私はこれをずっとずっと見つめているように感じます、そして私はこれを達成することを私が見落としているもっと簡単な方法があると感じています。

どんな助けでも大歓迎です。さらに詳細が必要な場合、または重要なことを見逃した場合は、お知らせください。

4

4 に答える 4

2

作成する式は比較的複雑になります。すべてのプロパティをフェッチしてから、匿名型コンストラクターを呼び出す必要があります。苦痛を伴う可能性のある「逆アセンブル」...それでも試してみたい場合は、空のメソッド実装を残し、デバッガーを調べて式がどのように見えるかを確認することをお勧めします。

少し醜い形式の呼び出しコードに満足している場合は、これを実装する方がはるかに簡単です。

@Html.MyHelper(p => p.Author.Name, p => p.Author.Location, p => p.Author.Age);

あなたはそれをとらせることができます、params Expression<TModel, object>あるいはあなたは異なる数のパラメータで複数のオーバーロードを宣言することができます、例えば

// Overload for one property
MyHelper<TModel, TProperty1>(this ..., Expression<Func<TModel, TProperty1>> func1)

// Overload for two properties
MyHelper<TModel, TProperty1, TProperty2>(this ...,
   Expression<Func<TModel, TProperty1>> func1,
   Expression<Func<TModel, TProperty2>> func2)

于 2012-09-10T19:23:22.640 に答える
2

次のことを想定した場合:

  1. 入力式の結果は射影です(匿名またはその他の方法で新しいオブジェクトを返します)
  2. 射影の要素はすべてMemberExpressionsであり、モデルまたはその子のメソッドへの呼び出しは含まれていません

次に、次のアプローチを使用して、目的を達成できます。

編集:

最初の例では複雑なプロパティを持つオブジェクトを処理できないことに気付いた後、ヘルパーメソッドを使用してプロパティ値にアクセスするようにコードを更新しました。このメソッドは、再帰を使用してプロパティチェーンをトラバースし、適切な値を返します。

public static MvcHtmlString MyHelper<TModel,object>(
    this HtmlHelper<TModel> helper, 
    Expression<Func<TModel,object>> expression) {

        var newExpression = expression.Body as NewExpression;
        TModel model = helper.ViewData.Model;

        foreach (MemberExpression a in newExpression.Arguments) {

            var propertyName = a.Member.Name;
            var propertyValue = GetPropertyValue<TModel>(model, a);

            // Do whatever you need to with the property name and value;

        }

    }

    private static object GetPropertyValue<T>(T instance, MemberExpression me) {

        object target;

        if (me.Expression.NodeType == ExpressionType.Parameter) {
            // If the current MemberExpression is at the root object, set that as the target.            
            target = instance;
        }
        else {                
            target = GetPropertyValue<T>(instance, me.Expression as MemberExpression);
        }

        // Return the value from current MemberExpression against the current target
        return target.GetType().GetProperty(me.Member.Name).GetValue(target, null);

    }

注: IDEでMVC拡張メソッドとしてこれを直接実装しなかったため、構文を少し変更する必要がある場合があります。

于 2012-09-10T19:52:39.237 に答える
1

おそらく、ビルダースタイルのAPIは物事を単純化するかもしれません:

@(Html.MyHelper(p)
    .Add(p => p.Author.Age)
    .Add(p => p.Author.LastName, "Last Name")
    .Build())

これにより、必要に応じてオプションのパラメータを追加できることに注意してください。

コードは次のようになります

public static class Test
{
    public static Helper<TModel> MyHelper<TModel>(this HtmlHelper helper, TModel model)
    {
        return new Helper<TModel>(helper, model);
    }
}

public class Helper<TModel>
{
    private readonly HtmlHelper helper;
    private readonly TModel model;

    public Helper(HtmlHelper helper, TModel model)
    {
        this.helper = helper;
        this.model = model;
    }

    public Helper<TModel> Add<TProperty>(Expression<Func<TModel, TProperty>> expression)
    {
        // TODO
        return this;
    }

    public MvcHtmlString Build()
    {
        return new MvcHtmlString("TODO");
    }
}
于 2012-09-10T19:59:41.343 に答える
1

このことを考慮:

App_Codeフォルダーを作成します

かみそりヘルパーファイルTemplates.cshtmlを配置します。

以下のようになります。

 @helper Print(string myvalue,string special="")
 {
   <pre> <input id="id" type="text" value ="@myvalue" data-val="@special"/> </pre>
 }

このようにして、C#ファイルにHTMLを記述する必要はありません。とても便利です。

Authors.cshtmlは次のようになります。

@model IEnumerable<MvcApplication1.Models.Author>
@{
   ViewBag.Title = "Authors";
}

<h2>Authors</h2>

@foreach(var auth in Model)
{
   @Templates.Print(auth.Name);    
}

books.cshtmlは次のようになります。

@model IEnumerable<MvcApplication1.Models.Book>
@{
    ViewBag.Title = "Books";
 }

<h2>Books</h2>

@foreach(var book in Model)
{
   @Templates.Print(book.Title,book.ISBN);    
}

モデルクラスごとに必要に応じて、すべての特別なプロパティをプラグインします。複雑になりすぎる場合は、動的オブジェクトとエキスパンドオブジェクトを調べてください。モデル/ビューモデルの複雑さによって異なります。

于 2012-09-10T22:58:58.077 に答える