Modifying MVC 3 ViewBag in a partial view does not persist to the _Layout.cshtml

我怕爱的太早我们不能终老 提交于 2019-11-28 06:11:27

I also had this problem, and couldn't find any neat and obvious solution.

The solution I came up with was to implement an Html extension method that returns a 'PageData' class that you define, containing whatever data you need:

    [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;
        }
    }

It finds the top-level controller for the current request, and returns the same PageData object every time the method is called within the same HTTP request. It creates a new PageData object the first time it is called in a new HTTP request.

If you pass the ViewBag into the partial's viewdatadictionary, then pull it out (and cast), you can do whatever you want and the reference is kept. The cool part is that since it's dynamic, you can even add properties and then they'll show up on the parent page's Viewbag.

Page:

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

Partial:

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

Page

@ViewBag.TheTitle = "New value"

The partial view gets its own ViewBag.

You can get the page's ViewBag from ((WebViewPage) WebPageContext.Current.Page).ViewBag

You can do this trick in your partial view to override the title in your _Layout.cshtml:

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

......

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

As others have pointed out Layout, Views and Partials get their own ViewBag. However, I was able to get it to work with the following: In the View or Partial.

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

Then in _Layout

@Html.ViewContext.ViewBag.Title

By explicitly using the ViewContext, the views 're-use' the same ViewBag.

I've tried the following and it works:

In the (parent) view...

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

Note: ViewData is passed as the model argument, but you have to specify null for the viewData argument to use the correct overload. You can't use ViewBag because Html.Partial doesn't like dynamics.

Then , in the partial view...

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

Of course, if you need to use the model argument for a real model, you'll have to be more creative.

killebytes

try @SLaks code with

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

If anyone is still looking for a solution to this it appears that you can do it with TempData:

TempData["x"] = x;

TempData is persisted until it is read so you can just read it in your _Layout. You just have to be careful that you read everything so that it is cleared for the next request.

herui

I encountered the same problem when I use mvc3, and I found that

this.ViewBag.ViewBag.PropertyName 

works in your custom control.

I this is what page data is designed for. Pop this into your view.

@Page.somePropertyName = "Whatever you want";

And then access it in your layout view. Be sure to check that its not null first.

@{
    if(Page.somePropertyName != null)
    {
       //Do stuff
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!