web-dev-qa-db-ja.com

カスケードセクションを備えたRazorネストレイアウト

Razorをビューエンジンとして使用するMVC3サイトがあります。自分のサイトをスキン可能にしたい。可能なスキンのほとんどは、共有マスターレイアウトから派生できるほど十分に類似しています。

したがって、私はこの設計を検討しています:

Planned view diagram

ただし、一番下のレイヤー__Common.cshtml_でRenderSectionを呼び出し、一番上のレイヤー_Detail.cshtml_で定義されているセクションをレンダリングできるようにしたいと思います。これは機能しません:RenderSectionは明らかに、次のレイヤーで定義されたセクションのみをレンダリングします。

もちろん、各スキンの各セクションを定義できます。たとえば、Detailで定義されたセクションに対して__Common_がRenderSection("hd")を呼び出す必要がある場合、これを各__Skin_に配置するだけで機能します。

_@section hd {
    @RenderSection("hd")
}
_

これにより、コードの重複が発生し(各スキンには同じセクションが必要になるため)、通常は煩雑に感じます。私はまだRazorに慣れていないので、明らかな何かを見逃しているようです。

デバッグ時には、WebViewPage.SectionWritersStackで定義済みセクションの完全なリストを見ることができます。あきらめる前にリスト全体を調べるようにRenderSectionに指示できれば、必要なセクションが見つかります。残念ながら、SectionWritersStackは非公開です。

あるいは、レイアウトページの階層にアクセスして、それぞれ異なるコンテキストでRenderSectionの実行を試みることができれば、必要なセクションを見つけることができます。私はおそらく何かを見逃していますが、これを行う方法がわかりません。

既に説明した方法以外に、この目標を達成する方法はありますか?

77
Chris Nielsen

これは、実際には、パブリックAPIを使用して(セクションの再定義アプローチを使用する以外は)実際には不可能です。プライベートリフレクションを使用すると多少の幸運があるかもしれませんが、それはもちろん脆弱なアプローチです。 Razorの次のバージョンでは、このシナリオを簡単にすることを検討します。

それまでの間、このテーマについて私が書いたいくつかのブログ投稿があります。

35
marcind
@helper ForwardSection( string section )
{
   if (IsSectionDefined(section))
   {
       DefineSection(section, () => Write(RenderSection(section)));
   }
}

これは仕事をしますか?

17
Randy

MVC 3でこれが可能かどうかはわかりませんが、MVC 5では次のトリックを使用してこれを正常に行うことができます:

~/Views/Shared/_Common.cshtmlに、次のような一般的なHTMLコードを記述します。

<!DOCTYPE html>
<html lang="fa">
<head>
    <title>Skinnable - @ViewBag.Title</title>
</head>
<body>
@RenderBody()
</body>
</html>

~/Views/_ViewStart.cshtml ::

@{
    Layout = "~/Views/Shared/_Common.cshtml";
}

あとは、すべてのスキンのLayoutとして_Common.cshtmlを使用するだけです。たとえば、~/Views/Shared/Skin1.cshtml

@{
    Layout = "~/Views/Shared/_Common.cshtml";
}

<p>Something specific to Skin1</p>

@RenderBody()

これで、基準に基づいてスキンをコントローラーまたはビューのレイアウトとして設定できます。例えば:

    public ActionResult Index()
    {
        //....
        if (user.SelectedSkin == Skins.Skin1)
            return View("ViewName", "Skin1", model);
    }

上記のコードを実行する場合、Skin1.cshtml_Common.cshtmlの両方のコンテンツを含むHTMLページを取得する必要があります

つまり、(スキン)レイアウトページのレイアウトを設定します。

4
Alireza Noori

これが役立つかどうかはわかりませんが、パーシャル内からセクションを「バブルアップ」するのに役立つ拡張メソッドをいくつか作成しました。これはネストされたレイアウトでも機能するはずです。

Razor View Engineを使用した部分ビューASP.NET MVC 3から特定のセクションにコンテンツを挿入

子レイアウト/ビュー/部分で宣言

@using (Html.Delayed()) {
    <b>show me multiple times, @Model.Whatever</b>
}

任意の親でレンダリング

@Html.RenderDelayed();

繰り返しビューで宣言されていても1つの遅延ブロックのみをレンダリングする、特定の遅延ブロックをレンダリングするなど、その他のユースケースについては、回答リンクを参照してください。

1
drzaus