上一章节中我们学习了如何使用 EF 框架从数据库中读取并显示数据,我们使用了两个模板文件 Index.cshtml
和 Detail.cshtml
。相信大家在创建填充 Detail.cshtml
内容的时候会想,为什么 <!DOCTYPE html>
这样重复的内容我们要输入一次又一次,有没有办法只输入一次呢?
答案是肯定的,有的。
Razor 视图引擎提供了 Layout
布局的功能,可以把视图中公共的部分抽出来单独为一个文件,这样就省去了不少的麻烦
本章节,我们就来学习下 Razor 布局视图。大多数网站和 Web 应用程序都会创建具有一些通用元素的页面
- 通常每个页面顶部都有一个区域用于显示公共的 logo 和导航菜单
- 页面的左边侧栏都会添加一些其它的链接和信息,且页面底部都会显示版权信息和一些公司信息
几乎应用程序的每个页面都可能包含这些公共元素。在 ASP.NET Core 中,我们可以使用布局视图来避免一次又一次的重复编写它们
布局视图 ( Layout View )
首先,我们来了解下 ASP.NET Core 中的布局视图到底是什么:
- 布局视图是带有
.cshtml
扩展名的 Razor 视图可以随意给布局视图命名,一般情况下,默认的约定的布局视图名字是_Layout.cshtml
这是布局视图的通用名称,可以不需要前导下划线。因为这只是许多开发者遵循的一个约定用来区分布局视图与普通视图 - 布局视图是一种特殊的视图,一旦我们有了布局视图,我们就可以设置我们的控制器视图,如
Home
的Index
视图
- 我们可以将控制器视图设置为在布局视图内的特定位置显示这种视图布局方法意味着
Index.cshtml
不需要知道有关 logo 或顶级导航的任何信息Index.cshtml
视图只需要显示控制器操提供模型的特定内容,其它内容则由 布局视图来负责处理
范例
我们举一个简单的例子,给我们的 HelloWorld
项目添加一个布局视图
如果我们有多个视图,那么会看到所有的视图都会包含一些重复的标记。比如都有一个 <html>
标签,<head>
标签和 <body>
标签
虽然我们的 HelloWorld
项目中没有导航菜单,但其它应用程序则可能存在,我们并不希望在每个视图中都复制这些相同的标签
现在,我们来创建 Layout 视图
我们会在 Views
目录下新建一个目录 Shared
,然后添加一个布局视图 _Layout.cshtml
从前面的几章节中我们了解到,如果 MVC 框架在控制器目录找不到视图,它们就会尝试在 Shared
目录中查找,也就是说 Shared
目录里的视图可以被多个控制器使用
- 在
Views
目录下新建一个目录Shared
- 右键点击
Shared
目录,选择 添加 -> 新建文件 打开新建文件对话框如果你的电脑是Windows
系统,则是选择 添加 -> 新建项
- 选中左边的 ASP.NET Core,然后从中间选中 MVC 视图布局页面如果你的电脑是
Windows
系统,则是先选中 ASP.NET Core -> Web -> ASP.NET ,然后从中间选择 Razor 布局
- 在名称中输入
_Layout
或_Layout.cshtml
( Windows ),然后点击右下角的 新建 或 添加 ( Windows )
创建完成后的目录结果如下
_Layout.cshtml
中默认的内容如下
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
</head>
<body>
<div>
@RenderBody()
</div>
</body>
</html>
因为布局视图也是一个 Razor 视图,所以也可以使用 C# 表达式
在上面的代码中,可以看到像 RenderBody
和 ViewBag.Title
这样的 C# 表达式。 当一个 MVC 控制器方法渲染一个普通的视图时,如果普通的视图有加载一个布局视图。 那么普通视图和它生成的 HTML 片段就会被包含进布局视图中
而被包含的位置,就是 @RenderBody
表达式的地方,也就是说 @RenderBody
表达式用于包含普通视图生成的内容
布局视图中的其它表达式,例如 @ViewBag.Title
,ViewBag
是一种数据结构,可以添加任何想要放入 ViewBag
的属性或数据。 例如可以在 ViewBag
上添加 ViewBag.Title
,ViewBag.CurrentDate
或我们想要的任何属性
我们修改下 _Layout.cshtml
文件,添加一个当前的时间
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
</head>
<body>
<div>@DateTime.Now</div>
<div>
@RenderBody()
</div>
</body>
</html>
接下来我们回到 Home/Index.html
普通控制器视图,它的原本内容如下
@model HelloWorld.Controllers.HomePageViewModel
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Home 控制器下的 Index 方法</title>
</head>
<body>
<h1>欢迎!</h1>
<div>这个消息来自 Home 控制器下的 Index 的视图文件 index.cshtml </div>
<table>
@foreach (var employee in Model.Employees) {
<tr>
<td><a href="/Home/Detail/@employee.ID">@employee.ID</a></td>
<td>@employee.Name</td>
</tr>
}
</table>
</body>
</html>
有了布局视图,我们就可以删除所有我们不在需要的 HTML 标签,比如 <!DOCTHPE html>
、<html>
和 <head>
,还有 <body>
以及它们对应的结束标记
删除后的代码如下
<h1>欢迎!</h1>
<div>这个消息来自 Home 控制器下的 Index 的视图文件 index.cshtml </div>
<table>
@foreach (var employee in Model.Employees) {
<tr>
<td><a href="/Home/Detail/@employee.ID">@employee.ID</a></td>
<td>@employee.Name</td>
</tr>
}
</table>
当然了,我们还要引入布局视图和给 ViewBag
赋值一个 title
属性,这可以使用 @{}
C# 语句块来完成
@model HelloWorld.Controllers.HomePageViewModel
@{
ViewBag.Title = "Home 控制器下的 Index 方法";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>欢迎!</h1>
<div>这个消息来自 Home 控制器下的 Index 的视图文件 index.cshtml </div>
<table>
@foreach (var employee in Model.Employees)
{
<tr>
<td><a href="/Home/Detail/@employee.ID">@employee.ID</a></td>
<td>@employee.Name</td>
</tr>
}
</table>
保存所有的代码,然后刷新浏览器,显示如下
每刷新一次时间就会变一次,是不是很有成就感?