我的MVC之旅(3)--------MVC Music Store 第三篇 Views and ViewModels [翻译]
前言
有网友说不要浪费时间了赶紧干点正事,也有人觉得还不错;我个人觉得看懂是一回事,但是能够写出来分享是另一回事,这样记忆深刻有助于加深自己的理解,而且可以帮助有些和我一样的初学者,遇到的问题,怎么解决等等.
好了,下面开始正题.上周做完第二篇控制器之后,接下来就是第三篇 视图和模型了,源文章链接:http://www.asp.net/mvc/tutorials/mvc-music-store/mvc-music-store-part-3
概述
本章通过简单的几个示例,介绍视图的使用方法以及控制器和视图之间的数据传递方法,简单而且直观,并且简单介绍了生成页面链接的方法.
内容
先来回顾一下:我们之前已经使用控制器返回String 类型的字符,这很好的让我们了解了控制器的工作原理,但是这不是真正的web应用程序.
我们想要一个更简单的方式返回HTML内容到浏览器--模版文件可以轻松地自定义HTML内容并发回浏览器,这就是视图.
1.添加第一个视图:
要应用视图,首先要改变HomeController 控制器的 Index方法的返回值为ActionResult,并且我们让他返回一个View();代码如下:
3 // GET: /Home/
4
5 public ActionResult Index()
6 {
7 return View();
8 }
上面的代码表示,我们想使用视图得到一个结果返回.
我们现在添加一个视图 到我们的项目里面,光标定位到Index()方法,右键单击选择添加视图:
添加视图对话框允许我们快速方便地生成视图文件,默认情况下,添加视图对话框已经为我们光标定位的控制器方法添加好了名称,我们不需要修改什么,直接点击确定,就会添加一个试图文件到工程目录:Views/Home/Index.cshtml.
视图文件的名字和目录非常重要,使用Mvc默认的命名规则,目录/View/Home 对应HomeController,文件Index.cshtml对应控制器方法:Index(). 使用这种命名规则,我们不用在Index()方法中显示指定视图文件,而是直接 return View().就像上面的HomeController.Index(),它将会自动引用View/Home/Index.cshtml.
添加完视图之后,默认会打开该视图文件,默认代码如下:
3 }
4
5 <h2>Index</h2>
该视图文件使用 Razor 语法 ,这比Asp.net Web Forms 中的 Web Forms视图引擎和上一版本的Asp.net mvc 更简洁.当然 WebForms视图引擎在Asp.net 3.0中仍然可用,但是很多开发者觉得Razor更加适用于Asp.net Mvc 开发.
前三行代码设置页面标题ViewBag.Title.我们等一下将看看它是怎么工作的,现在我们改一下页面的代码:
3 }
4
5 <h2>This is the Home Page</h2>
接下来运行程序看一下主页,会看到我们已经更新的文本:
现在,我们已经为HomeController.Index添加了视图Index.cshtml,并且运行了出来。下面我们即将看一下Mvc给我们提供的统一网站部局文件.
2.使用统一布局文件
很多网站的网页有一些全站公用的东西:导航,页脚,LOGO,CSS文件等.Razor 让我们很轻松地管理这些东西,使用_Layout.cshtml,工程已经为我们建立此文件,位于:/Views/Shared/_Layout.cshtml.
我们看一下布局文件:
3 <head>
4 <meta charset="utf-8" />
5 <title>@ViewBag.Title</title>
6 <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
7 <script src="@Url.Content("~/Scripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
8 <script src="@Url.Content("~/Scripts/modernizr-2.5.3.js")" type="text/javascript"></script>
9 </head>
10
11 <body>
12 @RenderBody()
13 </body>
14 </html>
很简单,仅包含一些CSS和常规脚本.我们的视图文件将会通过 @RenderBody()命令来展示,其他的公共内容都可以在这里添加,我们想让我们的MuvMusicStore用一个公共的页眉,上面包含首页链接和商店链接,所以,我们在@RenderBody()命令上面添加一些代码(黄色高亮部分),个人觉得这里和母版差不多.
添加完之后,先不要运行
3 <head>
4 <meta charset="utf-8" />
5 <title>@ViewBag.Title</title>
6 <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
7 <script src="@Url.Content("~/Scripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
8 <script src="@Url.Content("~/Scripts/modernizr-2.5.3.js")" type="text/javascript"></script>
9 </head>
10
11 <body>
12 <div id="header">
13 <h1>ASP.NET MVC MUSIC STORE</h1>
14 <ul id="navlist">
15 <li class="first"><a href="/"
16 id="current">Home</a></li>
17 <li><a
18 href="/Store/">Store</a></li>
19 </ul>
20 </div>
21 @RenderBody()
22 </body>
23 </html>
空项目模版进包含一些基本的css用于显示验证消息,本项目设计师添加了一些常用的Css和一些图片来控制我们的应用程序的外观,我们需要把这些文件同时添加进项目里(可以去这里下载)
下载完成解压之后,打开相关文件包,选择我们需要的文件添加到项目里:
现在,我们重新生成并刷新首页:
是不是很熟悉:这就是母版.
简单回顾一下:
- HomeCongroller.Index引用并展示/Views/Home/Index.cshtml ,尽管我们并没有显示指定视图文件地址而只是使用return View();但是因为我们使用Mvc默认命名规则,所以,这样并不会导致找不到文件.
- 首页通过视图文件展示一个简单的欢迎信息.
- 使用统一布局文件,让欢迎信息显示于全局统一风格的页面中。
3.使用模型传递数据到视图
只有html代码的静态页面不能满足我们的应用程序要求,我们需要的是一个能够和用户沟通的动态网站,我们需要把数据从控制器传递到视图文件.
在Model-view-controller模式里,Model表示 应用程序中的数据.Model一般对应数据库中的表,但这不是必须的.
返回一个ActionResult的控制器可以传递一个Model到视图,控制器可以打包所有需要的数据,生成输出并传递到目标视图文件用来得到Html输出给浏览器,这很简单,让我们看看怎么做.
首先我们需要建立一些Model类:Genres(流派),Albums(相册).右击/Models/文件夹,选择添加/类,命名为:Genre.cs,确定之后编辑器会打开Genre.cs文件,我们添加公共属性Name
3 using System.Collections.Generic;
4 using System.Linq;
5 using System.Web;
6
7 namespace MvcMusicStore.Models
8 {
9 public class Genre
10 {
11 public string Name { get; set; }
12 }
13 }
相同步骤创建Album.cs,有一个Title属性和 Genre属性:
3 using System.Collections.Generic;
4 using System.Linq;
5 using System.Web;
6
7 namespace MvcMusicStore.Models
8 {
9 public class Album
10 {
11 public string Title { get; set; }
12 public Genre Genre { get; set; }
13 }
14 }
现在,我们可以修改StoreController 使用视图从模型显示动态信息,我们给Albums一个和Request ID相同的名字(演示起见).
首先:打开StoreController.cs,修改Details 方法,让它显示一个相册的信息,引入MuvMusicStore.Models命名空间以便于使用相关模型类,修改Detail的返回值类型:string to ActionResult,修改方法逻辑:返回一个Album对象给View(稍后我们会讲如何从数据库检索数据), 但是现在我们将使用“模拟数据”.
给Details添加视图文件,不过这个步骤要复杂一点点,在添加视图对话框里我们要勾选“创建强类型视图” 然后在下面选择 Album (MvcMusicStore.Models),如果下拉框里面没有选项,请重新生成项目后重试,支持模版就用Empty ,勾选 引用脚本库,点击确定我们会看到生成的视图文件如下:
3
4 @{
5 ViewBag.Title = "Details";
6 }
7
8 <h2>Details</h2>
比我们刚才添加的HomeController.Index的视图多了第一行:它表明这个视图强制关联到 Album class,Razor引擎会识别传进来的Album对象,所以我们可以在视图编辑器中轻松访问 模型属性,并且写代码时候能够自动感应.
更新<h2>标签内容:
3
4 @{
5 ViewBag.Title = "Details";
6 }
7
8 <h2>Album: @Model.Title</h2>
9
当你输入 @Model 之后,智能感知便会出现所有的成员.
现在我们重新生成之后访问 /Store/Details/5 :
现在我们给Browse方法一个相应的修改:
更新StroeController.Browse方法:
3 {
4 var genreModel = new Genre { Name = genre };
5 return View(genreModel);
6 }
给Browse 添加视图,强类型关联到Genre
更新Browse.cshtml代码:
3 @{
4 ViewBag.Title = "Browse";
5 }
6 <h2>Browsing Genre: @Model.Name</h2>
重新生成项目并访问 /Store/Browse?Genre=Disco:
最后,我们给StoreController.Index一个稍微复杂的逻辑和视图来展示一个流派列表,我们使用一个Genres列表,而不是单个对象:
StoreController.Index代码:
4 public ActionResult Index()
5 {
6 var genres = new List<Genre>
7 {
8 new Genre { Name = "Disco"},
9 new Genre { Name = "Jazz"},
10 new Genre { Name = "Rock"}
11 };
12 return View(genres);
13 }
给Index添加视图强类型管理到Genre,打开视图文件Models/Store/Index.cshtml,把第一行的接收参数生命更新:
3 @*@model MvcMusicStore.Models.Genre *@
4 @model IEnumerable<MvcMusicStore.Models.Genre>
@**@表示注释代码
第四行代码表示我们将接受多个Genre对象;
我们使用 IEnumerable<Genre>而不是 List<Genre>,是因为IEnumerable更为通用,我们可以传递任意实现IEnumerable接口的参数类型.
下面我们将遍历Genre对象:
3 @model IEnumerable<MvcMusicStore.Models.Genre>
4 @{
5 ViewBag.Title = "Store";
6 }
7 <h3>Browse Genres</h3>
8 <p>
9 Select from @Model.Count()
10 genres:
11 </p>
12 <ul>
13 @foreach (var genre in Model)
14 {
15 <li>@genre.Name</li>
16 }
17 </ul>
任然有非常强悍的智能感知:比如输入@Model会出来所有支持的成员;
循环内部,每个 var生命的genre 也可以智能感知所有的成员;
现在我们已经可以循环传入对象列表,并输出对象总数以及每个对象的信息,以后我们会生成编辑、查看详细信息和删除操作到每一项,现在我们先来看看简单列表:
重新生成项目,访问 /Store:
4.添加站内链接
刚才的 /Store页面展示了一组Genre对象的Name属性作为普通文本,我们给他们加上链接,通过点击访问相关的详细信息/Store/Browse,当我们点击 "Disco",能够跳转到/Store/Browse?genre=Disco,我们更新/Views/Store/Index.cshtml的代码:
3 @foreach (var genre in Model)
4 {
5 <li><a href="/Store/Browse?genre=@genre.Name">@genre.Name</a></li>
6 }
7 </ul>
这样可以实现,但是不是很好的方式,比如我们要更新Browse为BrowseDetail,那就需要检索整个项目来查找需要更新的地方.
另一个实现方式就是利用 HTML Helper 方法.Asp.net Mvc包含HTML Helper方法,在View编辑器中,我们可以使用它来完成相关的任务. Html.ActionLink()是一个特别有用的方法,使用它创建链接,维护路径,还是确保URL编码都比较简单,
Html.ActionLink()有几个重载允许我们传入需要的各种参数, 在示例中我们仅需要提供链接文本和目标Action,比如我们可以在Store Details 页面点击"Go to Store Index" 连接到"/Store"页面,用法如下:
链接到Browse页面需要一个参数,所以我们需要另外一个重载,它将支持三个参数:
- 链接文本
- 控制器名称
- 参数 包括参数名和参数值
下面看一下代码:
3 @model IEnumerable<MvcMusicStore.Models.Genre>
4 @{
5 ViewBag.Title = "Store";
6 }
7 <h3>Browse Genres</h3>
8 <p>
9 Select from @Model.Count()
10 genres:
11 </p>
12 <ul>
13 @foreach (var genre in Model)
14 {
15 <li>@Html.ActionLink(genre.Name,
16 "Browse", new { genre = genre.Name })</li>
17 }
18 </ul>
重新生成项目,访问 /Store/ :
查看源代码:
3 <li><a href="/store/Browse?genre=Disco">Disco</a></li>
4 <li><a href="/store/Browse?genre=Jazz">Jazz</a></li>
5 <li><a href="/store/Browse?genre=Rock">Rock</a></li>
6 </ul>
可以发现,解析出来的html代码和预期的相同.
个人感悟
通过这章,可以发现,在mvc中,业务逻辑和表示层分离的程度非常高,几乎可以同时开始编码,而不用担心其他问题.我们将在下一章介绍模型和数据访问.