カスタム Html ヘルパーを Razor に適合させる (HtmlTextWriter を使用するため void を返します)

StackOverflow https://stackoverflow.com/questions/4217611

  •  26-09-2019
  •  | 
  •  

質問

問題

WebFormViewEngine ビュー用に書かれた非常に気の利いたメニュー HTML ヘルパーがあります。このエンジンにより、ヘルパーは void を返しても、次のものを使用できるようになります。

@Html.Theseus

これは、出力ストリームに直接レンダリングされる HtmlTextWriter を使用してメニューをレンダリングできるため、ヘルパーにとっては非常に便利です。

ただし、Razor ビューでは、Html ヘルパーは出力に追加される値 (通常は MvcHtmlString) を返すことが期待されます。小さな違いは大きな結果をもたらします。

GvS が私に指摘したように、これを回避する方法があります (「 ASP.NET MVC 2 から MVC 3:Razor のカスタム HTML ヘルパー) 次のように:

ヘルパーが void を返した場合は、次の操作を実行します。

@{Html.Theseus;}

(基本的に、ビューにレンダリングするのではなく、メソッドを呼び出すだけです)。

これはまだきちんとしていますが、@Html.Theseus とまったく同じではありません。それで...

私のコードは複雑ですが、非常にうまく機能するので、大規模な編集、つまり HtmlTextWriter を別のライターに置き換えるのは嫌いです。コードの一部は次のようになります。

writer.AddAttribute(HtmlTextWriterAttribute.Href, n.Url);
writer.AddAttribute(HtmlTextWriterAttribute.Title, n.Description);
writer.RenderBeginTag(HtmlTextWriterTag.A);
writer.WriteEncodedText(n.Title);
writer.RenderEndTag();

// Recursion, if any
// Snip off the recursion at this level if specified by depth
// Use a negative value for depth if you want to render the entire sitemap from the starting node

    if ((currentDepth < depth) || (depth < 0))
    {
         if (hasChildNodes)
         {
              // Recursive building starts here

              // Open new ul tag for the child nodes 
              // "<ul class='ChildNodesContainer {0} Level{1}'>"; 
              writer.AddAttribute(HtmlTextWriterAttribute.Class, "Level" + currentDepth.ToString());
              writer.RenderBeginTag(HtmlTextWriterTag.Ul);

              // BuildMenuLevel calls itself here to 
              // recursively traverse the sitemap hierarchy, 
              // building the menu as I go.
              // Note: this is where I increase the currentDepth variable!
               BuildChildMenu(currentDepth + 1, depth, n, writer);

              // Close ul tag for the child nodes
              writer.RenderEndTag();
          }
    }

TagBuilders で書き直すのは面白くありません。現状では、私の 4guysfromrolla の記事で説明されている「インクリメンタル ナビゲーション」を含む、あらゆる種類のメニューが表示されます。ASP.NET を使用したインクリメンタル ナビゲーションの実装

オプション:

空の MvcHtmlString を返すこともできると思いますが、それはほとんどハッキングの定義です...

唯一の代替案は、日没に向かい、TagBuilder を使用してヘルパーを書き換えて各タグを構築し、それを StringBuilder に追加して、次のタグなどを構築してから、StringBuilder インスタンスを使用して MvcHtmlString を作成することです。本当に醜い、次のようなことができない限り...

質問:

次の方法はありますか?

ストリームへの HtmlTextWriter レンダリングを停止し、代わりにプロセスの最後に MvcHtmlString (または HtmlString) を作成するために使用する StringBuilder のように使用しますか?

書いていても、ありそうもないように思えますが...

追伸:

HtmlTextWriter の優れた点は、TagBuilder のようにタグを 1 つずつ構築するのではなく、大量のタグを構築できることです。

役に立ちましたか?

解決

あなたが受け取った回答とは反対に、 その他の質問 Razor では、HtmlString を返す必要はありません。現時点でのコードの問題は、次のように書いていることです。 直接 応答ストリームに送信されます。Razor は物事を裏返しに実行します。つまり、応答の順序が台無しになる可能性があります (「 同様の質問).

したがって、あなたの場合、おそらくこれを行うことができます(ただし、私はそれをテストしていません):

public static void Theseus(this HtmlHelper html)
{
    var writer = new HtmlTextWriter(html.ViewContext.Writer);
    ...
}

編集 (コメントに対処するためにフォローアップします):

HTML ヘルパーは、HtmlString を直接返すか、void してコンテキスト ライターに書き込むことができます。たとえば、両方とも Html.Partial そして Html.RenderPartial Razor では正常に動作します。あなたが混乱しているのは、 構文 一方のバージョンを呼び出す必要があり、もう一方のバージョンを呼び出す必要はありません。

たとえば、Aspx ビューを考えてみましょう。

<%: Html.Partial("Name") %>
<% Html.RenderPartial("Name") %>

各メソッドを異なる方法で呼び出します。物事をひっくり返すと、物事はうまくいきません。Razor でも同様に:

@Html.Partial("Name")
@{ Html.RenderPartial("Name"); }

たまたまですが、Razor では void ヘルパーを使用するための構文が Aspx に比べてはるかに冗長になっています。ただし、どちらも問題なく動作します。「問題は、HTML ヘルパーが void を返せないことにある」ということで別の意味であった場合を除きます。

ちなみに、本当にこの構文を使用してヘルパーを呼び出したい場合は、次のようにします。 @Html.Theseus() これを行うことができます:

public static IHtmlString Theseus(this HtmlHelper html)
{
    var writer = new HtmlTextWriter(html.ViewContext.Writer);
    ...
    return new HtmlString("");
}

しかし、それはちょっとしたハックです。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top