【问题标题】:Razor Nested Layouts with Cascading Sections具有级联部分的 Razor 嵌套布局
【发布时间】:2011-07-28 09:22:58
【问题描述】:

我有一个使用 Razor 作为其视图引擎的 MVC3 站点。我希望我的网站可以换肤。大多数可能的皮肤都非常相似,可以从共享的主布局中派生出来。

因此,我正在考虑这样的设计:

但是,我希望能够在底层 _Common.cshtml 中调用 RenderSection,并让它渲染在顶层 Detail.cshtml 中定义的部分。这不起作用:RenderSection 显然只渲染定义在下一层之上的部分。

当然,我可以在每个皮肤中定义每个部分。例如,如果_Common 需要为Detail 中定义的部分调用RenderSection("hd"),我只需将其放在每个_Skin 中即可:

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

这会导致一些代码重复(因为现在每个皮肤都必须有相同的部分)并且通常感觉很混乱。我还是 Razor 的新手,似乎我可能遗漏了一些明显的东西。

调试时,我可以在 WebViewPage.SectionWritersStack 中看到已定义部分的完整列表。如果我可以告诉 RenderSection 在放弃之前查看整个列表,它会找到我需要的部分。唉,SectionWritersStack 是非公开的。

或者,如果我可以访问布局页面的层次结构并尝试在每个不同的上下文中执行 RenderSection,我可以找到我需要的部分。我可能遗漏了一些东西,但我没有看到任何方法可以做到这一点。

除了我已经概述的方法之外,还有其他方法可以实现这个目标吗?

【问题讨论】:

    标签: asp.net-mvc-3 razor viewengine


    【解决方案1】:

    这实际上在今天使用公共 API 是不可能的(除了使用部分重新定义方法)。使用私有反射可能会有一些运气,但这当然是一种脆弱的方法。我们将研究在下一版本的 Razor 中简化此场景。

    与此同时,我写了几篇关于这个主题的博文:

    【讨论】:

    • 感谢您的回复,我一直在使用这种语法(如上)使用嵌套部分:@section hd { @RenderSection("hd") } ...这实际上对我有用看起来我可以复制现有的嵌套母版页。我想我稍微误解了这个问题,并认为这行不通。
    • 问题和答案都有很大帮助,我也同意在 Razor 的下一个版本中这应该更容易。您还应该启用部分视图也能够实现部分的可能性,目前不支持。
    • @Shrike 我认为这方面没有任何改变。您可以在uservoice site 上输入功能请求或在codeplex 上输入错误
    • @marcind 看看我的回答。我认为这是OP所要求的。对吗?
    • MVC 5 出来了。任何更新? Alirzea 说他找到了一个解决方案,但它似乎与 OPs 问题不匹配,因为它根本没有引用章节。
    【解决方案2】:
    @helper ForwardSection( string section )
    {
       if (IsSectionDefined(section))
       {
           DefineSection(section, () => Write(RenderSection(section)));
       }
    }
    

    这样可以吗?

    【讨论】:

    • 你是在中间层用这个吗?和this extension class差不多吗?如果是这样,这在重新声明一个部分时更方便,而不是解决问题,对吧?只是确保我理解,因为我迟到了这个讨论。
    • 对我来说,这是唯一有效的解决方案。如果一个部分在基本布局中有条件地呈现,MVC 将抛出运行时错误,除非该部分在中间层中有条件地定义(像这样)。谢谢@Randy!
    • 是否可以转发当前定义的所有section?
    【解决方案3】:

    我不确定这在 MVC 3 中是否可行,但在 MVC 5 中,我可以使用以下技巧成功地做到这一点:

    ~/Views/Shared/_Common.cshtml 中编写您常用的 HTML 代码,例如:

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

    ~/Views/_ViewStart.cshtml:

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

    现在您所要做的就是将_Common.cshtml 用作所有皮肤的Layout。比如~/Views/Shared/Skin1.cshtml:

    @{
        Layout = "~/Views/Shared/_Common.cshtml";
    }
    
    <p>Something specific to Skin1</p>
    
    @RenderBody()
    

    现在您可以根据您的标准将皮肤设置为控制器或视图中的布局。例如:

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

    如果你运行上面的代码,你应该得到一个包含Skin1.cshtml_Common.cshtml内容的HTML页面

    简而言之,您将为(皮肤)布局页面设置布局。

    【讨论】:

    【解决方案4】:

    不确定这是否会对您有所帮助,但我编写了一些扩展方法来帮助从部分中“冒泡”部分,这也适用于嵌套布局。

    Injecting content into specific sections from a partial view ASP.NET MVC 3 with Razor View Engine

    在子布局/视图/部分中声明

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

    在任何父级中渲染

    @Html.RenderDelayed();
    

    查看答案链接了解更多用例,例如即使在重复视图中声明也只渲染一个延迟块、渲染特定延迟块等。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-26
      • 1970-01-01
      • 2019-11-04
      • 2020-04-18
      • 1970-01-01
      • 2018-03-03
      • 1970-01-01
      相关资源
      最近更新 更多