




















在讲解自定义视图引擎之前,我们先来了解一下其中涉及到的一些对象类型。
首先来看IViewEngine接口类型的定义:
代码1-1
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);
}
在代码1-1中我们可以看到IViewEngine接口类型中定义了三个方法,第一个FindPartialView()方法中第一个参数是控制器上下文类型,里面包含着ViewData、ViewBag一些信息等,第二个字符串类型的参数表示为分部视图的名称,第三个参数是布尔值类型的参数表示是否缓存当前信息。
FindView()方法和FindPartialView()方法相似,只是多了一个viewName参数,表示视图名称。
这里实际的实现都是根据不同的视图引擎类型在这两个方法中返回对应视图引擎类型的IView【视图处理类型】,这个部分内容会在一下个篇幅中讲解到。
ReleaseView()方法则是用来释放IView中处理视图的资源。
ViewEngineResult类型是封装着IViewEngine类型和IView类型的一个操作返回类型,上面的两个方法的返回类型都是ViewEngineResult类型。
代码1-2
public class ViewEngineResult
{
public ViewEngineResult(IEnumerable<string> searchedLocations);
public ViewEngineResult(IView view, IViewEngine viewEngine);
public IEnumerable<string> SearchedLocations { get; }
public IView View { get; }
public IViewEngine ViewEngine { get; }
}
在代码1-2中我们可以看到ViewEngineResult类型的两个构造函数,第一个可枚举字符串类型表示的是搜索视图位置地址的这么一个集合,第二个不用说了就是对象封装了。
我们再来看一下IView的定义:
代码1-3
public interface IView
{
// 摘要:
// 使用指定的编写器对象来呈现指定的视图上下文。
//
// 参数:
// viewContext:
// 视图上下文。
//
// writer:
// 编写器对象。
void Render(ViewContext viewContext, TextWriter writer);
}
IView类型的在我的理解就是视图处理类型,它只是代表着一种类型的视图,比如说Razor视图是cshtml格式的文件,对应的IView就是RazorView这个处理类型,这个下篇会讲到。
先来看一下下面所要讲解的示例的示意图:
图1

大概流程是这样的,首先在我们控制器方法返回ViewResult的时候,ViewResult会从系统的IViewEngine集合中中读取IViewEngine,并且执行每个IViewEngine的FindView【假使是视图】,执行中某一个IViewEngine有返回ViewEngineResult类型的话则会停下向下执行,而是执行ViewEngineResult类型中的IView的Render()方法。最后视图的呈现则不是MVC部分负责的了下一篇中会讲解。现在我们来看一下示例。
首先是自定义的IViewEngine:
代码1-4
using System.Web.Mvc;
using MvcApplication.CustomView;
namespace MvcApplication.CustomViewEngine
{
public class MyCustomViewEngine : IViewEngine
{
public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
return new ViewEngineResult(new string[] { " MyCustomView " });
}
public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
if (viewName == "MyCustomView")
{
return new ViewEngineResult(new MyCustomView(), this);
}
else
{
return new ViewEngineResult(new string[] { " MyCustomView " });
}
}
public void ReleaseView(ControllerContext controllerContext, IView view)
{
}
}
}
这里只是在FindView()方法中实现了一个基础的代码,如果是视图名称为"MyCustomView",则会返回MyCustomView类型的视图处理类型作为ViewEngineResult类型的构造函数参数来返回。
自定义的IView:
代码1-5
using System.Web.Mvc;
namespace MvcApplication.CustomView
{
public class MyCustomView:IView
{
public void Render(ViewContext viewContext, System.IO.TextWriter writer)
{
foreach (string key in viewContext.ViewData.Keys)
{
writer.Write("Key:" + key + ",Value:" + viewContext.ViewData[key] + ".<p/>");
}
}
}
}
代码1-5中的定义则是简单的向writer中写入ViewData的值,并且最后呈现在视图页面上。
最后我们要把自定义的视图引擎添加到系统的视图引擎集合中,在Global.asax文件的Application_Start()方法中。
代码1-6
ViewEngines.Engines.Insert(0, new CustomViewEngine.MyCustomViewEngine());
这种添加的方式不多说了,前面篇幅对于这种模式的已经说过了,让自定义的排在集合的第一位而已。
最后随意的在某个控制器方法中代码修改为如下代码:
代码1-7
public ActionResult CustomView()
{
this.ViewData.Add("DebugData", "Jinyuan");
this.ViewData.Add("DebugDate", "2014-01-01");
return View("MyCustomView");
}
图2

大家看过上个篇幅对试图引擎的执行过程有个大概的了解,而Razor视图引擎的执行则更为详细,不会像上篇这么粗糙,来看一下示意图,图1.
图1

图1中所示的是Razor视图引擎中的对象结构的继承关系,对于图1右边部分的意思则是表示视图引擎是从系统的ViewEngines.Engines也就是ViewEngineCollection集合类型中取出来的,在ViewEngineCollection下面部分则是表示Razor试图引擎的一个继承关系示意图。
图2

我们先来看红色指针所指部分的流程:
在我们使用ViewResult作为控制器方法的返回类型的时候,MVC框架会首先执行ViewResult中的FindView()方法,而在ViewResult的FindView()中并不是由它自身来执行查找视图的任务的,MVC框架很多管闲事,它会干扰进来。它会从系统的ViewEngines.Engines也就是ViewEngineCollection集合类型中取出来Razor试图引擎来执行查找视图的任务【图1中例举了Razor视图引擎的继承结构】。
随之由VirtualPathProviderViewEngine类型来执行查找视图的任务,实际在这里根据视图名称查找的并不是我们定义的xxxx.cshtml视图文件,而是由xxxx.cshtml文件编译成的cs文件,这些cs文件中表示对应视图的代码,这个在本篇的最后会有讲解,那么这些cs文件是在什么时候生成的呢?是在请求到达控制器的时候,好事的MVC框架就会把View文件夹下或者是对应区域的View文件夹下,关于这个控制器文件夹中的所有视图都会统统的编译生成为cs文件。
扯蛋扯远了回到主题,在VirtualPathProviderViewEngine类型找到视图后【cs文件】,便会让RazorViewEngine类型来生成视图处理类,也就是实现了IView的RazorView类型了并且封装到ViewEngineResult类型实例中。
下面我们就来说明下蓝色指针所指部分的流程:
这个时候MVC框架会调用ViewEngineResult类型中的View属性中的Render()方法,实则就是刚刚上面说的RazorView类型实例中的方法。
然后我们看图2,在Render()方法的执行过程中,首先是由BuildManagerWrapper类型根据视图的路径去读取文件并且动态编译过后返回视图cs文件中所表示的类型,这里BuildManagerWrapper类型是MVC框架的内部类型是实现了内部的IBuildManager接口,BuildManagerWrapper类型的意思就是对于动态编译功能的一个封装,实则就是调用System.Web.Compilation.BuildManager里的功能。
回到主题,类型生成好了过后是由DefaultViewPageActivator类型来生成视图所对应的C#类型【System.Web.Mvc.WebViewPage<dynamic>】,最后通过RazorView类型中的RenderView()方法来对刚刚我们生成好的视图对应的C#类型进行基础性的赋值,比如说从ViewContext类型参数中获取ViewData、ViewBag等等数据信息【ViewContext类型继承自ControllerContext,所以你懂的】进行赋值。
最后会被WebPageRenderingBase类型的对象来做渲染处理,这部分内容就不做阐述了。
以上就是多Razor视图引擎也就是ViewResult类型的处理过程,说的很粗糙大家见谅希望能对大家对于视图的理解上有所帮助。
下面来说一下由MVC框架对于视图文件编译后的cs文件,要眼见为实嘛对吧。
首先我们看一下某个视图的运行的结果,图3.
图3

而对应的视图代码【cshtml文件中的代码】,如代码1-1.
代码1-1
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
@foreach (var item in Model)
{
<h3>ID: @item.ID Name:@item.Name</h3>
}
那么在请求达到控制器过后编译后的视图cs文件存在哪了呢?
在系统的C:\Users\你系统的用户名\AppData\Local\Temp\Temporary ASP.NET Files中,当然了并不是在这个文件夹下面,而是在会这里又生成一些其它的乱七八糟名称的文件夹,找一个即可。
我就是在C:\Users\你系统的用户名\AppData\Local\Temp\Temporary ASP.NET Files\root\19537580\dfb4a136文件夹下找的,当然了cs文件的命名也不是对应着视图名称的,一般是由App_Web开头命名的。经过一番查找找到了对应代码1-1的cs文件了,来看一下,代码1-2
代码1-2
#pragma checksum "E:\JY\JY\Action\ASP.NET MVC\SystemCase\MyMvcApplication\MvcApplication\Views\iocdemo\Index.cshtml" "{406ea660-64cf-4c82-b6f0-42d48172a799}" "11002EF3288CEAD21A96AC68FD35C045"
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.1008
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace ASP {
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Helpers;
using System.Web.Security;
using System.Web.UI;
using System.Web.WebPages;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using System.Web.Mvc.Html;
using System.Web.Routing;
public class _Page_Views_iocdemo_Index_cshtml : System.Web.Mvc.WebViewPage<dynamic> {
#line hidden
public _Page_Views_iocdemo_Index_cshtml() {
}
protected ASP.global_asax ApplicationInstance {
get {
return ((ASP.global_asax)(Context.ApplicationInstance));
}
}
public override void Execute() {
#line 1 "E:\JY\JY\Action\ASP.NET MVC\SystemCase\MyMvcApplication\MvcApplication\Views\iocdemo\Index.cshtml"
ViewBag.Title = "Index";
#line default
#line hidden
WriteLiteral("\r\n<h2>Index</h2>\r\n");
#line 6 "E:\JY\JY\Action\ASP.NET MVC\SystemCase\MyMvcApplication\MvcApplication\Views\iocdemo\Index.cshtml"
foreach (var item in Model)
{
#line default
#line hidden
WriteLiteral(" <h3>ID: ");
#line 8 "E:\JY\JY\Action\ASP.NET MVC\SystemCase\MyMvcApplication\MvcApplication\Views\iocdemo\Index.cshtml"
Write(item.ID);
#line default
#line hidden
WriteLiteral(" Name:");
#line 8 "E:\JY\JY\Action\ASP.NET MVC\SystemCase\MyMvcApplication\MvcApplication\Views\iocdemo\Index.cshtml"
Write(item.Name);
#line default
#line hidden
WriteLiteral("</h3>\r\n");
#line 9 "E:\JY\JY\Action\ASP.NET MVC\SystemCase\MyMvcApplication\MvcApplication\Views\iocdemo\Index.cshtml"
}
#line default
#line hidden
}
}
}
想必看到这里大家清楚了吧,对于视图文件cshtml、vbhtml等等最后在运行时编译成的类型System.Web.Mvc.WebViewPag,至于类型后面的泛型类型是dynamic类型是对应于普通视图,而强类型视图编译后的类型就会将此处的dynamic类型替换成强类型视图的ViewModel类型了,最后说一下对于#line的意思可问度娘,是便于我们调试用的。
上篇对于Razor视图引擎和视图的类型做了大概的讲解,想必大家对视图的本身也有所了解,本篇将利用IoC框架对视图的实现进行依赖注入,在此过程过会让大家更了解的视图,最后还会简单的介绍一下自定义的视图辅助器是怎么定义和使用的。
首先我们来看一下要定义实现依赖注入的功能接口规范和默认实现,示例代码1-1.
代码1-1
using System.Web.Mvc;
using Ninject;
namespace MvcApplication.Models
{
public interface IStringManage
{
MvcHtmlString CombinationString(string strPar1, string strPar2);
}
public class DefaultStringManage : IStringManage
{
public MvcHtmlString CombinationString(string strPar1, string strPar2)
{
return new MvcHtmlString(strPar1 + strPar2);
}
}
}
在IStringManage类型中定义了CombinationString()方法,用于将两个字符串类型的数值拼接起来,DefaultStringManage类型就是默认实现了,这里就不多说了。
下面我们再来定义在编译时刻视图将要实现继承的类型,示例代码1-2
代码1-2
using System.Web.Mvc;
using Ninject;
namespace MvcApplication.Models
{
public abstract class StringManageView : WebViewPage
{
[Inject]
public IStringManage StringManage { get; set; }
}
}
这样的定义起初是不会有什么问题的,因为cshtml视图文件在编译时是继承自WebViewPage类型的,现在我们要让cshtml视图所继承的类型是StringManageView,所以必须让StringmanageView继承自WebViewPage,因为WebViewPage是抽象类型,而我们又不想实现什么所以要定义为抽象类型,在StringManageView类型中,我定义了IStringManage类型的属性StringMange,并且使用IoC框架中的Inject特性来描述它,使的在编译是可以通过IoC来实现属性的依赖注入。
下面我们来看一下视图代码,这里的视图代码还是引用前面篇幅使用最多的一个示例,代码1-3和代码1-4
代码1-3
public ActionResult Index(List<Product> model)
{
ViewBag.StrPar1 = "This is";
ViewBag.StrPar2 = "ViewIoCCase";
return View(model);
}
代码1-4
@inherits MvcApplication.Models.StringManageView
@{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
@foreach (var item in Model)
{
<h3>ID: @item.ID Name:@item.Name</h3>
}
<h2>@StringManage.CombinationString(ViewBag.StrPar1, ViewBag.StrPar2)</h2>
控制器方法部分的代码定义是没有问题的,在代码1-4,也就是Index视图的定义中通过@inherits指令来使视图文件在编译时继承自某个类型,以及在下面的使用中用到了StringManage属性,并且还调用了方法,这里看起来都没什么问题,但是放在这里用就有问题了,因为上面使用了foreach来遍历Model,在我们定义StringManageView的时候并没有对Model的类型做约束什么的,而控制器方法中也是需要将List<Product>类型传递到视图的,这里就引起了冲突,图1.
图1

遇到这种情况我们只需修改一下代码1-2中的定义,让Model类型是在编译时是可确定的而不是object类型,来看代码1-5
代码1-5
public abstract class StringManageView : WebViewPage<dynamic>
{
[Inject]
public IStringManage StringManage { get; set; }
}
对的,让StringManageView实现泛型的WebViewPage就可以了,上个篇幅中视图的基类也都是这样定义的,不是说上面代码1-2定义的就是错误的,定义的没有错,只是应用的视图不合适,因为Index视图本身需要对Model做一些操作,又不想因为为视图添加的依赖注入功能而修改所以才会引起的这个错误,如果放在普通的视图里使用那是没有问题的。
上面这些都定义好了,下面我们需要实现自定义的IDependencyResolver类型,目的在于绑定我们需要进行依赖注入的功能模块到IoC中,代码1-6.
代码1-6
using Ninject;
using System.Web.Mvc;
namespace MvcApplication.CustomDependencyResolver
{
public class NinjectDependencyResolver:IDependencyResolver
{
private IKernel Kernel;
public NinjectDependencyResolver()
{
Kernel = new StandardKernel();
AddBinding();
}
private void AddBinding()
{
Kernel.Bind<Models.IStringManage>().To<Models.DefaultStringManage>();
}
public object GetService(Type serviceType)
{
return this.Kernel.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return this.Kernel.GetAll(serviceType);
}
}
}
对于代码1-6就不作过多的解释了,在控制器激活部分都讲解过近乎类似的注入封装类型。
最后我们在Global.asax文件的Application_Start()方法中,将Model绑定器和NinjectDependencyResolver类型添加中MVC框架中,代码1-7
代码1-7
ModelBinders.Binders.Add(typeof(List<Product>), new CustomListModelBinder()); DependencyResolver.SetResolver(new CustomDependencyResolver.NinjectDependencyResolver());
最后看下结果,图2.
图2

实际上自定义视图辅助器就是扩展方法的定义,首先我们来看定义,实现的功能同代码1-1相同,代码2-1
using System.Web.Mvc;
namespace MvcApplication.CustomHtmlHelper
{
public static class MyCustomHtmlHelper
{
public static MvcHtmlString CombinationString(this HtmlHelper htmlHelper, string strPar1, string strPar2)
{
return new MvcHtmlString(strPar1 + strPar2);
}
}
}
代码2-1这样的一个类型也就是自定义视图辅助器了,当然了这只是一个简单的示例,现在我们需要把它在视图中使用起来,我们得先把这个自定义的视图辅助器所在的命名空间添加到Views文件中的Web.Config中,代码2-2.
代码2-2
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="MvcApplication.CustomHtmlHelper"/>
</namespaces>
</pages>
</system.web.webPages.razor>
然后是在视图中引用扩展方法所处的命名空间, 这样配置过后就可以在视图用运用了我们刚刚自定义的视图辅助器了,代码2-3.
代码2-3
@inherits MvcApplication.Models.StringManageView
@using MvcApplication.CustomHtmlHelper
@{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
@foreach (var item in Model)
{
<h3>ID: @item.ID Name:@item.Name</h3>
}
<h2>@StringManage.CombinationString(ViewBag.StrPar1, ViewBag.StrPar2)</h2>
@Html.CombinationString("This is a ","Case")
最后我们看一下结果如图3.
图3

分段的使用
在ASP.NET MVC框架的Razor引擎中有着分段的概念,分段什么意思呢?分段的意思就是把视图分成几个自定义的分段,并且交由布局来控制分段的显示。说是这么说,我们还是来看一看代码的定义,首先是使用了分段的视图代码的定义,示例代码1-1.
代码1-1
@{
ViewBag.Title = "ViewPartCase";
}
<h2>ViewPartCase</h2>
@section PartOne
{
<h1>这是使用分段的示例:分段一</h1>
}
@section PartTwo
{
<h1>这是使用分段的示例:分段二</h1>
}
@section PartThree
{
<h1>这是使用分段的示例:分段三</h1>
}
在代码1-1中,使用@section标间来创建分段,而后面紧跟着的就是自定义分段的名称,这个时候布局的代码如示例代码1-2.
代码1-2
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
</head>
<body>
@RenderBody()
</body>
</html>
这个时候的运行结果如图1。
图1

很明显的看到图1中所提示的错误信息,在布局中并未定义PartOne、PartTow、PartThree。
这样我们只能修改代码1-2,修改布局中的定义,修改后的示例代码1-3.
代码1-3
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
</head>
@RenderSection("PartOne")
@RenderSection("PartThree")
<body>
@RenderBody()
</body>
@RenderSection("PartTwo")
</html>
在代码1-3中@RenderSection标签用于显示指定的分段,参数为自定义的分段名称,这里大家要清楚一个概念,就是布局的定义是来引导视图分段的定义,只不过在本篇幅是为了让大家清楚的理解所以颠倒了顺序,实际运用中应该是先定义好布局中所要使用的分段,以及指示的分段名称,然后在视图的定义中如果要使用当前的布局则会根据当前布局中的指定的分段名称来定义内容。看下以上代码运行结果如图2.
图2

比如说现在我是先定义好的代码1-3,然后要我定义我要呈现的页面也就是我们的视图,像代码1-1那样定义就是正常的应用顺序,那我现在修改代码1-1,因为我不想我的视图中使用分段,示例代码1-4
代码1-4
@{
ViewBag.Title = "ViewPartCase";
}
<h2>ViewPartCase</h2>
正如大家所看到的,代码1-4也就是我修改后的视图,就是显示一个标题,不想有其他的东西,再次运行结果如图2.
图3

图3中明确的指示出了视图中并未定义名为PartOne的分段,所以才会导致这样的错误,就算定义了PartOne,到了PartThree还要定义,不然接着报错,这就是强制约束了,这样多不人道主义阿对吧。
面对这样的情况,我们有两种方式来处理这种情况,
第一种,使用IsSectionDefinde()方法,方法参数即为分段名称,以此判断视图中是否定义了此名称的分段,如果定义了则会返回true,否则返回false(下面代码会给出示例)。
第二种,使用@RenderSection()方法的重载版本,讲第二个参数设置为false,如果视图中包含了方法参数所指定的分段则会显示,没有则不会报出异常。
现在我们来看一下,修改布局代码1-3中的代码以此演示上面说到的两种情况,示例代码1-5.
代码1-5
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
</head>
@if (IsSectionDefined("PartOne"))
{
@RenderSection("PartOne")
}
@RenderSection("PartThree",false)
<body>
@RenderBody()
</body>
@RenderSection("PartTwo",false)
</html>
这个时候我们再来看一下结果图4,,
图4

分部视图的使用
对于分部视图的使用其实也就是跟视图一样的,但是分布视图跟视图的区别在于分部视图是禁用布局的,也就是说上面小节所说的分段在分部视图中使用是毫无意义的,下面我们就来看一下分部视图的定义
图5

图5中并没有创建强类型的分部视图,这样做的意义不大,在点击“添加”按钮后,所呈现的代码页面是个空的页面什么都没有,这里主要是给大家看一下分部视图在编译后类型所呈现的样子也就是跟普通的视图没什么区别,示例代码1-6.
代码1-6
public class _Page_Views_iocdemo_CustomPartialView_cshtml : System.Web.Mvc.WebViewPage<dynamic> {
#line hidden
public _Page_Views_iocdemo_CustomPartialView_cshtml() {
}
protected ASP.global_asax ApplicationInstance {
get {
return ((ASP.global_asax)(Context.ApplicationInstance));
}
}
public override void Execute() {
WriteLiteral("<h1>PartialView</h1>");
}
}
代码1-6所呈现的就如同在ASP.NET MVC 视图(二)中最后所示的示例那般,核心的地方是不会变的,我们还是来删除掉这个分部视图,重新添加一个同样命名的强类型视图,或者直接使用@model标签来引用一个类型作为视图的Model类型,示例代码1-7.
代码1-7
@model MvcApplication.Models.CustomerInfo
<h1>@Model.ID</h1>
<h2>@Model.Name</h2>
我们再看一下对于分部视图使用的视图的代码定义,示例代码1-8
代码1-8
@model IEnumerable<MvcApplication.Models.CustomerInfo>
@{
ViewBag.Title = "ViewPartCase";
}
<h2>
ViewPartCase</h2>
@foreach (var model in Model)
{
@Html.Partial("CustomPartialView",model)
}
最后的呈现的界面如图6.
图6

在视图中,我们可以体会得到Razor引擎给我们带来的便利,然后初学的时候总有一个写苦恼,因为在使用代码或者是标签的时候常常分不清在哪里使用”;”分号来结束代码,在Razor中提供了两种不同的方式来区分代码和标签,也就是代码段和代码块。
代码段
代码段就是一些视图辅助器或者是表达式,它们可以在一行中进行渲染,也可以与文本混合的,例如示例代码1-1.
代码1-1
<p>Action to Index:@Html.ActionLink("Index","Index")</p>
表达式跟在@之后,代码1-1将会渲染以下的输出结果:
<p>Action to Index: <a href="/home/Index">Index</a></p>
这里要说明就是这里的代码段必须是要返回标记代码给视图渲染,如果编写的代码段是返回void的,视图在执行的时候是会报错的。
代码块
代码块是一段包含代码的视图,它里面只包含代码,而不是代码和标记语言的混合,Razor中定义的代码块要求是使用”@{}”来包装,以”@{“开始,”}”结束。而且在代码块中的代码书写方式就和.cs文件中的C#类型代码一样,每行代码都必须以;结尾的。来看下示例代码1-2.
代码1-2
@{
ViewBag.Title = "代码块";
}
视图辅助器
这些辅助器都是扩展了ASP.NET MVC视图对象的HtmlHelper对象,用扩展方法的技术来实现这些辅助器,对于扩展方法可以看我前面写的《ASP.NET MVC学前篇之扩展方法、链式编程》篇幅,而在视图系列前面的篇幅也说到过视图文件会编译成.cs类文件并且是编译成类型对象,而我们使用的就是这个类型对象中的Html属性,它的属性类型就是HtmlHelper类型,对于自定义辅助器可以看这个视图系列的《ASP.NET MVC 视图(三)》篇幅,这里不多说了下面我们来看一下关于Input类型的一些视图辅助器,都是些基础知识,了解的大神们可以直接忽略掉了。
我们先来看一下Checkbox的视图辅助器。
对应的是HtmlHelper.CheckBox()和HtmlHelper. CheckBoxFor<TModel>(),第一个方法是个普通的类型,可自由的传递参数值,而第二种方法则是针对于强类型视图的辅助器方法,参数类型为lambda表达式,而表达式的参数类型就是强类型视图的Model类型了,以下所讲的这些视图辅助器几乎所有都是采用的这种编程模型,我们还是来看代码。
代码1-3ViewModel:
public class CustomerInfo
{
public string ID { get; set; }
public string Name { get; set; }
public bool Check { get; set; }
}
代码1-4View:
@model MvcApplication.Models.CustomerInfo
<h1>@Model.ID</h1>
<h2>@Model.Name</h2>
@Html.CheckBox("Check", Model.Check)
@Html.CheckBoxFor(model => model.Check)
在示例中Check值是为true的,我们看一下渲染后的效果图,以及辅助器所返回的Html代码,图1、图2
图1

图2

这里要说明的一点是,因为是为了方便演示,所以这里的视图是强类型视图,所以可以使用强类型的视图辅助器。
从图2中我们就可以看到两者的使用效果是相同的,而且CheckBox类型的辅助器是唯一的一个生成两个input元素的辅助器。
下面我们再来看Radiobutton辅助器
我先来演示一下普通的方式,也就是RadioButton()方法。还是接着上面的示例来看,
代码1-5
@model MvcApplication.Models.CustomerInfo
<h1>@Model.ID</h1>
<h2>@Model.Name</h2>
@Html.CheckBox("Check", Model.Check)
@Html.CheckBoxFor(model => model.Check)
@Html.RadioButton("Check",Model.Check,Model.Check)
@Html.RadioButton("Check", !Model.Check, !Model.Check)
这里的视图还是强类型视图,只不过是借用一下Model,我们来看一下结果图,
图3

在这里要说一下,Model里的Check属性值是为true的,所以在显示的时候第一个单选框被设置为了选中状态了。
图4

图4所示的就是辅助器生成返回的Html代码
下面我们再来看一下单选框强类型版本的辅助器,也就是对应着RadioButtonFor()扩展方法。
代码1-6
<div class=@Model.ID>
<p>
@Html.RadioButtonFor(model => model.Check, Model.Check, new { id = "radio1" })</p>
<p>
@Html.RadioButtonFor(model => model.Check, !Model.Check, new { id = "radio2" })</p>
</div>
在代码1-6中我们可以看到RadioButtonFor()方法中有三个参数,第一个为lambda表达式类型,这个参数表示着RadioButtonFor()扩展方法内部绑定值的类型,这里这样说可能有点不清楚后面我会给大家再演示一个示例,这样才会更清晰。
第二个参数则为对应第一个参数类型当中的值,如果此时的Model当中的值跟当前的参数匹配则会设置当前的单选框为选中状态。
第三个参数则是为了修改RadioButtonFor()扩展方法生成的Input元素中的属性值,示例中我们是修改了Input元素的Id的值。我们看一下渲染后的视图和Input元素代码。
图5

图6

下面我修改一下ViewModel,示例代码1-7.
代码1-7
public class CustomerInfo
{
public string ID { get; set; }
public string Name { get; set; }
public bool Check { get; set; }
public EnumCase EnumCase { get; set; }
}
public enum EnumCase
{
EnumOne=0,
EnumTwo=1,
EnumThree=2
}
看到代码1-7中的定义了吧,我在ViewModel中添置了一个示例的枚举类型,这样更直观,现在我们看一下视图中的代码,示例代码1-8
代码1-8
@model MvcApplication.Models.CustomerInfo
@using MvcApplication.Models
<h1>@Model.ID</h1>
<h2>@Model.Name</h2>
@Html.CheckBox("Check", Model.Check)
@Html.CheckBoxFor(model => model.Check)
<div class=@Model.ID>
<p>
@Html.RadioButtonFor(model => model.EnumCase, EnumCase.EnumOne, new { id = "radio1" })</p>
<p>
@Html.RadioButtonFor(model => model.EnumCase, EnumCase.EnumTwo, new { id = "radio2" })</p>
<p>
@Html.RadioButtonFor(model => model.EnumCase, EnumCase.EnumThree, new { id = "radio3" })</p>
</div>
传入到视图的ViewModel值如下代码1-9所示:
代码1-9
new CustomerInfo(){ ID="001",Name="张三", Check=true, EnumCase= EnumCase.EnumThree};
下面我们代码1-8视图渲染呈现后的页面,图7.
图7

最后附上Input类型的表格,用法上面所演示的都一样,唯一缺少的就是对于强类型的演示;不过不复杂了自己捣鼓一下也就行了,带点难度的上面都说明了。
|
Html元素 |
示例 |
|
Checkbox (复选框) |
Html.CheckBox(“myCheckBox”,false) 输出: <input id=”myCheckBox” name=”myCheckBox” type=”checkbox” value=”true” /> <input name=”myCheckBox” type=”hidden” value=”false”/> |
|
Hidden field (隐藏字段) |
Html.Hidden(“mHidden”,”val”) 输出: <input id=”myHidden” name=”myHidden” type=”hidden” value=”val” /> |
|
Radio button 单选框 |
Html.RadioButton(“myRadiobutton”,”val”,true) 输出: <input checked=”checked” id=”myRadiobutton” name=”myRadiobutton” type=”radio” value=”val” /> |
|
Password 密令字符 |
Html.Password(“myPassword”,”val”) 输出: <input id=”myPassword” name=”myPassword” type=”password” value=”val” /> |
|
TextBox 文本框 |
Html.TextBox(“myTextbox”,”val”) 输出: <input id=”myTextbox” name=”myTextbox” type=”text” value=”val” /> |
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。