用ASP.NET MVC3混合Razor视图和WebForms母版页

[原文发表地址] Mixing Razor Views and WebForms Master Pages with ASP.NET MVC 3

[原文发表时间] Jan 25, 2011 ,12:17 PM

在过去的几个星期,我一直周游各地向人们介绍WebMatrix、Razor和ASP.NET MVC 3 。很多人已经在WebForms视图下安装了WebForms或MVC应用程序。很多时候人们会提出这样的疑问:“我可以在一个ASP.NET应用程序中混合Razor视图和WebForms视图吗?”

回答是:“不行,可以,或许行,但这不被支持。”

最常见的情况是某人已经有了一个运转良好的WebForms(ASPX)母版页,现在他想将一些Razor页面添加到应用程序中,但不想维护两个同等效果的母版页(一个用于ASPX,另一个用于Razor)。他想同时在WebForms和Razor视图中共享WebForms母版页。

首先,不可行的情况。

您不能直接以Razor模式布局WebForms视图,或者在Razor视图中直接运用WebForms母版页。尽管它们概念相似,Razor视图的布局毕竟与母版页的不同,而且概念也不能完全替换。

第二,可行的情况。

不过,Html.RenderPartial或Html.RenderPartial为两个视图引擎间的移动带来了可能。正如Eilon Lipton

所说,关于局部视图或者控制器的操作的想法是ASP.NET MVC中的首要概念,但是母版页是特定视图引擎实施过程中的细节。

您可以将共享的内容放在局部视图上,并且从任何地方调用它们。比如,根据Elion的建议,假设您有以下三个WebForms局部视图:

Header.ascx: 包括顶部导航,菜单等。

Footer.ascx: 包括版权声明,隐私权链接等。

SearchBox.ascx: 包括搜索栏操作执行。

然后,您就有了一个引用上述用户控件的基础WebForms母版页。

WebFormLayout.master: 按照以上控件进行布局。

Page1.aspx: 使用 WebFormLayout.master。

Page2.aspx: 使用 WebFormLayout.master。

Page3.aspx: 使用 WebFormLayout.master。

然后,假设您有一个引用这三个**相同**用户控件的Razor布局(Razor布局与WebForms母版页相似)。

* _RazorLayout.cshtml: 按照以上控件进行布局。

* Page4.cshtml: 使用 _RazorLayout.cshtml.

* Page5.cshtml: 使用 _RazorLayout.cshtml.

* Page6.cshtml: 使用 _RazorLayout.cshtml.

这是最干净、最简单和最受支持的做事方式。您会在Razor布局和WebForms母版页中看到一些轻微的重复,但是大多数分享的内容在.ascx局部页面中。你可以将它们随意混合,但我相信你已经有了自己的想法。

这或许可行,但它不被支持

Matt Hawley在CodePlex上看了看这个问题并在博客上发表了他对于CodePlex得解决方案。尽管这个任务看上去很简单,但是由于WebForms创建并呈现了一个控件树,而Razor是一个接近于一次性模板的全新系统,它实际上很棘手。Matt希望直接共享WebForms的母版页。

他的解决方案是

1. 有一个webforms母版页

2.在\Share folder中有一个名为”Razor View”的web forms视图页面

3. 在内容占位符中,aspx视图呈现的局部页面实际上是razor视图

4. 新添加了一些RazorView扩展方法来使控制器返回一个ViewResult。

这看起来复杂,实际上很简单。例如使用Matt的方法来像这个浏览器截图一样得到输出… …

clip_image002

… …我们从WebForms视图 Site.Master入手:

<%@ Master="" Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

  <!DOCTYPE html>

  <html>

    <headrunat=="server">

      <title>

        <="TitleContent"= runat"server"asp:ContentPlaceHolder ID /></>title

        </head>

        <body>

          <div>

            >I'm a WebForms Site.Master Page View. Seriously.</h1>

            <asp:ContentPlaceHolder ID="MainContent" runat="server" />

          </div>

        </body>

      </html>

这是一个常规的母版面。接下来是一个同样常规的Index.cshtml (Razor视图下)

<h3>This is the index page. I'm Razor. We've clearly broken some kind of law to be at this point, right?</h3>

 

这个页面基本上没有什么内容,而且没有布局或任何指定。

不过,Matt已经将一些扩展方法添加到控制器类中了。他没有从控制器操作中返回View(),而是通过下面的方法返回了RazorView()。

public class HomeController : Controller

{

 

    // GET: /Home/

 

  public ActionResult Index()

    {

 

        return this.RazorView();

 

    }

 

}

于它提醒重复,我们在此提醒RazorView是一个扩展方法。这是Matt提供的所有ControllerExtensions类。

{

 

public static ViewResult RazorView(this Controller controller)

 

{

 

return RazorView(controller, null, (ꡧnull);

 

}

 

public static ViewResult RazorView(this Controller controller, object model)

 

{

 

return RazorView(controller, null, model);

 

}

 

public static ViewResult RazorView(this Controller controller, string viewName)

 

{

 

return RazorView(controller, viewName, null);

 

}

 

public static ViewResult RazorView(this Controller controller, string viewName, object model)

 

{

 

if (model != null)

 

controller.ViewData.Model = model;

 

controller.ViewBag._ViewName = GetViewName(controller, viewName);

 

return new ViewResult

 

{

 

ViewName = "RazorView",

 

ViewData = controller.ViewData,

 

TempData = controller.TempData

 

};

 

}

 

static string GetViewName(Controller controller, string viewName)

 

{

 

return !string.IsNullOrEmpty(viewName)

 

? viewName

 

: controller.RouteData.GetRequiredString("action");

 

}

 

}

Matt解决方案的高明之处在于其简洁性。他在调用RazorView()时,返回了一个WebForm视图的ViewResult,尽管我们需要一个Razor视图的ViewResult。这符合了整个WebForms母版页系统。然而,名为“RazorView“的WebForms视图只为一个原因而存在。它调用了RenderPartial,因为只是在视图引擎间切换的正确方法。

<%@ Page="" Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage"

<dynamic>

  " %>

 

  <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

 

    <h2>I'm a secret WebForms View that lies to everyone and renders Razor stuff. Ssssh! Delete this line!</h2>

 

    <% Html.RenderPartial((string) ViewBag._ViewName); %>

 

  </asp:Content>

当然,当你使用该功能使,可以删除<h2>,它只是起到说明作用。这个视图只是保证转换的填充码。

现在你已经完全明白了吧。在您的ASP.NET MVC应用程序中有两个不错的选择。第一个方法有点重复,但是洁净。第二个方法有点棘手而且您必须依靠自己,但可以直接在WebForms和Razor视图间共享母版页。您可以从Matt的博客中获取第二种方法的代码

感谢Elion和Matt!