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 76

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, которые также должны работать для вложенных макетов.

ввод содержимого в определенные разделы из частичного представления ASP.NET MVC 3 с Razor View Engine

объявить в дочернем макете / представлении / частичном

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

рендер в любом родителе

@Html.RenderDelayed();

см. ссылку на ответ для получения дополнительной информации варианты использования, такие как рендеринг только одного задержанного блока, даже если он объявлен в повторяющемся представлении, рендеринг конкретных задержанных блоков и т. д.