ASP.NET MVC 1.0 (五) ViewEngine 深入解析与应用实例
一.摘要本文讲解ViewEngine的作用, 并且深入解析了实现ViewEngine相关的所有接口和类, 最后演示了如何开发一个自定义的ViewEngine. 本系列文章已经全部更新为ASP.NET MVC 1.0版本.希望大家多多支持!二.承上启下首先注意: 我会将大家在MVC之前一直使用的ASP.NET页面编程模型称作ASP.NET WebForm编程模型.上一讲中我们已经学习了如何向View传递Model, 以及如何在View中使用Model对象. 目前为止我们使用的都还是ASP.NET WebForm的页面模型,比如aspx页面,用户控件,母版页等. 最后这些页面中都要转换为HTML代码. 比如页面中的内嵌代码:% ViewData[model] %你是否思考过, 为何页面会支持% %这种语法? 为何最后一个aspx页面会在浏览器中以HTML代码的形式展现?有人会回答这是ASP.NET自带的语法和功能. 没有错, ASP.NET帮我们做了编译页面, 输出HTML, 返回HTML给客户端浏览器等一系列工作.但是这些工作在MVC框架中有很多是属于View角色的职责. 为了继续使用原有的ASP.NET WebForm页面引擎, ASP.NET MVC抽象出来了ViewEngine这个角色. 顾名思义ViewEngine即视图引擎, 其主要作用就是找到View对象, 编译View对象中的语言代码(执行语言逻辑), 并且输出HTML. 下面讲解的WebFormViewEngine就是使用ASP.NET WebForm的页面编译/呈现功能实现的.三.ViewEngine解析下面将讲解和ViewEngine有关的各个接口和类.IView接口IView接口是对MVC结构中View对象的抽象, 此接口只有一个方法:void Render(ViewContext viewContext, TextWriter writer);Render方法的作用就是展示View对象, 通常是将页面HTML写入到Writer中供浏览器展示.在本系列第三篇文章中我曾经分析过, 虽然IView对象是MVC中View角色的抽象, 并且提供了Render方法, 但是实际上真正的View角色的显示逻辑在ViewPage/ViewUserControl类中. 这是由于ASP.NET MVC提供的WebFormViewEngine视图引擎是使用原有的ASP.NET Web From的页面显示机制, 我们无法直接将WebForm模型中的页面转化为IView对象.于是最后使用了一个折中的办法:在IView对象的Render方法中调用WebForm页面的Render方法. WebFormView是目前ASP.NET MVC中唯一实现了IView接口的类所以如果我们使用自定义的ViewEngine引擎, 就可以直接创建一个实现了IView接口的类实现Render方法.IViewEngine接口ViewEngine即视图引擎, 在ASP.NET MVC中将ViewEngine的作用抽象成了 IViewEngine 接口.虽然IViewEngine的职责是寻找View对象, 但是其定义的两个方法:FindPartialViewFindView返回的结果是ViewEngineResult对象, 并不是View对象. 我们可以将ViewEngineResult理解为一次查询的结果, 在ViewEngineResult对象中包含有本次找到的IView对象.ASP.NET MVC 提供了下面两个实现了IViewEngine接口的类:VirtualPathProviderViewEngineWebFormViewEngineWebFormViewEngine是VirtualPathProviderViewEngine的派生类.VirtualPathProviderViewEngine类实现了FindPartialView/FindView方法, 更够根据指定的路径格式搜索页面文件, 并且使用了提供了Cache机制缓存数据. 注意因为使用的是ASP.NET Cache,依赖HttpContext对象, 这就导致Cache无法在WebService或者WCf等项目中使用. VirtualPathProviderViewEngine寻找页面的方法依赖下面三个属性:MasterLocationFormatsViewLocationFormatsPartialViewLocationFormats在VirtualPathProviderViewEngine中只定义了这三个属性, 具体的值在派生类WebFormViewEngine中指定:public WebFormViewEngine() { MasterLocationFormats new[] { ~/Views/{1}/{0}.master, ~/Views/Shared/{0}.master }; ViewLocationFormats new[] { ~/Views/{1}/{0}.aspx, ~/Views/{1}/{0}.ascx, ~/Views/Shared/{0}.aspx, ~/Views/Shared/{0}.ascx }; PartialViewLocationFormats ViewLocationFormats; }上面的代码中我们可以一步了然ViewEngine都搜索哪些路径.甚至还可以添加我们自己的路径和文件类型.因为有了VirtualPathProviderViewEngine类, 在开发自定义的ViewEngine时不需要再编写搜索View文件的逻辑了.只需要定义搜索路径即可. 如果不使用ASP.NET WebForm的页面显示方式, 就需要自己定义的View对象如何显最后转化为HTML代码.在后面的实例中会演示创建一个我们自定义的ViewEngine.ViewEngineResultViewEngineResult是ViewEngine寻找View的查询结果.ViewEngineResult类没有派生类, 也就是说不同的ViewEngine返回的结果都是ViewEngineResult对象.ViewEngineResult类有一个很重要的构造函数:public ViewEngineResult(IView view, IViewEngine viewEngine)以WebFormViewEngine为例, 在WebFormViewEngine类中定义了 MasterLocationFormats/ViewLocationFormats /PartialViewLocationFormats , 在调用FindPartialView/FindView方法时, 首先找到View对象的磁盘路径, 然后使用CreatePartialView/CreateView方法将磁盘路径转化实现了IView接口的WebFormView对象.WebFormView中依然保存这页面对象的磁盘路径, 在调用Render时会根据磁盘路径创建ViewPage对象, 调用页面的Render方法.ASP.NET MVC编译页面时, 使用了.NET Framework 2.0以上的版本中提供的根据虚拟路径编译页面的函数:BuildManager.CreateInstanceFromVirtualPath(string virtualPath, Type requiredBaseType)命名空间为System.Web.Compilation.ViewEngineCollectionViewEngineCollection是IViewEngine对象的集合类. 在我们的系统中可以使用多个ViewEngine, 在寻找时会返回第一个匹配的ViewEngineResult, 下面是ViewEngineCollection类的Find方法代码:private ViewEngineResult Find(FuncIViewEngine, ViewEngineResult cacheLocator, FuncIViewEngine, ViewEngineResult locator) { ViewEngineResult result; foreach (IViewEngine engine in Items) { if (engine ! null) { result cacheLocator(engine); if (result.View ! null) { return result; } } } Liststring searched new Liststring(); foreach (IViewEngine engine in Items) { if (engine ! null) { result locator(engine); if (result.View ! null) { return result; } searched.AddRange(result.SearchedLocations); } } return new ViewEngineResult(searched); }通过上面的代码我们了解到, ViewEngineCollection会首先从Cache中搜索, 如果没有搜索到结果,则根据路径格式搜索. 如果最后还是没有搜索到View对象则抛出找不到View的异常.所以虽然我们可以添加多个ViewEngine, 但是永远不要为两个ViewEngine指定同样的搜索格式(路径文件类型), 因为如果出现一个页面对象符合两个ViewEngine的搜索格式的情况, 将无法控制使用哪一个ViewEngine输出页面.在ViewBaseResult.ExecuteResult()方法中, 调用了ViewEngineCollection.Find方法获取ViewEngineResult对象,并调用其中的IView.Render()方法完成View对象的显示.四.开发自定义ViewEngine下面通过示例演示如何开发自己的ViewEngine.其中要用到StringTemplate这个模板引擎, 在老赵的的MVC视频教程中也使用的此引擎演示ViewEngine. StringTemplate负责翻译一个模板页上面的占位符(aspx页面中的内嵌代码), 输出HTML.目前在StringTemplate的官方网站上已经提供了针对Asp.Net Mvc的ViewEngine.但是官方的ViewEngine模板没有使用VirtualPathProviderViewEngine基类.下面我将提供一种不能说更好但至少是另一种实现的StringTemplateViewEngine.其中需要使用StringTemplate的模版功能.1. 实现IView接口要开发一个自己的ViewEngine, 首先要创建实现了IView接口的类, 在此我们创建了名为StringTemplateView的类public class StringTemplateView : IView { #region 属性 Properties /// summary /// StringTemplate 对象, 在构造函数中创建 /// /summary private StringTemplate StringTemplate { get; set; } #endregion #region 构造函数 Constructed Function private StringTemplateView() { //不用于使用不带参数的构造函数 this.StringTemplate new StringTemplate(); } public StringTemplateView(StringTemplate template) { //null check if (template null) throw new ArgumentNullException(template); //set template this.StringTemplate template; } #endregion #region IView 成员 void IView.Render(ViewContext viewContext, System.IO.TextWriter writer) { foreach(var item in viewContext.ViewData) { this.StringTemplate.SetAttribute(item.Key.ToString(), item.Value.ToString()); } //为StringTemplate设置HttpContext this.StringTemplate.SetAttribute(context, viewContext.HttpContext); //输出模板 NoIndentWriter noIndentWriter new NoIndentWriter(writer); this.StringTemplate.Write(noIndentWriter); } #endregion }StringTemplateView是在StringTemplate视图引擎中View角色的抽象, 所以功能是实现呈现页面的Render方法. StringTemplate的核心功能就是一套自己定义的模板输出引擎, 所以在构造StringTemplateView对象是必须传入一个StringTemplate实例,在Render时只是调用StringTemplate对象的模板输出方法.2. 实现IViewEngine接口有了IView对象. 接下来就要实现最核心的IViewEngine接口. 在具体的StringTemplateViewEngine类中, 要返回一个带有StringTemplateView对象的ViewEngineResult.在我的实现方法中,使用了ASP.NET MVC已经提供的VirtualPathProviderViewEngine类作为我们的基类. VirtualPathProviderViewEngine类实现了IViewEngine接口的方法, 提供了在程序中寻找View物理文件路径的机制, 搜索时要使用在派生类中赋值的搜索路径.下面是我们的StringTemplateViewEngine类实现:public class StringTemplateViewEngine : VirtualPathProviderViewEngine { private string _AppPath string.Empty; #region 属性 Properties public static FileSystemTemplateLoader Loader { get; private set; } public static StringTemplateGroup Group { get; private set; } #endregion public StringTemplateViewEngine(string appPath) { _AppPath appPath; Loader new FileSystemTemplateLoader(appPath); Group new StringTemplateGroup(views, Loader); MasterLocationFormats new[] { /Views/{1}/{0}.st, /Views//Shared/{0}.st }; ViewLocationFormats MasterLocationFormats; PartialViewLocationFormats MasterLocationFormats; } protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) { return this.CreateView(controllerContext, partialPath, String.Empty); } protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) { StringTemplate stringTemplate Group.GetInstanceOf(viewPath.Replace(.st, )); StringTemplateView result new StringTemplateView(stringTemplate); return result; } }注意首先在我们的StringTemplateViewEngine中,提供了搜索模板文件的路径,即先从View/{controller}中搜索,再从View/Share中搜索. 这样VirtualPathProviderViewEngine基类的方法就可以找到我们.st模板文件的具体路径, 然后使用StringTemplateViewEngine中提供的创建StringTemplateView的方法, 根据具体路径创建StringTemplateView对象.在一些开源的ViewEngine中,尤其是MvcContrib项目中的ViewEngine都将创建View对象的功能放在一个ViewFactory类中, 个人认为这个更好的设计, 但是由于我们的StringTemplateViewEngine要继承VirtualPathProviderViewEngine, 所以没办法拆分创建View的方法.至此我们已经完成了StringTemplateViewEngine的全部工作.3.使用StringTemplateViewEngine(1)为 .st 模板页增加智能感知首先做一些准备工作. 因为我们的StringTemplate模板文件后缀是.st, 里面写的大部分都是HTML代码. 默认情况下Visual Studio是不会在编辑.st功能的时候提供智能感知支持的. 但是可以通过如下设置实现:单击菜单中的工具-选项:在文本编辑器的文件扩展名中, 如图所示的为.st扩展名增加HTML编辑器.接下来在.st文件中就可以识别HTML代码了:(2) 创建公用的菜单模板StringTemplate引擎支持模板的嵌套, 所以可以讲两个页面公用的菜单栏放在menu.st文件中. 而且我们将此文件放在share文件夹中以便供所有模板页调用. menu.st文件代码如下:ul idmenu lia href/StringTemplate/HelloSTHelloST/a/li lia href/StringTemplate/SharedSTSharedST/a/li /ul(3) 创建页面模板和Controller在Controller文件夹中, 创建StringTemplateController用于跳转到我们的模板页:public class StringTemplateController : Controller { public ActionResult HelloST() { ViewData[msg] Hello String Template ! ; return View(HelloST); } }在View文件夹中创建StringTemplate文件夹, 添加一个HelloST.st文件:!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Strict//EN http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd html xmlnshttp://www.w3.org/1999/xhtml head title在Shared文件夹中的st页面/title link href../Content/Site.css relstylesheet typetext/css //head body div classpage h1StringTemplateViewEngine示例程序/h1 div idmenucontainer $Views/Shared/menu()$ /div div idmain $msg$ /div /div /body /html示例中的代码十分简单, $msg$是StringTemplate的模板语言, 可以识别名称为msg的变量. $Views/Shared/menu()$也是StringTemplate中的语法, 作用是加载名为menu的模板.(4) 加载StringTemplateViewEngine 模板引擎虽然Controller和View文件都建立好了, 但是因为ASP.NET MVC默认的视图引擎是WebFormViewEngine, 但是可以同时使用多个视图引擎, 比如可以为所有.st后缀名的文件使用StringTemplateViewEngine视图引擎.在Global.asax文件中, 在程序启动时注册我们的ViewEngine:protected void Application_Start() { RegisterRoutes(RouteTable.Routes); //添加StringTemplate视图引擎 StringTemplateViewEngine engine new StringTemplateViewEngine(Server.MapPath(/)); ViewEngines.Engines.Add(engine); }现在, 访问localhost/StringTemplate/HelloST,就可以看到我们的自定义的模板引擎的输出结果了:在文章最后会提供本实例附带StringTemplateViewEngine的完整源代码.五.其他ViewEngine简介除了自己开发, 目前已经有了很多为ASP.NET MVC提供的ViewEngine:MVCContrib项目中的ViewEngine:SparkViewEngine(不推荐)
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2468976.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!