ASP.NET Core Razor 布局视图
上一章节中我们学习了如何使用 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>
保存所有的代码,然后刷新浏览器,显示如下

每刷新一次时间就会变一次,是不是很有成就感?