ASP.NET MVC5 基础系列(3)——视图 (一)使用表达式 (二)使用代码块 (三)使用@:和text标签

一、视图约定

当创建一个项目模版时,可以注意到,项目以一种非常具体的方式包含了一个结构化的Views目录。在每一个控制器的View文件夹中,每一个操作方法都有一个同名的视图文件与其对应。(约定大于配置)这就提供了视图与操作方法关联的基础。

1 public ActionResult Index()
2 {
3      return View();  
4 }

视图选择逻辑在/Views/ControllerName目录(这里就是去掉Controller后缀的控制器名)下查找与操作方法同名的视图。此处选择的是/Views/Home/Index.cshtml。与ASP.NET MVC中的大部分方法一样,这一约定是可以重写的。想让Index操作方法渲染一个不同的视图,可以向其提供一个不同的视图名称,代码如下:

1 public ActionResult Index()
2 {
3      return View("NotIndex");  
4 }

对于上面的编码,操作方法依然在/Views/Home目录中查找视图,但选择的不再是Index.cshtml,而是NotIndex.cshtml。如果需要制定完全位于不同目录结构中的视图,编码如下:

1 public ActionResult Index()
2 {
3      return View("~/Views/Example/Index.cshtml");  
4 }

二、 强类型视图

假设需要编写一个显示Album实例列表的视图,一种方法是将专辑添加到ViewBag中,然后在视图中进行迭代。

 1         public ActionResult List()
 2         {
 3             var albums = new List<Album>();
 4             for (int i = 0; i < 10; i++)
 5             {
 6                 albums.Add(new Album { Title = "Product" + i });
 7             }
 8             ViewBag.Albums = albums;
 9             return View();
10         }

然后,再在视图中迭代显示,如下代码:

1 <ul>
2  @foreach (Album a in (ViewBag.Albums as IEnumerable<Album>))
3 {
4    <li>@a.Tilte</li>
5 }
6 </ul>

注意在枚举之前需要将动态的ViewBag.Albums转换为IEnumerable<Album>类型。为了使代码整洁,可以使用dynamic关键字,但是当访问每个Album对象的属性时,就不能再使用智能感知功能。

1 <ul>
2  @foreach (dynamic p in ViewBag.Albums)
3 {
4    <li>@p.Tilte</li>
5 }
6 </ul>

强类型视图既能获得dynamic的简洁语法,又能获得强类型和编译时检查的好处(比如正确的输入属性和方法名称)。强类型视图允许设置视图的模型类型。因此可以从控制器向视图传递一个在两端都是强类型的模型对象,从而获得智能感知、编译器检查等好处。在Controller方法中,可以通过向重载的View方法中传递模型实例来指定模型,代码如下:

1     public ActionResult List()
2     {
3         var Musics = new List<MusicModels>();
4         for (int i = 0; i < 10; i++)
5         {
6             Musics.Add(new MusicModels { MusicName = "MusicName" + i.ToString() });
7         }
8         return View(Musics);
9     }

下一步是告知视图哪种类型的模型正在使用@model声明。但要注意这里需要输入模型类型的完全限定类型名(名称空间和类型名称),如下所示

1 @model IEnumerable<MvcMusicStore.Models.MusicModels>
2 <ul>
3     @foreach(MvcMusicStore.Models.MusicModels music in Model)
4     <li>@music.SingerName</li>
5 </ul>

如果不想输入模型类型的完全限定类型名,可使用@using关键字,如下所示

1  @using MvcMusicStore.Models
2  @model IEnumerable<MusicModels>
3  <ul>
4      @foreach(MusicModels music in Model)
5      <li>@music.SingerName</li>
6  </ul>

对于在视图中经常使用的名称空间,好的方法是在Views目录下的web.config文件中声明:

<add namespace="MvcMusicStore.Models">

三、 理解ViewBag、ViewData和ViewDataDictionary

之前介绍了使用ViewBag从控制器向视图传递信息,然后介绍了传递强类型模型。现实中,这些都是通过ViewDataDictionary传递的。从技术的角度看,数据从控制器传送到视图是通过一个名为ViewData的ViewDataDictionary(这是一个特殊的字典类)。我们可以使用标准的字典语法设置或读取其中的值:

ViewData["CurrentTime"] = DateTime.Now;

尽管这种语法现在也能用,但是MVC3提供了更简单的语法,可以利用C#4的dynamic关键字。ViewBag是ViewData的动态封装器。这样我们就可以按照下面的方式来设置值:

ViewBag.CurrentTime = DateTime.Now;

 ViewBag.CurrentTime和ViewData["CurrentTime"] 起到了等同的作用。一般来说,大部分代码使用ViewBag,而不是ViewData,这两种语法并不存在技术上的差异,仅仅是因为ViewBag相对于字典语法而言看上去好看。

注意,ViewBag和ViewData的差异:

  • 只有当要访问的关键字是一个有效的C#标识符时,ViewBag才起作用。例如,如果在ViewData["Key With Spaces"]中存放一个值,那么就不用使用ViewBag访问,因为无法通过编译。
  • 动态值不能作为一个参数传递给扩展方法,因为C#编译器为了选择正确的扩展方法,在编译时必须知道每一个参数的真正类型。

四、Razor语法

在演示Razor语法的使用之前,我们需要做一些准备工作。

1.打开VS创建一个ASP.NET MVC空项目,很简单,就不具体演示了。

2.添加一个Model。在项目的Models文件夹中添加一个名为Product的类。代码如下:

namespace MvcApplication1.Models {
    public class Product {
        public int ProductID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public string Category { set; get; }
    }
}

3.添加一个Controller。右击项目中的Controllers文件夹,选择添加控制器,命名如下图所示:

ASP.NET MVC5 基础系列(3)——视图
(一)使用表达式
(二)使用代码块
(三)使用@:和text标签

点添加后,对ProdcutController中的代码进行如下编辑:

using System.Web.Mvc;
using MvcApplication1.Models; 

namespace MvcApplication1.Controllers
{
    public class ProductController : Controller
    {
        public ActionResult Index()
        {
            Product myProduct = new Product {
                ProductID = 1,
                Name = "苹果",
                Description = "又大又红的苹果",
                Category = "水果",
                Price = 5.9M
            };
            return View(myProduct); 
        }
    }
}

4.添加一个View。右击Index方法,选择添加视图,在弹出的窗口进行如下配置:
ASP.NET MVC5 基础系列(3)——视图
(一)使用表达式
(二)使用代码块
(三)使用@:和text标签

点添加后,系统自动帮我们创建一个Product文件夹和一个Index.cshtml文件,Index.cshtml内容如下:

@model MvcApplication1.Models.Product

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

5.修改默认路由。为了方便,我们应该让应用程序启动时直接导向我们需要的请求处理(此处是Product/Index)。打开Global.asax文件,找到注册路由RegisterRoutes方法下的routes.MapRoute方法,把controller的值改为“Product”,如下所示:

routes.MapRoute(
    "Default", // 路由名称
    "{controller}/{action}/{id}", // 带有参数的 URL
    new { controller = "Product", action = "Index", id = UrlParameter.Optional } // 参数默认值
);

使用Model对象介绍Razor语法,让我们从Index.cshtml文件的第一行开始:

@model MvcApplication1.Models.Product

Razor语句都是以@符号开始的每个视图都有自己的Model属性(通过@Model调用)。上面这句代码的意思是将本视图的Model属性的类型指向MvcApplication1.Models.Product类型,这就实现了强类型。强类型的好处之一是类型安全,如果写错了Model对象的某个成员名,编译器会报错;另一个好处是在VS中可以使用VS中的代码智能提示自动完成类型成员调用的代码编写。 当然这句代码不要程序也可以正常运行,只是给编写代码造成了一定的困难。

视图中的Model属性用于存放控制器(Controller)传递过来的model实例对象(本示例中ProductController通过“return View(myProduct)”传递给Index视图),下面的代码演示了如何调用该model对象:

@model MvcApplication1.Models.Product

@{
    ViewBag.Title = "Index";
}
<!-- 调用Product实例的Name属性 -->
<h2>名称:@Model.Name</h2>

注意,第一行代码用于声名Model属性类型用的是@model <Model类型名>(小写m),而调用控制器传递过来的Model对象用的是@Model.<属性名>(大写M)。按F5运行效果如下:
ASP.NET MVC5 基础系列(3)——视图
(一)使用表达式
(二)使用代码块
(三)使用@:和text标签

上面讲的使用Model对象是很常用的一种Razor代码。其实上面示例中的@Model.Name就是一个简单的表达式,表示向Web页面呈现Model.Name的文本值。Razor语法中的表达式除了可以使用Model对象,也可以使用几乎任何一个其他可访问权限范围内的对象,来向Web面面输出该对象成员的文本值。如下代码所示:

@model MvcApplication1.Models.Product

@{
    ViewBag.Title = "Index";
}

现在的时间是: @DateTime.Now.ToShortTimeString()

运行效果如下:
ASP.NET MVC5 基础系列(3)——视图
(一)使用表达式
(二)使用代码块
(三)使用@:和text标签

这种使用对象的简单表达式(@DateTime.Now.ToShortTimeString()和@Model.Name),在这我们不防称之为对象表达式。除了对象表达式,还可以是其他任意的有返回值的表达式,如条件表达式。如下面代码所示:

@model MvcApplication1.Models.Product

@{
    ViewBag.Title = "Index";
}

现在的时间是: @DateTime.Now.ToShortTimeString()

<br/>@(DateTime.Now.Hour>22 ? "还早,再写一会吧!" : "该睡觉咯!")

运行效果如下:
ASP.NET MVC5 基础系列(3)——视图
(一)使用表达式
(二)使用代码块
(三)使用@:和text标签

注意,一般使用非对象表达式时都需要用小括号括起来。

(二)使用代码块

和表达式的使用方式一样,Razor语法中也可以使用由{}括起来的单个C#过程控制代码块(如if、switch、for等)。使用方式如下:

@model MvcApplication1.Models.Product

@{
    ViewBag.Title = "Index";
}

@if (Model.Price > 5M) {
    string test = "买不起!";
    <p>@Model.Name <b>太贵了!</b> @test </p>
}

效果如下:
ASP.NET MVC5 基础系列(3)——视图
(一)使用表达式
(二)使用代码块
(三)使用@:和text标签

由{}括起来的代码块内可以写任何C#代码,也可以使用任何HTML标签。但需注意的是,当控制语句内只有一句代码时不能像写C#后台代码一样省略大括号。还有一种更常用的使用代码块的方式。你也可以通过以@{开始,以}闭合的方式来使用代码块,它可以把多个代码块放在一起,开成一个更大的代码块。如下代码所示:

@model MvcApplication1.Models.Product

@{
    ViewBag.Title = "Index";
}
@{
    if(Model.Category=="水果"){
        string test="是一种水果。";
        @Model.Name @test
    }
    if (Model.Price > 5M) {
        string test = "买不起!";
        <p>@Model.Name <b>太贵了!</b> @test </p>
    } 
}

运行结果如下:
ASP.NET MVC5 基础系列(3)——视图
(一)使用表达式
(二)使用代码块
(三)使用@:和text标签

(三)使用@:和text标签

我们注意到,在代码块中,要么是C#代码,要么是HTML标签,不能直接写纯文字,纯文字须包裹在HTML标签内。但如果需要在代码块中直接输出纯文字而不带HTML标签,则可以使用@:标签,在代码块中输出纯文本文字非常有用。如下代码所示:

@if (Model.Price > 5M) {
    @Model.Name@:太贵了 。
    <br />
    @: @@:后面可以是一行除@字符以外的任意文本,包括<、>和空格,怎么写的就怎么输出。
    <br />
    @: 如果要输出@符号,当@符号前后都有非敏感字符(如<、{、和空格等)时,可以直接使用@符号,否则需要使用两个@符号。
}

注意@符号的使用。上面代码运行效果如下:
ASP.NET MVC5 基础系列(3)——视图
(一)使用表达式
(二)使用代码块
(三)使用@:和text标签

使用@:标签在代码块中输出一行不带html标签的文本非常方便,但如果需要在代码块中输出续或不连续的多行纯文本,则使用text标签较为方便,如下代码所示:

@if (Model.Price > 5M) {
    <text>
    名称:<b>@Model.Name</b><br />
    分类:<b>@Model.Description</b><br />
    价钱:<b>@Model.Price</b><br />    
    <pre>
        测试行一: <a>aaaa</a>
        测试行二: @@ fda@aaa
    </pre>
    </text>
}

运行结果:
ASP.NET MVC5 基础系列(3)——视图
(一)使用表达式
(二)使用代码块
(三)使用@:和text标签

五、简单分部视图

创建一个PartialView,在解决方案资源管理器中右键点击Shared文件夹选择添加->MVC 5 分部页(Razor)。如下图所示:

ASP.NET MVC5 基础系列(3)——视图
(一)使用表达式
(二)使用代码块
(三)使用@:和text标签

文件命名为PartialPage.cshtml,写入如下代码:

<h2>This is a partial page.</h2>

这样我们就创建好了一个简单的分部视图,现在我们来创建一个Controller和View来调用它。

在Controllers文件夹下创建PartialViewController.cs并写入如下代码:

    using System.Web.Mvc;  
    namespace SlarkInc.Controllers  
    {  
        public class PartialViewController : Controller  
        {  
            public ActionResult Index()  
            {  
                return View();  
            }  
        }  
    }  

一个最简单的Controller,就是为了让大家好理解。右键点击上面的"Index"函数名,选择添加视图。系统会在~ViewsPartialView文件夹下创建Index.cshtml文件,在这个文件中写入如下代码:

    @{  
        Layout = null;  
    }  
    <h2>Before PartialView</h2>  
    <hr />  
    @Html.Partial("PartialPage")  
    <hr />  
    @{  
        Html.RenderPartial("PartialPage");  
    }  
    <hr />  
    <h2>After PartialView</h2>  

其显示效果如下图所示:

上面代码中第1-3行表示在分部视图中不用加载模板。<hr />是下图所示的分隔线。

从下面显示结果可以看出,PatialPage.cshtml中的内容被显示了两次,这对应两个不同的调用分部视图的函数。第6行Html.Partial函数的作用是返回所调用的PartialView中的内容。其所在的View会负责输出其返回的内容。而第9行的代码则是直接输出所调用的PartialView中的内容。

如果还是不清楚Partial和RenderPartial的关系,可以这样类比:比如我们有一个string叫s,Partial和RenderPartial的关系就相当于s.ToString()和Response.Write(s.ToString())的关系一样。前者是返回内容,后者是输出内容。

ASP.NET MVC5 基础系列(3)——视图
(一)使用表达式
(二)使用代码块
(三)使用@:和text标签

(一)带Model的分部视图

前面只是创建了一个静态分部视图,下面我们来把它改造一下来显示Model数据。修改~ViewsPartialViewIndex.cshtml文件,代码如下:

    @{  
        Layout = null;  
    }  
    <h2>Before PartialView</h2>  
    <hr />  
    @Html.Partial("PartialPage",1)  
    <hr />  
    @{  
        Html.RenderPartial("PartialPage",2);  
    }  
    <hr />  
    <h2>After PartialView</h2>  

上面的第6和9行,加入了第二个参数,是一个数字。这个数字就是我们要传给PartialView的Model。修改~ViewsSharedPartialView.cshtml文件,内容如下:

@model int
<h2>This is a partial page @Model.</h2>

第1行表示传入的model是int类型。第2行把这个数字显示出来。显示结果如下:

ASP.NET MVC5 基础系列(3)——视图
(一)使用表达式
(二)使用代码块
(三)使用@:和text标签

(二)使用ChildAction调用分部视图

前面调用PartialView的方式都是通过一个View来调用PartialView。下面我们来介绍通过View调用ChildAction来返回PartialView。

首先在PartialViewController.cs里面写一个ChildAction代码如下:

    [ChildActionOnly]  
    public PartialViewResult ChildAction(DateTime time)  
    {  
        string greetings = string.Empty;  
        if(time.Hour > 18)  
        {  
            greetings = "Good evening. Now is " + time.ToString("HH:mm:ss");  
        }  
        else if (time.Hour > 12)  
        {  
            greetings = "Good afternoon. Now is " + time.ToString("HH:mm:ss");  
        }  
        else  
        {  
            greetings = "Good morning. Now is " + time.ToString("HH:mm:ss");  
        }  
        return PartialView("ChildAction",greetings);  
    }  

第1行,在ChildAction函数的前面写上[ChildActionOnly]表示这个Action只能作为ChildAction使用。ChildAction返回partialView的好处就是在Action里可以做一些处理和控制。这里第4到16行就是根据获得的时间返回不同的问候语。第17行返回其对应的PartialView并传入greetings作为Model。右键点击ChildAction函数名选择创建视图,取名为ChildAction。写入如下代码:

@model string
<h2>@Model</h2>

这个PartialView很简单,就是把传入的Model显示出来。修改~ViewsPartialViewIndex.cshtml文件,代码如下:

    @{  
        Layout = null;  
    }  
    <h2>Before PartialView</h2>  
    <hr />  
    @Html.Action("ChildAction", new { time = DateTime.Now })  
    <hr />  
    @{  
        Html.RenderAction("ChildAction", new { time = DateTime.Now.AddHours(12) });  
    }  
    <hr />  
    <h2>After PartialView</h2>  

如图中黄色所示,调用ChildAction同样有两种方法,Html.Action和Html.RenderAction。它们的区别跟Partial和RenderPartial是一样的。这两个函数的第一个参数是要调用的ChildAction的名字,第二个参数是要传递的参数。参数是用匿名对象的方法创建的Object。运行结果如下:

ASP.NET MVC5 基础系列(3)——视图
(一)使用表达式
(二)使用代码块
(三)使用@:和text标签

(三)ajax无刷新更新分部视图

要通过ajax来调用ChildAction返回PartialView,首先要去掉ChildAction开头写的[ChildActionOnly]。因为这种调用方法不算ChildAction调用。然后修改~ViewsPartialViewIndex.cshtml文件,代码如下:

    @{  
        Layout = null;  
    }  
    <script src="~/Scripts/jquery-1.10.2.js"></script>  
    <h2>Before PartialView</h2>  
    <hr />  
    <div id="header"></div>  
    <hr />  
    <h2>After PartialView</h2>  
    <script>  
        setInterval(LoadAction, 1000);  
        function LoadAction()  
        {  
            var time = new Date();  
            $.ajax({  
                type: "POST",  
                url: '@Url.Action("ChildAction", "PartialView")',  
                data: { time: time.getHours() + ":" + time.getMinutes() + ":" + time.getSeconds()},  
                datatype: "json",  
                success: function (data) {  
                    $('#header').html(data);  
                },  
                error: function (XMLHttpRequest, textStatus, errorThrown) {  
                    alert(errorThrown);  
                }  
      
            });  
        }  
    </script>  
显示的结果如下图所示,问候语和时间会每秒更新并且页面不刷新。上面代码中15-27行用到了Jquery的ajax方法获取数据。第17行url的值是ChildAction对应的路由。第18行传递的数据是名为time的当前时间。第21行,如果成功获取数据则将数据显示出来。第11行,通过SetInterval函数每秒调用一次LoadAction函数,更新一次数据。

ASP.NET MVC5 基础系列(3)——视图
(一)使用表达式
(二)使用代码块
(三)使用@:和text标签

这样就完成了无刷新更新局部页面数据。在视图里有多种方法可以 加载部分视图,包括:Partial()  Action()  RenderPartial()  RenderAction()  RenderPage() 方法。以下是这些方法的差别:

(四)Partial 与 RenderPartial 方法

1. Razor 语法:@Html.Partial() 与 @{Html.RenderPartial();}

2. 区别:Partial 可以直接输出内容,它内部是 将 html 内容转换为 string 字符(MVCHtmlString),然后缓存起来, 最后在一次性输出到页面。显然,这个转换的过程,会降低效率,所以通常使用 RenderPartial 代替。

(五)RenderPartial 与 RenderAction 方法

1. Razor 语法:@{Html.RenderPartial();}  与 @{Html.RenderAction();}

2. 区别RenderPartial 不需要创建 Controller 的 Action ,而 RenderAction 需要在 Controller 创建要加载的 Action。

   RenderAction 会先去调用 Contorller 的 Action ,最后再 呈现视图,所以这里 页面会在 发起一个链接。

   如果这个部分视图只是一些简单 的 html 代码,请使用 RenderPartial。 但如果这个部分视图 除了有 html 代码外,还需要通过读取数据库里的数据 来渲染,就必须使用 RenderAction 了,因为它可以在 Action 里调用 Model里的方法读取数据库,渲染视图后在呈现,而 RenderPartial 没有 Action,所以无法做到。

(六)RenderAction 与 Action

1. Razor 语法:@{Html.RenderAction();}  与 @Html.Action();

2. 区别:Action 也是直接输出,和 Partial 一样,也存在一个转换的过程。不如 RenderAction 直接输出到当前HttpContext 的效率高。

(七)RenderPage 与 RenderPartial 方法

1. Razor 语法:@{Html.RenderPartial();}  与 @RenderPage()

2. 区别:也可以使用 RenderPage 来呈现部分,但它不能使用 原来视图的 Model 和 ViewData ,只能通过参数来传递。而 RenderPartial 可以使用原来视图的 Model 和 ViewData。