ASP.NET MVC 音乐商店 - 3. 视图与模型
上一篇中有同学提到为什么不使用视图,而使用字符串,这一篇我们就开始使用视图来处理。
我们已经可以从控制器的 Action 中返回一个字符串,这可以帮助我们更好地理解 Controller 是如何工作的。但是对于创建一个 Web 程序来说还是不够的。下面我们使用更好的方法来生成 HTML,主要是通过模板来生成需要的 HTML,这就是视图所要做的。
增加视图模板
为了使用视图模板,我们需要将HomeController 中的 Index 这个 Action 的返回类型修改为 ActionResult,然后,让它像下面一样返回一个视图。
public class HomeController : Controller { // // GET: /Home/ public ActionResult Index() { return View(); }}
上面的修改表示我们将使用视图来替换掉原来的字符串,以便生成返回的结果。
现在为我们的项目增加一个视图,为达到这个目的,我们将光标移到 Index 方法内,然后,点击鼠标的右键,在右键菜单中选择“添加视图(D)…”,这样将会弹出增加视图的对话框。
添加视图的对话框允许我们快速,简单地创建一个视图模板,默认情况下,视图的名称使用当前 Action 的名字。因为我们是在 Index 这个 Aciton 上添加模板,所以添加视图对话框中,视图的名字就是 Index,我们不需要修改这个名字,点击添加。
在点击添加之后,Visual Studio 将会创建一个名为 Index.cshtml的视图模板,放置在 /Views/Home 目录中,如果没有这个目录,MVC 将会自动创建它。
Index.cshtml 所在文件夹的名称和位置是很重要的,它是根据ASP.NET MVC 的约定来指定的。目录名称 /Views/Home ,匹配的控制器就是 HomeController ,视图模板的名字 Index,匹配将要使用这个视图的 Action 方法的名字。
当使用默认的约定的时候,ASP.NET MVC 允许我们不用显式设置这些名字和位置,当我们的代码如下所示的时候,将会默认寻找 /Views/Home/Index.cshtml。
public class HomeController : Controller { // // GET: /Home/ public ActionResult Index() { return View(); }}
Visutal Studio 创建并打开了Index.cshtml 视图模板,其中的内容如下:
@{ ViewBag.Title = "Index"; } <h2>Index</h2>
视图使用了 Razor 语法,这比 Web Form 视图引擎的语法更加简单。
前三行使用 ViewBag.Title 设置了页面的标题,我们马上就可以看到这样做的效果,但是,首先,我们我们替换一下网页的内容,将 <h1> 标记中的内容修改为 This is the Home Page 。
@{ ViewBag.Title = "Index"; } <h2>This is the Home Page</h2>
现在,我们的首页应该变成下面的样子。
为页面的公共内容使用布局
大多数的网站在页面之间有许多共享的内容:导航,页首,页脚,公司的 Logo,样式表等等。Razor 引擎默认使用名为 _Layout.cshtml 的布局来自动化管理,它保存在 /Views/Shared 文件夹中。
打开之后,可以看到下列内容:
<!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.4.4.min.js")" type="text/javascript"></script>
</head>
<body>
@RenderBody()
</body>
</html>
来自内容视图中的内容,将会被通过@RenderBody() 来显示,任何出现在网页中的公共内容就加入到_Layout.cshtml 中,我们希望我们的MVC 音乐商店有一个公共的页首,其中含有链接到我们的首页和商店区域的链接,所以,我们将这些内容直接添加到这个布局中。
<!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.4.4.min.js")" type="text/javascript"></script>
</head>
<body>
<div id="header">
<h1>
ASP.NET MVC MUSIC STORE</h1>
<ul id="navlist">
<li class="first"><a href="/" id="current">Home</a></li>
<li><a href="/Store/">Store</a></li>
</ul>
</div>
@RenderBody()
</body>
</html>
更新样式表
在创建项目使用的空项目模板中,仅仅包含很简单的用来显示验证信息的样式。我们的设计师提供了一些额外的CSS 样式和图片来改进网站的观感,现在我们就使用它们。首先,到网站 mvcmusicstore.codeplex.com 下载 MvcMusicStore-v3.0.zip,这里面有一个文件夹 MvcMusicStore-Assets,将这个文件夹的Content 文件夹的内容复制到项目的 Content 文件夹中。
你会被询问是否需要覆盖存在的文件,选择是。
现在,网站的 Content 文件夹中的内容如下所示:
重新运行程序,现在的页面变成了这样。
我们回顾一下,什么发生了变化:HomeController 的 Index 的 Action 方法寻找并通过 /Views/Home/Index.cshtml 模板生成输出结果,代码中是通过 return View() 实现的,因为默认的命名约定,Index 这个 Action 方法将会默认使用 Index 视图输出。
而 Index 视图使用了我们的 _Layout.cshtml 模板,所以,欢迎信息被包含在标准的 HTML 布局中。
使用模型为视图传递信息
仅仅使用硬编码的 HTML 不能创建令人感兴趣的网站,创建动态网站,我们需要从控制器的 Action 传送信息给视图模板。
在 MVC 模式中,术语 Model 表示应用表现的数据,通常,模型对象用来表示数据库中保存在表中的数据,也不一定如此。
控制器的 Action 方法通过返回的 ActionResule 可以传送模型对象给视图。这就允许控制器可以将所有生成回应需要的数据打包,然会传送给视图模板,以便生成适当的 HTML 回应,在 Action 方法中可以很容易理解,让我们开始吧。
首先,我们将创建一些模型类来表示商店中的唱片类型和专辑类型,从创建类型 Genre 类开始,在项目中,右击模型 Models 文件夹,然后选择增加类选项,然后命名为 Genre.cs。
在新创建的类中增加一个属性。
public class Genre { public string Name { get; set; } }
注意:这里的 { get; set; } 是 C# 的自动属性特性,这使得我们不需要在创建属性的时候,先创建一个成员字段
现在,用同样的方法创建专辑类 Album,它有两个属性:Title 和 Genre .
public class Album { public string Title { get; set; } public Genre Genre { get; set; } }
现在,我们修改 StoreController 通过模型来使视图显示动态信息,为了演示方便,我们定义专辑基于一个唯一的标识 Id, 我们将在视图中显示这个标识。
我们从修改 Details 这个 Action 使得可以显示单个的专辑开始,在 StoreController.cs 的开始部分增加一些 using 语句来包含 MvcMusicStore.Models 命名空间,这使得我们不用总是输入这个命名空间。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using MvcMusicStore.Models;
然后,我们更新 Details Action ,使得返回 ActionResult 类型的结果而不是字符串,就像在 HomeController 中的 Index 方法中做得一样。
public ActionResult Details(int id)
现在,修改方法的处理逻辑,返回一个专辑对象到视图中,在这个项目最后,显示的数据将会来自数据库,现在我们仅仅填充一些数据而已。
public ActionResult Details(int id) { var album = new Album { Title = "Album " + id }; return View(album); }
如果你对 c# 不太熟悉,可能你会认为使用 var 定义变量使用了迟绑定,这是不正确的,C# 编译器使用赋予变量的值来推定变量的类型,所以,实际上变量的类型就是 Album 类型,因此不仅在编译时, Visual Studio 的代码编辑器中也会有类型支持。
下面创建一个使用专辑来生成 HTML 的模板,在这样做之前,我们需要编译项目,以便增加视图的对话框知道我们新创建的专辑类型。你可以通过菜单“生成”的“生成解决方案”来完成。
另外,也可以通过热键 Ctrl – Shift – B 来编译项目。
已经可以创建视图模板了,在 Details 方法中右键选择“增加视图…”
像以前一样,我们看到创建视图的对话框,不一样的是,我们要选中“创建强类型视图”,然后在下面的列表中选择“Album”类,这样视图将会期望得到一个 Album 类型的对象。
在点击增加之后,我们的视图模板 /Views/Store/Details.cshtml 被创建了,其中包含的如下的代码:
@model MvcMusicStore.Models.Album @{ ViewBag.Title = "Details"; } <h2>Details</h2>
注意第一行,表示视图使用强类型的 Album 类。Rozer 视图引擎理解传送来的 Album 对象,所以我们可以容易地访问模型的属性,在 Visual Studio 中得到智能感知的帮助。
更新 <h2> 标记,使得可以显示专辑的 Title 属性
@model MvcMusicStore.Models.Album @{ ViewBag.Title = "Details"; } <h2>Album: @Model.Title</h2>
注意,智能感知使得可以提示 Album 的属性和方法。
再次运行并访问 /Store/Details/5,可以得到下面的结果。
现在,我们继续修改 Browse 方法,更新方法返回 ActionResult 类型的结果,修改方法的处理,返回一个 Genre 类型的对象实例。
public ActionResult Browse(string genre) { var genreModel = new Genre { Name = genre }; return View(genreModel); }
在方法上右击,选择“增加视图…”,增加一个强类型的视图。
修改 <h2> 标记显示 Genre 的信息
@model MvcMusicStore.Models.Genre @{ ViewBag.Title = "Browse"; } <h2>Browsing Genre: @Model.Name</h2>
重新运行,访问 /Store/Browse?Genre=Disco,可以看到如下的显示
最后,将 Index 也修改为强类型的视图,显示所有唱片的类别,我们使用 Genre 的一个列表,而不是单个的 Genre 对象。
public ActionResult Index() { var genres = new List<Genre> { new Genre { Name = "Disco"}, new Genre { Name = "Jazz"}, new Genre { Name = "Rock"} }; return View(genres); }
创建一个强类型的视图
首先,我们将期望得到多个Genre 对象而不是一个,将第一行修改为如下内容。
@model IEnumerable<MvcMusicStore.Models.Genre>
这告诉视图引擎模式是一个包含多个Genre 对象的集合,我们使用IEnumerable<Genre> 而不是List<Genre>,因为这样更通用,可以允许我们在以后改变集合为任何实现IEnumerable 接口的集合。
现在,我们遍历集合中的Genre 对象进行处理。
@model IEnumerable<MvcMusicStore.Models.Genre>
@{
ViewBag.Title = "Store";
}
<h3>
Browse Genres</h3>
<p>
Select from @Model.Count() genres:</p>
<ul>
@foreach (var genre in Model)
{ <li>@genre.Name</li>
}
</ul>
注意,此时有完全的智能提示
在 foreach 循环中,也同样有提示。
再次运行程序,我们可以看到如下的结果。
增加页面之间的链接
现在,我们的/Store 可以使用纯文本列出当前的分类名称,下一步,我们将这些纯文本替换成可以链接到浏览分类的链接/Store/Browse 上,这样,当用户点击音乐分类“Disco”将会被导航到/Store/Browse?genre=Disco 的URL 地址上。我们再次更新/Views/Store/index.cshtml 视图模板,先看一下,一会我们还会再次改进。
<ul>
@foreach (var genre in Model)
{
<li><a href=/Store/Browse?genre=@genre.Name>@genre.Name</a></li>
}
</ul>
这样就可以工作了,但是这里使用了硬编码的字符串,如果我们希望修改控制器的名称,那么,我们就要找到所有这样的位置进行修改
更好的处理方式是使用HTMLHelper 的助手方法,ASP.NET MVC 包含了一个HTML 的助手类,其中的方法专门用于在视图模板中完成多种常见的任务,其中的Html.ActionLink() 助手方法就是常用的一个,这使得可以容易地创建<a> ,包括关于链接的一些细节处理,像地址需要进行URL 编码之类。
Html.ActionLink() 有多个重载用于多种情况,在简单的情况下,你只需要提供提示的文本,以及指向的Action 方法即可,在客户端,举个例子,我们希望链接到/Store 的Index 方法,提示文本为Go to the Store Index,那么下面的代码就可以。
@Html.ActionLink("Go to the Store Index", "Index")
注意:在这个例子中,我们不需要再特别指定控制器的名称,因为我们在使用同一个控制器的不同Action 方法。
我们的链接还需要一些参数,我们可以使用另外一种重载来传递三个参数。
1. 链接的提示文本,这里显示分类的名称
2. 控制器的名称,Browse
3. 路由参数,提供名字genre 和值,genre 的名字
合在一起,下面就是需要写在视图模板中的内容
<ul> @foreach (var genre in Model)
{
<li>@Html.ActionLink(genre.Name, "Browse", new { genre = genre.Name })</li>
}
</ul>
现在,当我们运行程序,访问/Store 的时候,将会看到一个分类的列表,每一个分类都是一个超级链接,当点击链接的时候,将会被导航到/Store/Browse?genre=[genre] 的地址
页面中生成的分类链接如下:
<ul>
<li><a href="/Store/Browse?genre=Disco">Disco</a></li>
<li><a href="/Store/Browse?genre=Jazz">Jazz</a></li>
<li><a href="/Store/Browse?genre=Rock">Rock</a></li>
</ul>
作者 冠军
最近更新
随机推荐
- 微软下周二将发布2个安全公告 修复8个
- 专家发现 Linksys WAG54G2路由器漏洞
- 知乎滴滴在裁员,BATJ却在“公司高管
- 腾讯发布公告:严厉打击“微信跑分”
- Microsoft IE帧位置绕过跨域安全限制
- Adobe Acrobat Reader漏洞引发rootki
- 苹果修复iPhone漏洞 突显手机安全隐忧
- Adobe软件超越微软Windows 成为病毒安
- 微信5.0革命性的APP
- Adobe修复Reader软件中的严重漏洞
- 微软Xbox或借道百视通落地中国 成新超
- 国产手机粗放式增长遇天花板 洗牌将近
- MS09-041:Workstation 服务中的允许特
- 知名站长工具服务商:爱站网突然关站
- 钱盆网:帮你划重点,如何看懂信息披
- 网站安全危机即将爆发 加速乐零部署化
- Wireshark 分析工具 1.0.1 版本修复多
- MPEG2 0DAY漏洞分析报告
- Cisco Unified MeetingPlace 有跨站脚
- MS05-047:即插即用中允许远程执行代码