Вложенные макеты Razor с каскадными разделами
У меня есть сайт MVC3 с использованием Razor в качестве механизма просмотра. Я хочу, чтобы мой сайт был скин. Большинство возможных скинов достаточно схожи, что они могут быть получены из общего макета.
Поэтому я рассматриваю этот дизайн:
![Planned view diagram]()
Тем не менее, я хотел бы иметь возможность вызывать RenderSection
в нижнем слое _Common.cshtml
и отображать раздел, который определен в верхнем слое, Detail.cshtml
. Это не работает: RenderSection
, по-видимому, только отображает разделы, которые определены следующим уровнем вверх.
Конечно, я могу определить каждый раздел в каждом скине. Например, если _Common
нужно вызвать RenderSection("hd")
для раздела, определенного в Detail
, я просто поместил его в каждый _Skin
и он работает:
@section hd {
@RenderSection("hd")
}
Это приводит к некоторому дублированию кода (поскольку каждый скин теперь должен иметь этот же раздел) и в целом чувствует себя грязным. Я до сих пор новичок в Razor, и кажется, что я могу пропустить что-то очевидное.
При отладке я вижу полный список определенных разделов в WebViewPage.SectionWritersStack. Если бы я мог просто сказать RenderSection просмотреть весь список, прежде чем сдаваться, он найдет раздел, в котором я нуждаюсь. Увы, разделWritersStack не является общедоступным.
В качестве альтернативы, если бы я мог получить доступ к иерархии страниц макета и попытаться выполнить RenderSection в каждом другом контексте, я мог бы найти нужный раздел. Я, вероятно, что-то пропустил, но я не вижу никакого способа сделать это.
Есть ли способ достичь этой цели, кроме метода, который я уже изложил?
Ответы
Ответ 1
Сегодня это невозможно сегодня, используя открытый API (кроме использования подхода переопределения раздела). Возможно, вам удастся использовать частное отражение, но это, конечно, хрупкий подход. Мы рассмотрим этот сценарий в следующей версии Razor.
Тем временем здесь несколько сообщений в блоге, которые я написал на эту тему:
Ответ 2
@helper ForwardSection( string section )
{
if (IsSectionDefined(section))
{
DefineSection(section, () => Write(RenderSection(section)));
}
}
Будет ли это выполняться?
Ответ 3
Я не уверен, что это возможно в 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
Вкратце, вы установите макет для страницы макета (скина).
Ответ 4
Не уверен, что это вам поможет, но я написал несколько методов расширения, чтобы помочь "раздуть" разделы из частичных, которые также должны работать и для вложенных макетов.
Ввод содержимого в определенные разделы из частичного представления ASP.NET MVC 3 с Razor View Engine
Объявить в дочернем макете/представлении/частичном
@using (Html.Delayed()) {
<b>show me multiple times, @Model.Whatever</b>
}
Render в любом родителе
@Html.RenderDelayed();
См. ответную ссылку для большего количества случаев использования, например, только рендеринг одного отложенного блока, даже если он объявлен в повторяющемся представлении, рендеринг определенных отложенных блоков и т.д.