ASP.NET MVC 音乐商店 - 3. 视图与模型

时间:2022-12-03 10:53:25

上一篇中使用字符串,这一篇我们就开始使用视图来处理。

我们已经可以从控制器的 Action 中返回一个字符串,这可以帮助我们更好地理解 Controller 是如何工作的。但是对于创建一个 Web 程序来说还是不够的。下面我们使用更好的方法来生成 HTML,主要是通过模板来生成需要的 HTML,这就是视图所要做的。

增加视图模板

为了使用视图模板,我们需要将HomeController 中的 Index 这个 Action 的返回类型修改为 ActionResult,然后,让它像下面一样返回一个视图。

ASP.NET MVC 音乐商店 - 3. 视图与模型
public class HomeController : Controller  
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
}
ASP.NET MVC 音乐商店 - 3. 视图与模型

上面的修改表示我们将使用视图来替换掉原来的字符串,以便生成返回的结果。

现在为我们的项目增加一个视图,为达到这个目的,我们将光标移到 Index 方法内,然后,点击鼠标的右键,在右键菜单中选择“添加视图(D)…”,这样将会弹出增加视图的对话框。

ASP.NET MVC 音乐商店 - 3. 视图与模型

添加视图的对话框允许我们快速,简单地创建一个视图模板,默认情况下,视图的名称使用当前 Action 的名字。因为我们是在 Index 这个 Aciton 上添加模板,所以添加视图对话框中,视图的名字就是 Index,我们不需要修改这个名字,点击添加。

ASP.NET MVC 音乐商店 - 3. 视图与模型

在点击添加之后,Visual Studio 将会创建一个名为 Index.cshtml的视图模板,放置在 \Views\Home 目录中,如果没有这个目录,MVC 将会自动创建它。

ASP.NET MVC 音乐商店 - 3. 视图与模型

Index.cshtml 所在文件夹的名称和位置是很重要的,它是根据ASP.NET MVC 的约定来指定的。目录名称 \Views\Home ,匹配的控制器就是 HomeController ,视图模板的名字 Index,匹配将要使用这个视图的 Action 方法的名字。

当使用默认的约定的时候,ASP.NET MVC 允许我们不用显式设置这些名字和位置,当我们的代码如下所示的时候,将会默认寻找 \Views\Home\Index.cshtml。

ASP.NET MVC 音乐商店 - 3. 视图与模型
public class HomeController : Controller  
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
}
ASP.NET MVC 音乐商店 - 3. 视图与模型

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>

现在,我们的首页应该变成下面的样子。

ASP.NET MVC 音乐商店 - 3. 视图与模型

为页面的公共内容使用布局

大多数的网站在页面之间有许多共享的内容:导航,页首,页脚,公司的 Logo,样式表等等。Razor 引擎默认使用名为 _Layout.cshtml 的布局来自动化管理,它保存在 /Views/Shared 文件夹中。

ASP.NET MVC 音乐商店 - 3. 视图与模型

打开之后,可以看到下列内容:

ASP.NET MVC 音乐商店 - 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.4.4.min.js")" type="text/javascript"></script>
</head> <body>
@RenderBody()
</body>
</html>
ASP.NET MVC 音乐商店 - 3. 视图与模型

来自内容视图中的内容,将会被通过 @RenderBody() 来显示,任何出现在网页中的公共内容就加入到 _Layout.cshtml 中,我们希望我们的 MVC 音乐商店有一个公共的页首,其中含有链接到我们的首页和商店区域的链接,所以,我们将这些内容直接添加到这个布局中。

ASP.NET MVC 音乐商店 - 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.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>
ASP.NET MVC 音乐商店 - 3. 视图与模型

更新样式表

在创建项目使用的空项目模板中,仅仅包含很简单的用来显示验证信息的样式。我们的设计师提供了一些额外的 CSS 样式和图片来改进网站的观感,现在我们就使用它们。

首先,到网站 mvcmusicstore.codeplex.com 下载 MvcMusicStore-v3.0.zip,这里面有一个文件夹 MvcMusicStore-Assets,将这个文件夹的Content 文件夹的内容复制到项目的 Content 文件夹中。

ASP.NET MVC 音乐商店 - 3. 视图与模型

你会被询问是否需要覆盖存在的文件,选择是。

ASP.NET MVC 音乐商店 - 3. 视图与模型

现在,网站的 Content 文件夹中的内容如下所示:

ASP.NET MVC 音乐商店 - 3. 视图与模型

重新运行程序,现在的页面变成了这样。

ASP.NET MVC 音乐商店 - 3. 视图与模型

我们回顾一下,什么发生了变化: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。

ASP.NET MVC 音乐商店 - 3. 视图与模型

ASP.NET MVC 音乐商店 - 3. 视图与模型

在新创建的类中增加一个属性。

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 命名空间,这使得我们不用总是输入这个命名空间。

ASP.NET MVC 音乐商店 - 3. 视图与模型
using System;  
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc; using MvcMusicStore.Models;
ASP.NET MVC 音乐商店 - 3. 视图与模型

然后,我们更新 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 的模板,在这样做之前,我们需要编译项目,以便增加视图的对话框知道我们新创建的专辑类型。你可以通过菜单“生成”的“生成解决方案”来完成。

ASP.NET MVC 音乐商店 - 3. 视图与模型

另外,也可以通过热键 Ctrl – Shift – B 来编译项目。

已经可以创建视图模板了,在 Details 方法中右键选择“增加视图…”

ASP.NET MVC 音乐商店 - 3. 视图与模型

像以前一样,我们看到创建视图的对话框,不一样的是,我们要选中“创建强类型视图”,然后在下面的列表中选择“Album”类,这样视图将会期望得到一个 Album 类型的对象。

ASP.NET MVC 音乐商店 - 3. 视图与模型

在点击增加之后,我们的视图模板 \Views\Store\Details.cshtml 被创建了,其中包含的如下的代码:

ASP.NET MVC 音乐商店 - 3. 视图与模型
@model MvcMusicStore.Models.Album  

@{  
ViewBag.Title = "Details";
} <h2>Details</h2>
ASP.NET MVC 音乐商店 - 3. 视图与模型

注意第一行,表示视图使用强类型的 Album 类。Rozer 视图引擎理解传送来的 Album 对象,所以我们可以容易地访问模型的属性,在 Visual Studio 中得到智能感知的帮助。

更新 <h2> 标记,使得可以显示专辑的 Title 属性

ASP.NET MVC 音乐商店 - 3. 视图与模型
@model MvcMusicStore.Models.Album  

@{  
ViewBag.Title = "Details";
} <h2>Album: @Model.Title</h2>
ASP.NET MVC 音乐商店 - 3. 视图与模型

注意,智能感知使得可以提示 Album 的属性和方法。

ASP.NET MVC 音乐商店 - 3. 视图与模型

再次运行并访问 /Store/Details/5,可以得到下面的结果。

ASP.NET MVC 音乐商店 - 3. 视图与模型

现在,我们继续修改 Browse 方法,更新方法返回 ActionResult 类型的结果,修改方法的处理,返回一个 Genre 类型的对象实例。

public ActionResult Browse(string genre)  
{
var genreModel = new Genre { Name = genre };
return View(genreModel);
}

在方法上右击,选择“增加视图…”,增加一个强类型的视图。

ASP.NET MVC 音乐商店 - 3. 视图与模型

修改 <h2> 标记显示 Genre 的信息

@model MvcMusicStore.Models.Genre  
@{
ViewBag.Title = "Browse";
} <h2>Browsing Genre: @Model.Name</h2>

重新运行,访问 /Store/Browse?Genre=Disco,可以看到如下的显示

ASP.NET MVC 音乐商店 - 3. 视图与模型

最后,将 Index 也修改为强类型的视图,显示所有唱片的类别,我们使用 Genre 的一个列表,而不是单个的 Genre 对象。

ASP.NET MVC 音乐商店 - 3. 视图与模型
public ActionResult Index()  
{
var genres = new List<Genre>
{
new Genre { Name = "Disco"},
new Genre { Name = "Jazz"},
new Genre { Name = "Rock"}
};
return View(genres);
}
ASP.NET MVC 音乐商店 - 3. 视图与模型

创建一个强类型的视图

ASP.NET MVC 音乐商店 - 3. 视图与模型

首先,我们将期望得到多个 Genre 对象而不是一个,将第一行修改为如下内容。

@model IEnumerable<MvcMusicStore.Models.Genre>  

这告诉视图引擎模式是一个包含多个 Genre 对象的集合,我们使用 IEnumerable<Genre> 而不是 List<Genre>,因为这样更通用,可以允许我们在以后改变集合为任何实现 IEnumerable 接口的集合。

现在,我们遍历集合中的 Genre 对象进行处理。

ASP.NET MVC 音乐商店 - 3. 视图与模型
@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>
ASP.NET MVC 音乐商店 - 3. 视图与模型

注意,此时有完全的智能提示

ASP.NET MVC 音乐商店 - 3. 视图与模型

在 foreach 循环中,也同样有提示。

ASP.NET MVC 音乐商店 - 3. 视图与模型

再次运行程序,我们可以看到如下的结果。

ASP.NET MVC 音乐商店 - 3. 视图与模型

增加页面之间的链接

现在,我们的 /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>