夜鹰教程网-程序员的加油站
 当前位置:文章中心 >> vs2022_vs2019_vs2017_vs2014_vs2012
ASP.NET MVC开发实践教程【ViewEngine 深入解析与应用实例】
夜鹰教程网 来源:www.yyjcw.com 日期:2016-11-13 17:00:12
本文讲解ViewEngine的作用, 并且深入解析了实现ViewEngine相关的所有接口和类, 最后演示了如何开发一个自定义的ViewEngine. 本系列文章已经全部更新为ASP.NET MVC 1.0版本.希望大家多多支持!

MVC视频教程点击查看


一.摘要
本文讲解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对象, 但是其定义的两个方法:

&bull;FindPartialView
&bull;FindView
返回的结果是ViewEngineResult对象, 并不是View对象. 我们可以将ViewEngineResult理解为一次查询的结果, 在ViewEngineResult对象中包含有本次找到的IView对象.

ASP.NET MVC 提供了下面两个实现了IViewEngine接口的类:

&bull;VirtualPathProviderViewEngine
&bull;WebFormViewEngine
WebFormViewEngine是VirtualPathProviderViewEngine的派生类.

VirtualPathProviderViewEngine类实现了FindPartialView/FindView方法, 更够根据指定的路径格式搜索页面文件, 并且使用了提供了Cache机制缓存数据. 注意因为使用的是ASP.NET Cache,依赖HttpContext对象, 这就导致Cache无法在WebService或者WCf等项目中使用. VirtualPathProviderViewEngine寻找页面的方法依赖下面三个属性:

&bull;MasterLocationFormats
&bull;ViewLocationFormats
&bull;PartialViewLocationFormats
在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.

ViewEngineResult
ViewEngineResult是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.

ViewEngineCollection
ViewEngineCollection是IViewEngine对象的集合类. 在我们的系统中可以使用多个ViewEngine,  在寻找时会返回第一个匹配的ViewEngineResult, 下面是ViewEngineCollection类的Find方法代码:

        private ViewEngineResult Find(Func<IViewEngine, ViewEngineResult> cacheLocator, Func<IViewEngine, ViewEngineResult> locator) {
            ViewEngineResult result;

            foreach (IViewEngine engine in Items) {
                if (engine != null) {
                    result = cacheLocator(engine);

                    if (result.View != null) {
                        return result;
                    }
                }
            }

            List<string> searched = new List<string>();

            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 id="menu">
    <li><a href="/StringTemplate/HelloST">HelloST</a></li>
    <li><a href="/StringTemplate/SharedST">SharedST</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 xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>在Shared文件夹中的st页面</title>
<link href="../Content/Site.css" rel="stylesheet" type="text/css" /></head>
<body>
    <div class="page"> 
        <h1>StringTemplateViewEngine示例程序</h1>
        <div id="menucontainer">
            $Views/Shared/menu()$
        </div>
       
        <div id="main">
            $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:

&bull;SparkViewEngine(不推荐)
&bull;BrailViewEngine
&bull;XsltViewEngine
StringTemplateViewEngine

这是StringTemplate项目为ASP.NET MVC开发的ViewEngine, 官方以及下载网址是

http://www.stringtemplate.org/

另外在著名的MonoRail项目中, 还有一些类似于StringTemplate的页面显示引擎, 虽然都没有为ASP.NET MVC开发专门的ViewEngine, 但是还是很有参考价值的.我们可以用上面介绍的方法, 在页面显示引擎的基础上自己开发ASP.NET MVC的ViewEngine:

MonoRail项目中的三个ViewEngine:

&bull;AspNetViewEngine:用传统的.aspx文件做模板, 可以照常使用aspx语法和服务器控件, 但是由于Webform的生命周期和MonoRail完全不同, 有时候会让人觉得别扭, 有部分特性也受到了限制.
&bull;NVelocityViewEngine: 用NVelocity做模板引擎, 需要学习VTL语法, 但是使用很简单, 特别是很多java程序员已经熟悉velocity. 简单的语法也强迫程序员把逻辑和界面很好的分离开来, 方便跟美工配合.
&bull;BrailViewEngine:基于Boo的模板引擎, Boo是一种语法类似python的.NET语言, 据MonoRail的参考说, Brail引擎是功能最强, 性能最好的选择, 但Boo是一种陌生的语言, 这成了Brail引擎应用的最大障碍.
六.总结
本篇文章详细介绍了ViewEngine相关类, 已经如何开发自己的ViewEngine. 花了2周时间创作完成, 让大家久等了. 说道最近博客园首页的文章问题, 我觉得一篇文章除了要有知识点, 还有能够很好的讲解, 让大家明白比让自己明白更重要.我没有为了速度草草发表文章,就是希望写出来的东西能够有资格发表到博客园首页.

我希望大家都通过自律来建设博客园,  明白分享知识是一件光荣而且快乐的事情!

文章示例代码下载:

http://files.cnblogs.com/zhangziqiu/AspNetMvc-5-Demo.rar

作者:张子秋

复制链接 网友评论 收藏本文 关闭此页
上一条: 如何封装JS和CSS文件为服务器端控…  下一条: ASP.NET MVC开发实践教程【View/Model …
夜鹰教程网成立于2008年,目前已经运营了将近 13 年,发布了大量关于 html5/css3/C#/asp.net/java/python/nodejs/mongodb/sql server/android/javascript/mysql/mvc/easyui/vue/echarts原创教程。 我们一直都在坚持的是:认证负责、一丝不苟、以工匠的精神来打磨每一套教程,让读者感受到作者的用心。我们默默投入的时间,确保每一套教程都是一件作品,而不是呆板的文字和视频! 目前我们推出在线辅导班试运营,模式为一对一辅导,教学工具为QQ。我们的辅导学科包括 java 、android原生开发、webapp开发、商城开发、C#和asp.net开发,winform和物联网开发、web前端开发,但不仅限于此。 普通班针对的是国内学员,例如想打好基础的大学生、想转行的有志青年、想深入学习的程序员、想开发软件的初学者或者业余爱好者等。 就业办针对即将毕业上岗的大四学生,或者打算转行的初级开发工程师。 留学生班针对的是在欧美、加拿大、澳洲、日本、韩国、新加坡等地留学的中国学子,目的是让大家熟练地掌握编程技能,按时完成老师布置的作业,并能顺利地通过考试。 详细咨询QQ:1416759661   夜鹰教程网  基于角色的权限管理系统(c-s/b-s)。
  夜鹰教程网  基于nodejs的聊天室开发视频教程
  夜鹰教程网  Git分布式版本管理视频教程
  夜鹰教程网  MVC+EasyUI视频教程
  夜鹰教程网  在线考试系统视频教程
  夜鹰教程网  MongoDB视频教程。
  夜鹰教程网 Canvas视频教程
  夜鹰教程网 报表开发视频教程
  热点推荐
一个关于天气预报的WebService【C…
VS2010最大的新特点是并行编程的进…
TextBox控件:asp.net中如何为密码…
Web服务调用实例:实现天气预报的…
ASP.NET程序员面试试题(130道题)
ASP.NET教程:调用WebService的源码…
网站开发全程设计
据说这套.net面试题很多网络公司都…
考考你:C#常见题型及部分答案
原创:.net读取数据库sql2000
伪静态URL重写配置
配置web.config代码asp.net3.5个性…
使用线程池提高性能 Socket网络编…
ASP.NET(C#)GridView表头的增加…
如何找到正确的学习方向【.NET版】…
  最近更新
C#修改注册表demo
一个获取内容中的图片地址的方法
ASP.NET 4.0尚未在 Web 服务器上注…
四大作用域:application,session…
ConfigurationManager不存在的解决…
vs2012_vs2013_vs2015没有Web Dep…
vs2015禁用解决方案中单击打开文件…
微软为Visual Studio 2015新增安卓…
C#如何实现搜索引擎网络爬虫程序
C#中正则表达式的用法
用C#抓取需要登录的页面数据
VS2015新功能
VS2015安装图解教程
vs2015新功能介绍
vs2015安装图解

关于我们 | 网站建设 | 技术辅导 | 常见问题 | 联系我们 | 友情链接

夜鹰教程网 版权所有 www.yyjcw.com All rights reserved 备案号:蜀ICP备08011740号3