web-dev-qa-db-fra.com

La modification de MVC ViewBag dans une vue partielle ne persiste pas dans _Layout.cshtml

J'utilise MVC 3 avec le moteur d'affichage Razor. Je souhaite définir certaines valeurs dans le ViewBag dans une vue partielle et souhaite récupérer ces valeurs dans mon _Layout.cshtml. Par exemple, lorsque vous configurez un projet ASP.NET MVC 3 par défaut, vous obtenez un fichier _Layout.cshtml dans le dossier "/ Views/Shared". Dans ce _Layout.cshtml, le titre de la page est défini comme suit: 

<title>@ViewBag.PageTitle</title>

Ensuite, dans "/Views/Home/About.cshtml", le contenu du ViewBag est modifié:

@{
    ViewBag.Title = "About Us";
}

Cela fonctionne bien. Lorsque la vue À propos de est rendue, le titre de la page est "À propos de nous". Alors, maintenant, je souhaite rendre une vue partielle dans ma vue À propos et je souhaite modifier le ViewBag.Title dans ma vue partielle. ("/Views/Shared/SomePartial.cshtml")

@Html.Partial("SomePartial")

Dans cette vue partielle, j'ai ce code:

@{
    ViewBag.Title = "About Us From The Partial View";
}

Lorsque je débogue ce code, je vois que le ViewBag.Title est défini sur "À propos de nous", puis dans la vue partielle, il est réinitialisé sur "À propos de nous à partir de la vue partielle". Retour à "À propos de nous". 

Cela signifie-t-il que si le contenu du ViewBag est modifié dans une vue partielle, ces modifications n'apparaîtront pas (ne seront pas accessibles) dans la vue principale (About.cshtml) ou dans _Layout.cshtml?

Merci d'avance!

41
Zoran

J'ai également eu ce problème et je n'ai pas trouvé de solution nette et évidente.

La solution que j'ai proposée consistait à implémenter une méthode d'extension HTML renvoyant une classe 'PageData' que vous définissez, contenant toutes les données dont vous avez besoin:

    [ThreadStatic]
    private static ControllerBase pageDataController;
    [ThreadStatic]
    private static PageData pageData;

    public static PageData GetPageData(this HtmlHelper html) {
        ControllerBase controller = html.ViewContext.Controller;
        while (controller.ControllerContext.IsChildAction) {
            controller = controller.ControllerContext.ParentActionViewContext.Controller;
        }
        if (pageDataController == controller) {
            return pageData;
        } else {
            pageDataController = controller;
            pageData = new PageData();
            return pageData;
        }
    }

Il trouve le contrôleur de niveau supérieur pour la demande en cours et renvoie le même objet PageData chaque fois que la méthode est appelée dans la même demande HTTP. Il crée un nouvel objet PageData lors de son premier appel dans une nouvelle requête HTTP.

12
Chris

Si vous transmettez le ViewBag au viewdatadictionary du partiel, puis le retirez (et le diffusez), vous pouvez faire ce que vous voulez et la référence est conservée. La partie intéressante est que, vu que c'est dynamique, vous pouvez même ajouter des propriétés, puis elles apparaîtront dans Viewbag de la page parente.

Page:

//set the viewbag into the partial's view data
@{Html.RenderPartial("Elaborate", Model, new ViewDataDictionary { {"vb", ViewBag}});}

Partiel:

@{
   var vb = ((dynamic)ViewData["vb"]);
   vb.TheTitle = "New values";
 }

Page

@ViewBag.TheTitle = "New value"
31
Tony Basallo

La vue partielle obtient sa propre ViewBag.

Vous pouvez obtenir le ViewBag de la page à partir de ((WebViewPage) WebPageContext.Current.Page).ViewBag

8
SLaks

Vous pouvez faire cette astuce dans votre vue partielle pour remplacer le titre dans votre _Layout.cshtml:

@{
    ViewBag.Title = "About Us From The Partial View";
}

......

<script type="text/javascript">
    document.title = "@ViewBag.Title";
</script>
8
marianotigre

Comme d'autres l'ont fait remarquer Mise en page , Vues et Partials obtenir leur propre ViewBag. Cependant, j'ai pu le faire fonctionner avec les éléments suivants: Dans la vue ou partielle.

@{ Html.ViewContext.ViewBag.Title = "Reusable Global Variable"; }

Puis dans _Layout

@ Html.ViewContext.ViewBag.Title

En utilisant explicitement ViewContext, les vues "réutilisent" le même ViewBag.

4
Dax70

essayez le code @SLaks avec

(((WebViewPage) WebPageContext.Current.Page).ViewBag).PropertyName
1
killebytes

J'ai essayé ce qui suit et ça marche:

Dans la vue (parent) ...

@Html.Partial("SomePartial", ViewData, null)

Remarque: ViewData est passé en tant qu'argument model, mais vous devez spécifier null pour que l'argument viewData utilise la surcharge correcte. Vous ne pouvez pas utiliser ViewBag car Html.Partial n'aime pas la dynamique.

Ensuite, dans la vue partielle ...

@model ViewDataDictionary
@{
    Model["Title"] = "About us from the partial view";
}

Bien sûr, si vous devez utiliser l'argument model pour un modèle réel, vous devrez être plus créatif.

1
Moose Factory

Si quelqu'un cherche encore une solution à cela, il semble que vous puissiez le faire avec TempData:

TempData["x"] = x;

TempData est conservé jusqu'à sa lecture, vous pouvez donc le lire dans votre _Layout. Vous devez juste faire attention à tout lire pour qu'il soit effacé pour la prochaine demande.

0
chris