• ASP.NET MVC5 基础系列(3)——视图


    一、视图约定

    当创建一个项目模版时,可以注意到,项目以一种非常具体的方式包含了一个结构化的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文件夹,选择添加控制器,命名如下图所示:

    点添加后,对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方法,选择添加视图,在弹出的窗口进行如下配置:

    点添加后,系统自动帮我们创建一个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运行效果如下:

    (一)使用表达式

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

    @model MvcApplication1.Models.Product
    
    @{
        ViewBag.Title = "Index";
    }
    
    现在的时间是: @DateTime.Now.ToShortTimeString()

    运行效果如下:

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

    @model MvcApplication1.Models.Product
    
    @{
        ViewBag.Title = "Index";
    }
    
    现在的时间是: @DateTime.Now.ToShortTimeString()
    
    <br/>@(DateTime.Now.Hour>22 ? "还早,再写一会吧!" : "该睡觉咯!")

    运行效果如下:

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

    (二)使用代码块

    和表达式的使用方式一样,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>
    }

    效果如下:

    由{}括起来的代码块内可以写任何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>
        } 
    }

    运行结果如下:

    (三)使用@:和text标签

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

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

    注意@符号的使用。上面代码运行效果如下:

    使用@:标签在代码块中输出一行不带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>
    }

    运行结果:

    五、简单分部视图

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

    文件命名为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())的关系一样。前者是返回内容,后者是输出内容。

    (一)带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行把这个数字显示出来。显示结果如下:

    (二)使用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。运行结果如下:

    (三)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函数,更新一次数据。

    这样就完成了无刷新更新局部页面数据。在视图里有多种方法可以 加载部分视图,包括: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。

  • 相关阅读:
    js node 节点 原生遍历 createNodeIterator
    nodejs fs copy本地文件src dst
    axios 请求常用组件,及其错误
    【IntelliJ IDEA学习之三】IntelliJ IDEA常用快捷键
    【IntelliJ IDEA学习之二】IntelliJ IDEA常用配置
    【IntelliJ IDEA学习之一】IntelliJ IDEA安装激活、VM参数
    【python学习案例】python判断自身是否正在运行
    【Linux脚本学习案例】shell脚本多通道并发执行存储过程
    【Activiti学习之四】Activiti API(三)
    【Activiti学习之三】Activiti API(二)
  • 原文地址:https://www.cnblogs.com/wyh19941210/p/8576410.html
Copyright © 2020-2023  润新知