Razor вложенные макеты с каскадными секциями
у меня есть сайт MVC3, использующий Razor в качестве своего движка просмотра. Я хочу, чтобы мой сайт был очищен. Большинство возможных оболочек достаточно похожи, что они могут быть получены из общего главного макета.
поэтому я рассматриваю эту конструкцию:
однако, я хотел бы иметь возможность позвонить RenderSection
в нижнем слое, _Common.cshtml
, и пусть он отобразит раздел, который определен в верхнем слое,Detail.cshtml
. Это не работает: RenderSection
видимо только делает разделы, которые определяются следующим слоем вверх.
конечно, я могу определить каждый раздел в каждой коже. Например, если _Common
нужно позвонить RenderSection("hd")
для раздела, определенного в Detail
, Я просто помещаю это в каждый _Skin
и это работает:
@section hd {
@RenderSection("hd")
}
это приводит к некоторому дублированию кода (так как каждая кожа теперь должна иметь этот же раздел) и обычно чувствует себя грязно. Я все еще новичок в Razor, и кажется, что я могу пропустить что-то очевидное.
когда отладка, я вижу полный список определенных разделов в WebViewPage.SectionWritersStack. Если бы я мог просто сказать RenderSection просмотреть весь список, прежде чем сдаться, он нашел бы нужный мне раздел. Увы, SectionWritersStack не является публичным.
кроме того, если бы я мог получить доступ к иерархии страниц макета и попытаться выполнить RenderSection в каждом отдельном контексте, я мог бы найти нужный мне раздел. Я, наверное, что-то упустил, но я не вижу никакого способа сделать это.
есть ли какой-то способ достичь этой цели, кроме метода, который я уже описал?
4 ответа:
Это фактически невозможно сегодня с помощью публичного API (кроме использования подхода переопределения раздела). Возможно, Вам повезет с использованием частного отражения, но это, конечно, хрупкий подход. Мы рассмотрим, как сделать этот сценарий проще в следующей версии Razor.
в то же время, вот несколько сообщений в блоге, которые я написал на тема:
@helper ForwardSection( string section ) { if (IsSectionDefined(section)) { DefineSection(section, () => Write(RenderSection(section))); } }
будет ли это работать ?
Я не уверен, что это возможно в 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"; }
теперь все, что вам нужно сделать, это использовать
_Common.cshtml
какLayout
для всех скинов. Например, в~/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); }
если вы запустите код выше, вы должны получить HTML-страницу с содержимым
Skin1.cshtml
и_Common.cshtml
короче говоря, вы установите макет для страницы макета (skin).
Не уверен, что это поможет вам, но я написал некоторые методы расширения, чтобы помочь "всплывать" разделы изнутри partials, которые также должны работать для вложенных макетов.
объявить в дочернем макете / представлении / частичном
@using (Html.Delayed()) { <b>show me multiple times, @Model.Whatever</b> }
рендер в любом родителе
@Html.RenderDelayed();
см. ссылку на ответ для получения дополнительной информации варианты использования, такие как рендеринг только одного задержанного блока, даже если он объявлен в повторяющемся представлении, рендеринг конкретных задержанных блоков и т. д.