Rasiermesserverschachtelte Layouts mit kaskadierenden Abschnitten

79

Ich habe eine MVC3-Site, die Razor als Ansichtsmodul verwendet. Ich möchte, dass meine Website skinnbar ist. Die meisten möglichen Skins sind so ähnlich, dass sie von einem gemeinsam genutzten Master-Layout abgeleitet werden können.

Daher denke ich über dieses Design nach:

Geplantes Ansichtsdiagramm

Ich möchte jedoch RenderSectionin der Lage sein, die unterste Ebene aufzurufen _Common.cshtmlund einen Abschnitt rendern zu lassen, der in der obersten Ebene definiert ist Detail.cshtml. Dies funktioniert nicht: RenderSectionAnscheinend werden nur Abschnitte gerendert, die in der nächsten Ebene definiert sind.

Natürlich kann ich jeden Abschnitt in jeder Skin definieren. Wenn zum Beispiel ein in definierter Abschnitt _Commonaufgerufen werden muss , platziere ich diesen einfach in jedem und es funktioniert:RenderSection("hd")Detail_Skin

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

Dies führt zu einer gewissen Duplizierung des Codes (da jeder Skin jetzt denselben Abschnitt haben muss) und fühlt sich im Allgemeinen unordentlich an. Ich bin noch neu bei Razor und es scheint, als würde mir etwas Offensichtliches fehlen.

Beim Debuggen wird die vollständige Liste der definierten Abschnitte in WebViewPage.SectionWritersStack angezeigt. Wenn ich RenderSection nur anweisen könnte, die gesamte Liste durchzusehen, bevor ich aufgebe, würde es den Abschnitt finden, den ich brauche. Leider ist SectionWritersStack nicht öffentlich.

Wenn ich alternativ auf die Hierarchie der Layoutseiten zugreifen und versuchen könnte, RenderSection in jedem unterschiedlichen Kontext auszuführen, könnte ich den gewünschten Abschnitt finden. Ich vermisse wahrscheinlich etwas, aber ich sehe keine Möglichkeit, dies zu tun.

Gibt es eine andere Möglichkeit, dieses Ziel zu erreichen, als die bereits beschriebene Methode?

Chris Nielsen
quelle

Antworten:

35

Dies ist heute mit der öffentlichen API nicht möglich (außer mit dem Ansatz der Neudefinition von Abschnitten). Sie haben vielleicht etwas Glück mit privater Reflexion, aber das ist natürlich ein fragiler Ansatz. Wir werden versuchen, dieses Szenario in der nächsten Version von Razor zu vereinfachen.

In der Zwischenzeit habe ich einige Blog-Beiträge zu diesem Thema verfasst:

Marcind
quelle
3
Vielen Dank für die Antwort. Ich habe mit den verschachtelten Abschnitten mit dieser Syntax (wie oben) gespielt: @section hd {@RenderSection ("hd")} ... was für mich tatsächlich funktioniert und so aussieht, als könnte ich vorhandene verschachtelte MasterPages replizieren . Ich glaube, ich habe die Frage leicht missverstanden und dachte, das würde nicht funktionieren.
Mark Redman
2
Sowohl die Frage als auch die Antwort haben sehr geholfen, und auch ich stimme zu, dass dies in der nächsten Version von Razor einfacher sein sollte. Außerdem sollten Sie die Möglichkeit aktivieren, dass Teilansichten auch Abschnitte implementieren können, was derzeit nicht unterstützt wird.
Stute
1
@ Shrike Ich glaube nicht, dass sich in diesem Bereich etwas geändert hat. Sie können Feature-Anfragen auf der Uservoice-Site oder Fehler in Codeplex
Marcind
1
@marcind Schau dir meine Antwort an. Ich denke, das hat das OP verlangt. Recht?
Alireza Noori
1
MVC 5 ist raus. Irgendein Update? Alirzea sagte, er habe eine Lösung gefunden, aber sie schien nicht mit dem OP-Problem übereinzustimmen, da sie überhaupt nicht auf Abschnitte verwies.
Snekse
17
@helper ForwardSection( string section )
{
   if (IsSectionDefined(section))
   {
       DefineSection(section, () => Write(RenderSection(section)));
   }
}

Würde das den Job machen?

Geil
quelle
Verwenden Sie dies in der Zwischenschicht? So ziemlich das gleiche wie diese Erweiterungsklasse ? Wenn ja, ist dies eher eine Annehmlichkeit, wenn Sie einen Abschnitt neu deklarieren, als das Problem zu lösen, oder? Ich stelle nur sicher, dass ich es verstehe, da ich zu spät zu dieser Diskussion komme.
Drzaus
Für mich war dies die einzige Lösung, die funktioniert hat. Wenn ein Abschnitt im Basislayout bedingt gerendert wird, gibt MVC einen Laufzeitfehler aus, es sei denn, dieser Abschnitt ist (wie folgt ) in der Zwischenebene bedingt definiert . Danke @Randy!
Michael
Ist es möglich, alle aktuell definierten Abschnitte weiterzuleiten?
nvirth
4

Ich bin nicht sicher, ob dies in MVC 3 möglich ist, aber in MVC 5 kann ich dies mit dem folgenden Trick erfolgreich durchführen:

In ~/Views/Shared/_Common.cshtmlSchreiben Sie Ihre gemeinsamen HTML - Code wie:

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

In ~/Views/_ViewStart.cshtml:

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

Jetzt müssen Sie nur noch das _Common.cshtmlals das Layoutfür alle Skins verwenden. Zum Beispiel in ~/Views/Shared/Skin1.cshtml:

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

<p>Something specific to Skin1</p>

@RenderBody()

Jetzt können Sie den Skin als Layout im Controller oder in der Ansicht basierend auf Ihren Kriterien festlegen. Zum Beispiel:

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

Wenn Sie den obigen Code ausführen, sollten Sie eine HTML-Seite mit dem Inhalt von Skin1.cshtmlund erhalten_Common.cshtml

Kurz gesagt, Sie legen das Layout für die (Skin-) Layoutseite fest.

Alireza Noori
quelle
Ich hatte Probleme mit diesem Ansatz, da Abschnitte nicht sichtbar waren. Ich fand die Lösung unter blogs.msdn.microsoft.com/marcinon/2010/12/15/…
Spikolynn
1

Ich bin mir nicht sicher, ob dies Ihnen helfen wird, aber ich habe einige Erweiterungsmethoden geschrieben, um Abschnitte aus Teilbereichen heraus "aufzublasen", die auch für verschachtelte Layouts funktionieren sollten.

Injizieren von Inhalten in bestimmte Abschnitte aus einer Teilansicht ASP.NET MVC 3 mit Razor View Engine

In untergeordnetem Layout / Ansicht / Teil deklarieren

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

In jedem Elternteil rendern

@Html.RenderDelayed();

Weitere Anwendungsfälle finden Sie unter dem Antwortlink. Sie können beispielsweise nur einen verzögerten Block rendern, auch wenn er in einer sich wiederholenden Ansicht deklariert ist, bestimmte verzögerte Blöcke rendern usw.

drzaus
quelle