J'ai un site MVC3 utilisant Razor comme moteur de vue. Je veux que mon site soit skinnable. La plupart des habillages possibles sont suffisamment similaires pour pouvoir dériver d'une disposition principale partagée.
Par conséquent, j'envisage cette conception:
Cependant, je voudrais pouvoir appeler RenderSection
dans la couche inférieure, _Common.cshtml
, Et lui faire rendre une section qui est définie dans la couche supérieure, Detail.cshtml
. Cela ne fonctionne pas: RenderSection
ne rend apparemment que les sections définies au niveau suivant.
Bien sûr, je peux définir chaque section dans chaque skin. Par exemple, si _Common
A besoin d'appeler RenderSection("hd")
pour une section définie dans Detail
, je place simplement ceci dans chaque _Skin
Et cela fonctionne:
@section hd {
@RenderSection("hd")
}
Cela entraîne une certaine duplication de code (puisque chaque skin doit maintenant avoir cette même section) et se sent généralement en désordre. Je suis encore nouveau pour Razor, et il semble que je manque peut-être quelque chose d'évident.
Lors du débogage, je peux voir la liste complète des sections définies dans WebViewPage.SectionWritersStack. Si je pouvais simplement dire à RenderSection de parcourir toute la liste avant d'abandonner, il trouverait la section dont j'ai besoin. Hélas, SectionWritersStack n'est pas public.
Alternativement, si je pouvais accéder à la hiérarchie des pages de mise en page et tenter d'exécuter RenderSection dans chaque contexte différent, je pourrais localiser la section dont j'ai besoin. Il me manque probablement quelque chose, mais je ne vois aucun moyen de le faire.
Y a-t-il un moyen d'atteindre cet objectif, autre que la méthode que j'ai déjà décrite?
Ce n'est en fait pas possible aujourd'hui en utilisant l'API publique (autre que l'utilisation de l'approche de redéfinition de section). Vous pourriez avoir de la chance en utilisant la réflexion privée, mais c'est bien sûr une approche fragile. Nous chercherons à faciliter ce scénario dans la prochaine version de Razor.
En attendant, voici quelques articles de blog que j'ai écrits sur le sujet:
@helper ForwardSection( string section )
{
if (IsSectionDefined(section))
{
DefineSection(section, () => Write(RenderSection(section)));
}
}
Est-ce que cela ferait l'affaire?
Je ne sais pas si cela est possible dans MVC 3, mais dans MVC 5, je suis en mesure de le faire avec succès en utilisant l'astuce suivante:
Dans ~/Views/Shared/_Common.cshtml
écrivez votre code HTML commun comme:
<!DOCTYPE html>
<html lang="fa">
<head>
<title>Skinnable - @ViewBag.Title</title>
</head>
<body>
@RenderBody()
</body>
</html>
Dans ~/Views/_ViewStart.cshtml
:
@{
Layout = "~/Views/Shared/_Common.cshtml";
}
Il ne vous reste plus qu'à utiliser le _Common.cshtml
comme Layout
pour tous les skins. Par exemple, dans ~/Views/Shared/Skin1.cshtml
:
@{
Layout = "~/Views/Shared/_Common.cshtml";
}
<p>Something specific to Skin1</p>
@RenderBody()
Vous pouvez maintenant définir l'habillage comme disposition dans le contrôleur ou la vue en fonction de vos critères. Par exemple:
public ActionResult Index()
{
//....
if (user.SelectedSkin == Skins.Skin1)
return View("ViewName", "Skin1", model);
}
Si vous exécutez le code ci-dessus, vous devriez obtenir une page HTML avec le contenu de Skin1.cshtml
et _Common.cshtml
En bref, vous définissez la mise en page de la page de mise en page (skin).
Je ne sais pas si cela vous aidera, mais j'ai écrit quelques méthodes d'extension pour aider à faire des bulles dans les partiels, ce qui devrait également fonctionner pour les dispositions imbriquées.
Déclarez dans la disposition enfant/vue/partielle
@using (Html.Delayed()) {
<b>show me multiple times, @Model.Whatever</b>
}
Rendu dans n'importe quel parent
@Html.RenderDelayed();
Voir le lien de réponse pour plus de cas d'utilisation, comme le rendu d'un seul bloc retardé même s'il est déclaré dans une vue répétitive, le rendu de blocs retardés spécifiques, etc.