文章非常长,仅仅用于记录自己学习。

创建自定义视图引擎

创建自定义视图引擎的价值是,演示请求处理管道如何工作,并完善关于MVC架构如何操作的知识,视图引擎实现IViewEngine接口,如下图所示:

    public interface IViewEngine{ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache);ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache);void ReleaseView(ControllerContext controllerContext, IView view);}

视图引擎的作用是将对视图的请求转化成ViewEngineResult对象。 这个接口中两个方法是FindView FindPartialView,给它们传递的是描述请求的参数:处理该请求的控制器(ControllerContext对象)、视图名及其布局,以及是否允许视图引擎重用其缓存结果。当框架对ViewResult进行处理时,会调用这个两个方法。最后一个方法是ReleaseView(释放视图),当视图不在需要时被调用(从其名称不难看出,该方法的作用是释放视图所占用的资源)。

当请求一个视图时,ViewEngineResult类使视图引擎能够对MVC框架做出响应。代码如下图所示:

using System.Collections.Generic;namespace System.Web.Mvc
{public class ViewEngineResult{public IEnumerable<string> SearchedLoactions{get;private set;}public IView View {get;private set;}public IViewEngine ViewEngine{get;private set;}public ViewEngineResutlt(IEnumerable<string> searchedLoactions){if(searchedLoactions == null){throw new ArgumentNullException("searchedLocations");}SearchedLoactions = searchedLoactions}public ViewEngineResutlt(IView view,IViewEngine viewEngine){if(view == null){throw new ArgumentNullException("view");}if(viewEngine == null){throw new ArgumentNullException("viewEngine");}View = view;ViewEngine = viewEngine;}}
}

可以通过两个构造器中的其中一个来表示一个结果,如果视图引擎能够对请求提供视图,那么可以用以下构造器创建一个ViewEngineResult:

public ViewEngineResutlt(IView view,IViewEngine viewEngine)

如果视图引擎不能对请求提供视图,那么可以使用如下构造器: 这一版本是查找视图位置的一个枚举。如果找不到视图,就会将枚举的信息显示给用户。

public ViewEngineResutlt(IEnumerable<string> searchedLoactions)

视图引擎的最后一个构造块是IView接口,如下图所示:

public interface IView{void Render(ViewContext viewContext, TextWriter writer);}

把一个IView实现传递给ViewEngineResult对象的构造器,然后它会被视图引擎方法所返回。MVC框架会调用Render方法(把IView实现传递给ViewEngineResult对象构造器时,自然便会调用IView接口中的这个Render方法)。 到目前为止,ViewContext对象定义了一些属性,这些属性给你提供请求信息以及MVC框架如何处理它的细节。

有用的ViewContext属性
名称 描述
Controller 返回处理当前请求的IController实现
RequestContext 返回当前请求的细节
RouteData 为当前请求返回的路有数据
TempData 返回和请求相关的临时数据
View 返回将要处理请求的IView接口的实现。很明显,如果你正在创建一个自定义视图实现,它将是当前类。
ViewBag 返回一个表示视图的object
ViewData 返回一个包含模式视图包和元数据的视图模型数据字典。

这些属性中最有趣的是ViewData,它返回一个ViewDataDictionary对象。定义了一些有用的属性,如下图所示:

有用的ViewDataDictionary
名称 描述
keys 为字典中的数据返回键值集合,他们可用来访问视图包属性
Model 为请求返回视图模型对象
ModelMetadata 返回一个可以用来反映模型类型的ModelMetadata对象
ModelState 返回有关模型的状态信息

根据以上知识,创建一个自定义视图引擎, 首先新建一个空白项目,创建一个Home控制器,如下图所示:

    public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Messge = "Hello,World";ViewBag.Time = DateTime.Now.ToShortTimeString();return View("DebugData");}public ActionResult List(){return View();}}

创建一个自定义IView,在项目中新建一个Infrastructure的文件夹,创建一个DebugDataView.cs的新的类文件,如下图所示:

namespace WebApplication1.Infrastructure
{public class DebugDataView : IView{public void Render(ViewContext viewContext, TextWriter writer){Write(writer, "---Routing Data---");foreach (string key in viewContext.RouteData.Values.Keys){Write(writer, "key:{0},value:{1}", key, viewContext.RouteData.Values[key]);}Write(writer, "---View Data---");foreach (string key in viewContext.ViewData.Keys){Write(writer,"key:{0},value:{1}",key,viewContext.ViewData[key]);}}private void Write(TextWriter writer, string template, params object[] values){writer.Write(string.Format(template,values) + "<p>");}}
}

视图引擎的目的是产生一个ViewEngineResult对象,它或者包含一个IView,或者是一个用于搜索适当视图的位置列表。现在已经有了IView的实现,于是可以创建视图引擎,在Infrastructure的文件夹下新建一个DebugDataViewEngine.cs的类文件,代码如下图所示:

namespace WebApplication1.Infrastructure
{public class DebugDataViewEngine : IViewEngine{public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache){return new ViewEngineResult(new string[] { "No View (Debug Data View Engine)" });}public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache){if (viewName == "DebugData"){return new ViewEngineResult(new DebugDataView(), this);}else{return new ViewEngineResult(new string[] { "No view (Debug Data View Engine)" });}}public void ReleaseView(ControllerContext controllerContext, IView view){}}
}

注册自定义视图引擎

视图引擎需要在 Global.asax 的 Application_Start 方法中进行注册,代码如下图所示:

ViewEngines.Engines.Add(new DebugDataViewEngine());

静态的ViewEngine.Engines集合含有一组在应用程序中安装的视图引擎。MVC框架支持在一个单一的应用程序中安装多个引擎。当处理一个ViewResult时,动作调用器获取这组已经安装的视图引擎,并依次调用它们的FindView方法。

一旦动作调用接收到一个含有IView方法的ViewEngineResult对象。便会停止调用FindView方法。如果有两个或者多个引擎能够对同视图的请求进行服务,这意味着在ViewEngines.Engines集合中添加引擎的顺序是很重要的,代码如下图所示:

ViewEngines.Engines.Insert(0,new DebugDataViewEngine());

测试视图引擎

运行程序,效果如下图所示:

如果导航到/Home/List,该动作方法调用View方法请求其默认视图,这是一个我们不支持的视图,就会报错,如下图所示:

从上述报错来看,Razor和ASPX视图也在列表中,可以清除其他类型的视图引擎, 代码如下:

ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new DebugDataViewEngine());

理解Razor视图渲染

Razor视图引擎会编译应用程序中的视图,以改善性能。视图会被转化成C#类,然后被编译。这是在视图中能够如此方便地包含C#代码片段的原因。

程序启动前,视图不会被编译。只有程序启动后才能触发视图编译过程。出于方便,会将视图文件生成的类写成磁盘上的C#代码,然后进行编译,这意味着能够看到一个视图的C#语句。这些语句可以在C盘的某些隐蔽的目录下被找到,并且文件名和它们所包含的类名不对应。


配置视图搜索位置

在查找视图时,Razor视图引擎遵循MVC框架早期版本建立起来的约定。例如,如果请求与Home控制器先关的Index视图时,Razor会审查一下视图列表:

/Views/Home/Index.cshtml

/Views/Home/Index.vbhtml

/Views/Shared/Index.cshtml

/Views/Shared/Index.vbhtml

Razor实际上不会在磁盘上查找这些视图文件,因为它们还没有被编译成C#文件。Razor查找的是表示这些视图的编译类。.cshtml文件是含有C#语句的模板,而.vbhtml文件含有Visual Basic语句。

Razor视图引擎搜索属性
属性 描述 默认值

ViewLoactionFormats

MasterLocationFormatsPartiaView

LocationFormats

查找视图、分部视图、以及布局的位置

~/ Views / {1} / {0}.cshtml,

~/ Views/ {1} / {0}.vbhtml,

~/ Views/ Shared / {0}.cshtml,

~/ Views/ Shared/ {0}.vbhtml,

AreaViewLocationFormats

AreaMasterLocationFormats

AreaPartialViewLocationForms

为一个区域查找视图、分部视图,以及布局位置

~/ Areas / {2} / Views / {1} / {0}.cshtml,

~/ Areas / {2} / Views / {1} / {0}.vbhtml,

~/ Areas / {2} / Views / Shared / {0}.cshtml,

~/ Areas / {2} / Views / Shared / {0}.vbhtml

这些属性是在Razor之前引入的,这是每组三个属性具有相同值的原因。每个属性都是一个字符串数组,它们是用合成字符串格式化符号来表示的。以下是与占位符对应的参数值:

{0} 表示视图名。

{1} 表示控制器名。

{2} 表示区域名。

通过创建一个RazorViewEngine子类,可以改变Razor搜索的视图文件。 在Infrastructrue文件夹中,创建了一个名称为CustomerLocationViewEngine的视图引擎,代码如下图所示:

    public class CustomLocationViewEngine:RazorViewEngine{public CustomLocationViewEngine(){ViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml","~/Views/Common/{0}.cshtml"};}}

以上代码对 ViewLoactionFormats设置了一个新值。新数组只包含用于.cshtml文件的条目。此外,已经将查找共享视图的位置修改为了View/Common,而不是Views/Shared。并且在Global.asax的Application_Start方法中,注册此引擎:

ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomLocationViewEngine());

最后创建/View/Common文件夹,并添加一个名称为List.cshtml的视图文件。 代码如下图所示:

@{Layout = null;
}<!DOCTYPE html><html>
<head><meta name="viewport" content="width=device-width" /><title>List</title>
</head>
<body><div> <h3>This is the /Views/Common/List.cshtml View</h3></div>
</body>
</html>

启动程序,并导航到/Home/List时,执行效果如下图所示:


使用分段

Razor引擎支持分段的概念,Razor分段能够灵活的控制将视图的哪一个部分插入到布局之中,以及把他们插入到哪里。

下面对/Views/Home/Index.cshtml文件进行编辑,如下图所示:


@model string[]
@{Layout = "~/Views/Shared/_Layout.cshtml";
}@section Header{<div class="view">@foreach (string str in new[] {"Home","List","Edit" }){@Html.ActionLink(str,str,null,new { style = "margin:5px"})}</div>
}<div class="view">This is a lists of fruit names@foreach (string name in Model){<span><b>@name</b></span>}
</div>@section Footer
{<div class="view">This is the footer</div>
}

在上图代码中,创建了名称为“Header” 和 “Footer”的分段。分段的内容可以混用HTML标记和Razor标签。你可以在布局中使用@RenderSection 辅助器方法来指定分段要插入的位置。修改Views/Shared/_Layout.cshtml文件,代码如下图所示:

<!DOCTYPE html><html>
<head><meta name="viewport" content="width=device-width" /><style type="text/css">div.layout {background-color:lightgray;}div.view {border:thin solid black;margin:10px 0;}</style><title>@ViewBag.Title</title>
</head>
<body>@RenderSection("Header")<div class="layout">This is part of the layout</div>@RenderBody()<div class="layout">This is part of Layout</div>@RenderSection("Footer")<div class="layout">This is part of Layout</div>
</body>
</html>

在Razor对布局进行解析时,RenderSection辅助器方法会显示视图中指定名称的分段内容。视图中未包含在分段中的内容会插入布局中使用RenderBody辅助器方法。 运行程序,结果如下图所示:

注意:一个视图只能定义在布局中被引用的分段。如果视图在视图布局中午没有对应的@RendSection辅助器调用分段,MVC框架会报错。

一般情况下,不要分段与视图的其余的部分混杂在一起。其约定是,在视图的开始或者结尾部分定义分段,以便更容易看到哪些内容区域被处理成分段,对于那些需要RenderBody辅助器捕捉的内容,可以把这些定义成一个独立的分段,如下图所示:

@model string[]
@{Layout = "~/Views/Shared/_Layout.cshtml";
}@section Header{<div class="view">@foreach (string str in new[] {"Home","List","Edit" }){@Html.ActionLink(str,str,null,new { style = "margin:5px"})}</div>
}@section Body
{<div class="view">This is a lists of fruit names@foreach (string name in Model){<span><b>@name</b></span>}</div>
}@section Footer
{<div class="view">This is the footer</div>
}

以上这种办法有利于建立更清晰的视图,并减少了RenderBody捕捉无关内容的情况。为了使用这种方法,我们得用Rend而Section("Body")替换对RenderBody辅助器的调用。 代码如下图所示:

<!DOCTYPE html><html>
<head><meta name="viewport" content="width=device-width" /><style type="text/css">div.layout {background-color:lightgray;}div.view {border:thin solid black;margin:10px 0;}</style><title>@ViewBag.Title</title>
</head>
<body>@RenderSection("Header")<div class="layout">This is part of the layout</div>@RenderSection("Body")<div class="layout">This is part of Layout</div>@RenderSection("Footer")<div class="layout">This is part of Layout</div>
</body>
</html>

对分段进行测试

检查一个视图是否已经定义了布局中的一个特定的分段,可以使用如下代码,修改了_layout.cshtml:

@if (IsSectionDefined("Footer"))
{@RenderSection("Footer")
}
else
{<h4> This is the deafault footer </ht>
}

渲染可选分段

默认情况下,视图中必须含有布局中的RendSection的所有分段。如果缺少分段,MVC框架将会报错,修改_layout.cshtml代码并运行,结果如下图所示:

定义可选分段,只需加上一个false值即可:如果视图定义了它,其内容将被插入到结果中,否则也不会抛出异常,代码如下图所示:

 @RenderSection("scripts",false)

使用分部视图

通常需要在应用程序中多个不同的地方,使用同样的Razor标签 和 HTML 标记片段。采取的办法不是重复这些标记,而是采用分部视图(Partial View).

新建一个名称为MyPartial.cshtml分部视图,代码如下图所示:

<div>This is the massage from the partial view.@Html.ActionLink("This is a link to the Index action", "Index");
</div>

修改List.cshtml页面,代码如下图所示:


@{Layout = null;
}<!DOCTYPE html><html>
<head><meta name="viewport" content="width=device-width" /><title>List</title>
</head>
<body><div> <h3>This is the /Views/Common/List.cshtml View</h3></div>
</body>
</html>
@Html.Partial("MyPartial")

提示:Razor视图引擎对分部视图的查找方式,于规则视图相同 (即在~/Views/<controller> 和 ~/Views/Shared文件夹中进行查找)。这意味着,可以创建控制器专用的的特殊版本的分部视图,它会覆盖Shared文件夹下进行查找。

运行程序,效果如下:


使用强类型分部视图

可以创建强类型分部视图,然后在渲染这个分部视图时,传递要使用的视图模型对象。

新建一个名为MyStrongTypedPartial.cshtml的分部视图,代码如下图所示:

@model IEnumerable<string><div>This is message from the partial view.<ul>@foreach (string str in Model){<li>@str</li>}</ul>
</div>

并修改/View/Common/List.cshtml文件,代码如下所示:


@{Layout = null;
}<!DOCTYPE html><html>
<head><meta name="viewport" content="width=device-width" /><title>List</title>
</head>
<body><div> <h3>This is the /Views/Common/List.cshtml View</h3></div>
</body>
</html>
@Html.Partial("MyStronglyTypedPartial",new[] {"Apple","Orange","Pear" })

运行效果如下图所示:


使用子动作

子动作是通过视同调用的动作方法。当你希望将某种控制器逻辑用于应用程序的多个地方时,子动作可以让你避免重复的控制器逻辑。子动作和动作之间的关系,如同分部视图和视图的关系一样。无论何时,当希望显示某些数据驱动的“小部件”,这些“小部件”要出现在多个页面上,而且含有与主动作无关的数据时,你可能就会希望使用子动作。

创建一子动作,代码如下图所示:

        [ChildActionOnly]public ActionResult Time(){return PartialView(DateTime.Now);}

新增Time.cshtml页面,代码如下图所示:

@model DateTime<p> The time is @Model.ToShortDateString()</p>

启动程序,再次导航到/Home/List,效果如下图所示:

通过提供一个匿名类型对象,其属性对应于子动作方法的参数名,可以将参数传递给动作方法,代码如下图所示:

        [ChildActionOnly]public ActionResult Time(DateTime time){return PartialView(DateTime.Now);}
@Html.Action("Time",new { time = DateTime.Now})

精通ASP.NET MVC ——视图相关推荐

  1. 【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(7.2) 模块管理,模块的添加、修改、删除...

    索引 [无私分享:从入门到精通ASP.NET MVC]从0开始,一起搭框架.做项目 目录索引 简述 今天我们来做模块管理的 添加.修改.删除 项目准备 我们用的工具是:VS 2013 + SqlSer ...

  2. 【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(8) 权限管理,自定义权限,扩展权限...

    索引 [无私分享:从入门到精通ASP.NET MVC]从0开始,一起搭框架.做项目 目录索引 简述 今天我们来做权限的管理,这篇比较多 希望新手朋友慢慢消化 项目准备 我们用的工具是:VS 2013 ...

  3. 《精通 ASP.NET MVC 4》----1.3 ASP.NET MVC的关键优点

    本节书摘来自异步社区<精通 ASP.NET MVC 4>一书中的第1章,第1.3节,作者: [美]Adam Freeman ,译者: 李萍 , 徐燕萍 , 林逸 , 更多章节内容可以访问云 ...

  4. 《精通 ASP.NET MVC 3 框架(第三版)》----第2章 准备工作 2.1 准备工作站

    本节书摘来自异步社区<精通 ASP.NET MVC 3 框架(第三版)>一书中的第2章,第1节,作者: [美]Adam Freeman , Steven Standerson,译者: 林逸 ...

  5. 《精通 ASP.NET MVC 5》----1.8 本书所需的软件

    本节书摘来自异步社区<精通 ASP.NET MVC 5>一书中的第1章,第1.8节,作者: [美]Adam Freeman(弗瑞曼 A.),译者: 张成彬 , 徐燕萍 , 李萍 , 林逸 ...

  6. 【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(5.5) 登录功能的实现,完善登录功能...

    索引 [无私分享:从入门到精通ASP.NET MVC]从0开始,一起搭框架.做项目 目录索引 简述 今天我们来完善我们的登录功能 项目准备 我们用的工具是:VS 2013 + SqlServer 20 ...

  7. ASP.NET MVC 视图(四)

    ASP.NET MVC 视图(四) 前言 上篇对于利用IoC框架对视图的实现进行依赖注入,最后还简单的介绍一下自定义的视图辅助器是怎么定义和使用的,对于Razor语法的细节和辅助器的使用下篇会说讲到, ...

  8. 《精通 ASP.NET MVC 5》----1.5 本书的结构

    本节书摘来自异步社区<精通 ASP.NET MVC 5>一书中的第1章,第1.5节,作者: [美]Adam Freeman(弗瑞曼 A.),译者: 张成彬 , 徐燕萍 , 李萍 , 林逸 ...

  9. [翻译]通过调用多个动作创建ASP.NET MVC视图

    在ASP.NET MVC中,创建视图最典型的方式是调用一个action方法,它使用模型准备视图数据.action方法然后调用控制器的视图方法创建视图.然而,你可能想要调用不同的动作方法创建视图的不同部 ...

最新文章

  1. 网站故障排查常用命令
  2. 编辑距离及最小编辑距离算法(转)----动态规划
  3. AcWing1072. 树的最长路径(树形DP)题解
  4. 看涨戴尔科技的八大原因
  5. csv文件 java_Java生成CSV文件实例详解
  6. 开源医学图像处理平台NiftyNet介绍
  7. 洋葱头动态鼠标指针绿色版
  8. python基础函数应用_python基础之函数的应用
  9. java语言使用tess4j识别苏康码图片
  10. 南京中北学院荣跃计算机,南京师范大学中北学院来我院交流调研
  11. 虚假IP地址攻击如何溯源?
  12. Java中的BigDecimal类你了解多少?
  13. OSChina 周二乱弹 —— 将娱乐进行到底
  14. qt样式表设置边框_QT样式表
  15. (APIO)烟火表演
  16. 安装SQL Server 2000时出现“以前的某个程序安装已在安装……”
  17. r8169驱动源码阅读记录
  18. 优锘科技:扒一扒图化资源申请之三生三世那点事儿
  19. HM编码器代码阅读(20)——与变换量化有关的其他知识
  20. Ant Design Pro 菜单icon图标修改或新增

热门文章

  1. 为什么公司要努力发展数字化战略
  2. 使用 React和webpack开发和打包发布
  3. python_L2_operator
  4. 【Android开发】之Fragment与Acitvity通信
  5. Oracle 统计信息备份/表分析
  6. python commands_Windows环境下使用python的commands.getstatusoutput
  7. 把 Rational Rose 的图表保存为图片文件
  8. android代码生成excel,AndroidExcel
  9. python单词首字母大写_在Python中将每个单词的首字母大写
  10. 检查字符串是否包含数字的Python程序