【问题标题】:Modifying MVC 3 ViewBag in a partial view does not persist to the _Layout.cshtml在局部视图中修改 MVC 3 ViewBag 不会持续到 _Layout.cshtml
【发布时间】:2011-06-25 19:59:06
【问题描述】:

我正在使用带有 Razor 视图引擎的 MVC 3。我想在部分视图内的 ViewBag 中设置一些值,并希望在我的 _Layout.cshtml 中检索这些值。例如,当您设置默认的 ASP.NET MVC 3 项目时,您会在“/Views/Shared”文件夹中获得一个 _Layout.cshtml 文件。在那个 _Layout.cshtml 中,页面标题设置如下:

<title>@ViewBag.PageTitle</title>

然后在“/Views/Home/About.cshtml”视图中修改ViewBag的内容:

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

这很好用。当“关于”视图呈现时,页面标题为“关于我们”。所以,现在我想在 About 视图中呈现 Partial 视图,并且我想在 Partial 视图中修改 ViewBag.Title。 ("/Views/Shared/SomePartial.cshtml")

@Html.Partial("SomePartial")

在这个部分视图中我有这个代码:

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

当我调试此代码时,我看到 ViewBag.Title 设置为“关于我们”,然后在部分视图中我看到它被重置为“从部分视图中了解我们”,但是当代码到达 _Layout 时。 cshtml 它可以追溯到“关于我们”。

这是否意味着如果在 Partial 视图中修改 ViewBag 的内容,这些更改将不会出现(可访问)在主视图 (About.cshtml) 或 _Layout.cshtml 中?

提前致谢!

【问题讨论】:

  • 请参阅此解决方案以获取按请求单例作为上述方案的替代方案,该方案非常有效:stackoverflow.com/questions/194999/…
  • 不是 100% 肯定,但我猜你不能更改 ViewBag 变量,因为基于 .Net 中的 MVC 模型,部分更像是更小的、可重用的组件,而不是完整的视图。视图可以使用部分组件将这些组件添加(渲染)到它们自己,但不能反过来。视图还可以将 ViewModels 传递给局部视图,以向它们提供需要渲染的数据。我知道这不是很有建设性,但我的直觉是,如果您尝试从部分更改 ViewBag 变量,那么您可能做错了什么。

标签: view asp.net-mvc-3 razor partial viewbag


【解决方案1】:

如果有人仍在寻找解决方案,您似乎可以使用 TempData:

TempData["x"] = x;

TempData 在被读取之前一直保持,因此您可以在 _Layout 中读取它。您只需要小心阅读所有内容,以便为下一个请求清除它。

【讨论】:

    【解决方案2】:

    这就是页面数据的设计目的。将此弹出到您的视图中。

    @Page.somePropertyName = "Whatever you want";
    

    然后在您的布局视图中访问它。请务必先检查它是否不为空。

    @{
        if(Page.somePropertyName != null)
        {
           //Do stuff
        }
    }
    

    【讨论】:

    【解决方案3】:

    尝试使用@SLaks 代码

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

    【讨论】:

      【解决方案4】:

      我已经尝试了以下方法并且它有效:

      在(父)视图中...

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

      注意:ViewData 作为 model 参数传递,但您必须为 viewData 参数指定 null 才能使用正确的重载。你不能使用ViewBag,因为Html.Partial 不喜欢动态。

      那么,在局部视图中……

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

      当然,如果您需要为真实模型使用 model 参数,则必须更具创造性。

      【讨论】:

        【解决方案5】:

        您可以在局部视图中使用此技巧来覆盖 _Layout.cshtml 中的标题:

        @{
            ViewBag.Title = "About Us From The Partial View";
        }
        
        ......
        
        <script type="text/javascript">
            document.title = "@ViewBag.Title";
        </script>
        

        【讨论】:

        • 这对 SEO 不起作用,因为它需要在呈现的标记中
        【解决方案6】:

        我在使用mvc3的时候也遇到了同样的问题,我发现

        this.ViewBag.ViewBag.PropertyName 
        

        在您的自定义控件中工作。

        【讨论】:

          【解决方案7】:

          正如其他人指出的 LayoutViewsPartials 拥有自己的 ViewBag。但是,我能够让它与以下内容一起使用: 在视图或局部。

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

          然后在_Layout中

          @Html.ViewContext.ViewBag.Title

          通过显式使用 ViewContext,视图“重用”同一个 ViewBag。

          【讨论】:

            【解决方案8】:

            如果您将 ViewBag 传递到部分的 viewdatadictionary,然后将其拉出(并投射),您可以做任何您想做的事情并保留引用。很酷的部分是,由于它是动态的,您甚至可以添加属性,然后它们会显示在父页面的 Viewbag 上。

            页面:

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

            部分:

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

            页面

            @ViewBag.TheTitle = "New value"
            

            【讨论】:

            • 或者,您可以直接将其添加到页面的视图包中。即在页面“ViewBag.PageBag = ViewBag;”中然后在部分 "ViewBag.PageBag.Title = "New value""
            • 当我需要在部分视图中设置页面标题以显示在我的布局头标签中时,这是我发现的唯一可行的方法。
            【解决方案9】:

            我也有这个问题,找不到任何简洁明了的解决方案。

            我想出的解决方案是实现一个 Html 扩展方法,该方法返回您定义的“PageData”类,包含您需要的任何数据:

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

            它为当前请求找到顶层控制器,并且每次在同一个 HTTP 请求中调用该方法时都返回同一个 PageData 对象。第一次在新的 HTTP 请求中调用它时,它会创建一个新的 PageData 对象。

            【讨论】:

            • 我有点惊讶 ASP.NET MVC 没有提供更好的方法来做到这一点。我能想到的是,我们正在尝试做的事情打破了 MVC 模式,但我们都知道在现实世界中会发生这种情况。我在当前项目中找到了解决此要求的方法,因此我将没有机会尝试您的解决方案。但感谢您的输入。我会将其标记为已接受的答案,并希望它对其他人有所帮助。 -谢谢
            • 我已经尝试过了,但它不起作用。我创建了一个名为 PageData 的简单类(未在您的答案中显示),具有 1 个 string 属性。如果您在局部视图中设置 PageData 的此属性,则在您的 _Layout 中它是 null,因为 _Layout 在设置之前访问该属性。
            【解决方案10】:

            局部视图有自己的ViewBag

            可以从((WebViewPage) WebPageContext.Current.Page).ViewBag获取页面的ViewBag

            【讨论】:

            • 那行不通。我尝试使用该代码修改部分视图中的 ViewBag,但是当我访问 _Layout.cshtml 中的 ViewBag 时,我仍然拥有在“关于”视图中设置的值。
            • 这不起作用(至少在 MVC 4 中)。局部视图有一个不同的 WebPageContext.Current.Page 对象(它有一个特定于局部视图。
            猜你喜欢
            • 2011-09-04
            • 2016-12-15
            • 2011-06-24
            • 1970-01-01
            • 1970-01-01
            • 2012-09-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多