ASP.NET 网页简介 - 创建一致的布局
本教程介绍如何使用布局为使用 ASP.NET 网页 的网站上的页面创建一致的外观。 它假定你已通过删除 ASP.NET 网页 中的数据库数据完成了该系列。
学习内容:
- 什么是布局页面。
- 如何将布局页面与动态内容组合在一起。
- 如何将值传递给布局页。
关于布局
到目前为止,你已创建的页面都是完整的独立页面。 它们都属于同一站点,但它们没有任何通用元素或标准外观。
大多数网站具有一致的外观和布局。 例如,如果转到 Microsoft.com/web 网站并环顾四周,则会看到页面都遵循整体布局和视觉主题:
创建此布局的 低效 方法是在每个页面上分别定义页眉、导航栏和页脚。 每次都会复制相同的标记。 如果要更改某些内容 (例如更新页脚) ,则必须单独更改每个页面。
这就是 布局页面 的用处。 在 ASP.NET 网页 中,可以定义一个布局页面,为网站上的页面提供整体容器。 例如,布局页可以包含页眉、导航区域和页脚。 布局页包含一个占位符,main内容所在的位置。
然后,可以定义仅包含该页面的标记和代码的单个内容页。 内容页不必是完整的 HTML 页面;它们甚至不必具有 <body>
元素。 它们还有一行代码,告知 ASP.NET 要在其中显示内容的布局页面。 下图大致显示了此关系的工作原理:
当你看到它的运行情况时,这种交互很容易理解。 在本教程中,你将更改电影页面以使用布局。
添加布局页
首先创建一个布局页,该页面定义具有页眉、页脚和main内容区域的典型页面布局。 在 WebPagesMovies 网站中,添加名为 _Layout.cshtml 的 CSHTML 页。
前导下划线 ( _
) 字符很重要。 如果页面名称以下划线开头,ASP.NET 不会直接将页面发送到浏览器。 此约定允许您定义网站所需的页面,但用户不应能够直接请求。
将页面中的内容替换为以下内容:
<!DOCTYPE html>
<html>
<head>
<title>My Movie Site</title>
<link href="~/Styles/Movies.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="container">
<div id="header">
<h1>My Movie Site</h1>
</div>
<div id="main">
@RenderBody()
</div>
<div id="footer">
© @DateTime.Now.Year My Movie Site
</div>
</div>
</body>
</html>
如你所看到的,此标记只是 HTML,它使用 <div>
元素定义页面中的三个部分,以及一 <div>
个元素来保存这三个部分。 页脚包含一些 Razor 代码: @DateTime.Now.Year
,它将呈现页面中该位置的当前年份。
请注意,有一个名为 Movies.css 的样式表的链接。 样式表是定义元素的物理布局详细信息的位置。 稍后你将创建它。
此 _Layout.cshtml 页中唯一不寻常的功能是 @Render.Body()
行。 这是当此布局与其他页面合并时内容将转到的占位符。
添加 .css 文件
定义实际排列 (即页面上元素的外观) 的首选方法是使用级联样式表 (CSS) 规则。 因此,你将创建一个 .css 文件,其中包含新布局的规则。
在 WebMatrix 中,选择站点的根。 然后在功能区的“ 文件 ”选项卡中,单击“ 新建 ”按钮下的箭头,然后单击“ 新建文件夹”。
将新文件夹命名 为 Styles。
在新的 Styles 文件夹中,创建名为 Movies.css 的文件。
将新的 .css 文件的内容替换为以下内容:
html{ height:100%; margin:0; padding:0; }
body {
height:60%;
font-family:'Trebuchet MS', 'Arial', 'Helvetica', 'sans-serif';
font-size:10pt;
background-color: LightGray;
line-height:1.6em;
}
h1{ font-size:1.6em; }
h2{ font-size:1.4em; }
#container{
min-height:100%;
position:relative;
left:10%;
}
#header{
padding:8px;
width:80%;
background-color:#4b6c9e;
color:White;
}
#main{
width:80%;
padding: 8px;
padding-bottom:4em;
background-color:White;
}
#footer{
width:80%;
height:2em;
padding:8px;
margin-top:-2em;
background-color:LightGray;
}
.head { background-color: #E8E8E8; font-weight: bold; color: #FFF; }
.grid th, .grid td { border: 1px solid #C0C0C0; padding: 5px; }
.alt { background-color: #E8E8E8; color: #000; }
.selected {background-color:Yellow;}
span.caption {width:100px;}
span.dataDisplay {font-weight:bold;}
我们不会对这些 CSS 规则说太多,只是要注意两点。 一个是,除了设置字体和大小外,规则还使用绝对定位来建立页眉、页脚和main内容区域的位置。 如果你不熟悉 CSS 中的定位,可以在 W3Schools 站点上阅读 CSS 定位 教程。
另一个需要注意的是,在底部,我们复制了最初在 Movies.cshtml 文件中单独定义的样式规则。 使用 ASP.NET 网页显示数据简介教程中使用了这些规则,使WebGrid
帮助程序呈现向表中添加了条带的标记。 (如果要对样式定义使用 .css 文件,则不妨将整个网站的样式规则放在其中。)
更新电影文件以使用布局
现在,您可以更新网站中的现有文件以使用新布局。 打开 Movies.cshtml 文件。 在顶部,作为第一行代码,添加以下内容:
Layout = "~/_Layout.cshtml";
页面现在以这种方式开始:
@{
Layout = "~/_Layout.cshtml";
var db = Database.Open("WebPagesMovies") ;
var selectCommand = "SELECT * FROM Movies";
var searchTerm = "";
// Etc.
这一行代码告知 ASP.NET,当 “电影 ”页运行时,它应与 _Layout.cshtml 文件合并。
由于 Movies.cshtml 文件现在使用布局页,因此可以从由 _Layout.cshtml 文件负责的 Movies.cshtml 页中删除标记。 取出 <!DOCTYPE>
、 <html>
和 <body>
开始和结束标记。 取出整个 <head>
元素及其内容,其中包括网格的样式规则,因为现在已在 .css 文件中获取这些规则。 使用时,将现有 <h1>
元素更改为 元素 <h2>
;布局页中已有一个 <h1>
元素。 将 <h2>
文本更改为“列出电影”。
通常,无需在内容页面中进行此类更改。 当您使用布局页面启动网站时,您将创建内容页面时,无需所有这些元素即可开始。 不过,在这种情况下,你要将独立页面转换为使用布局的页面,因此需要一些清理。
完成后, Movies.cshtml 页面将如下所示:
@{
Layout = "~/_Layout.cshtml";
var db = Database.Open("WebPagesMovies") ;
var selectCommand = "SELECT * FROM Movies";
var searchTerm = "";
if(!Request.QueryString["searchGenre"].IsEmpty() ) {
selectCommand = "SELECT * FROM Movies WHERE Genre = @0";
searchTerm = Request.QueryString["searchGenre"];
}
if(!Request.QueryString["searchTitle"].IsEmpty() ) {
selectCommand = "SELECT * FROM Movies WHERE Title LIKE @0";
searchTerm = "%" + Request.QueryString["searchTitle"] + "%";
}
var selectedData = db.Query(selectCommand, searchTerm);
var grid = new WebGrid(source: selectedData, defaultSort: "Genre", rowsPerPage:3);
}
<h2>List Movies</h2>
<form method="get">
<div>
<label for="searchGenre">Genre to look for:</label>
<input type="text" name="searchGenre"
value="@Request.QueryString["searchGenre"]" />
<input type="Submit" value="Search Genre" /><br/>
(Leave blank to list all movies.)<br/>
</div>
<div>
<label for="SearchTitle">Movie title contains the following:</label>
<input type="text" name="searchTitle" value="@Request.QueryString["searchTitle"]" />
<input type="Submit" value="Search Title" /><br/>
</div>
</form>
<div>
@grid.GetHtml(
tableStyle: "grid",
headerStyle: "head",
alternatingRowStyle: "alt",
columns: grid.Columns(
grid.Column(format: @<a href="~/EditMovie?id=@item.ID">Edit</a>),
grid.Column("Title"),
grid.Column("Genre"),
grid.Column("Year"),
grid.Column(format: @<a href="~/DeleteMovie?id=@item.ID">Delete</a>)
)
)
</div>
<p><a href="~/AddMovie">Add a movie</a></p>
测试布局
现在可以看到布局的外观。 在 WebMatrix 中,右键单击 Movies.cshtml 页,然后选择“ 在浏览器中启动”。 当浏览器显示页面时,页面如下所示:
ASP.NET 已将 Movies.cshtml 页面的内容合并到方法所在的RenderBody
_Layout.cshtml 页中。 当然, _Layout.cshtml 页面引用定义页面外观的 .css 文件。
更新 AddMovie 页面以使用布局
布局的真正好处是,你可以将它们用于网站中的所有页面。 打开 AddMovie.cshtml 页。
你可能还记得 ,AddMovie.cshtml 页面最初包含一些 CSS 规则来定义验证错误消息的外观。 由于您现在拥有网站的 .css 文件,因此您可以将这些规则移动到 .css 文件。 从 AddMovie.cshtml 文件中删除它们,并将其添加到 Movies.css 文件的底部。 你正在移动以下规则:
.field-validation-error {
font-weight:bold;
color:red;
background-color:yellow;
}
.validation-summary-errors{
border:2px dashed red;
color:red;
background-color:yellow;
font-weight:bold;
margin:12px;
}
现在,在 AddMovie.cshtml 中执行与对 Movies.cshtml 所做的相同类型的更改 - 添加 Layout="~/_Layout.cshtml;
和删除现在无关的 HTML 标记。 将 <h1>
元素更改为 <h2>
。 完成后,页面将如以下示例所示:
@{
Layout = "~/_Layout.cshtml";
Validation.RequireField("title", "You must enter a title");
Validation.RequireField("genre", "Genre is required");
Validation.RequireField("year", "You haven't entered a year");
var title = "";
var genre = "";
var year = "";
if(IsPost){
if(Validation.IsValid()){
title = Request.Form["title"];
genre = Request.Form["genre"];
year = Request.Form["year"];
var db = Database.Open("WebPagesMovies");
var insertCommand =
"INSERT INTO Movies (Title, Genre, Year) Values(@0, @1, @2)";
db.Execute(insertCommand, title, genre, year);
Response.Redirect("~/Movies");
}
}
}
<h2>Add a Movie</h2>
@Html.ValidationSummary()
<form method="post">
<fieldset>
<legend>Movie Information</legend>
<p><label for="title">Title:</label>
<input type="text" name="title" value="@Request.Form["title"]" />
@Html.ValidationMessage("title")
</p>
<p><label for="genre">Genre:</label>
<input type="text" name="genre" value="@Request.Form["genre"]" />
@Html.ValidationMessage("genre")
</p>
<p><label for="year">Year:</label>
<input type="text" name="year" value="@Request.Form["year"]" />
@Html.ValidationMessage("year")
</p>
<p><input type="submit" name="buttonSubmit" value="Add Movie" /></p>
</fieldset>
</form>
运行页面。 现在如下图所示:
您希望对网站中的页面进行类似的更改- EditMovie.cshtml 和 DeleteMovie.cshtml。 但是,在执行此操作之前,你可以对布局进行另一次更改,使其更灵活一点。
将标题信息传递到布局页
创建的 _Layout.cshtml 页面有一个 <title>
设置为“我的电影网站”的 元素。 大多数浏览器将此元素的内容显示为选项卡上的文本:
此游戏信息是通用的。 假设希望标题文本更特定于当前页。 (搜索引擎还使用标题文本来确定页面的内容。) 可以将内容页(如 Movies.cshtml 或 AddMovie.cshtml )中的信息传递到布局页,然后使用该信息自定义布局页面呈现的内容。
再次打开 Movies.cshtml 页面。 在顶部的代码中,添加以下行:
Page.Title = "List Movies";
对象 Page
在所有 .cshtml 页面上可用,用于此目的,即在页面及其布局之间共享信息。
打开 “_Layout.cshtml ”页。 更改 <title>
元素,使其类似于以下标记:
<title>@Page.Title</title>
此代码在页面的该 Page.Title
位置的 属性中呈现任何信息。
运行 Movies.cshtml 页。 这一次,浏览器选项卡显示作为 的值 Page.Title
传递的内容:
如果需要,请在浏览器中查看页面源。 可以看到 元素 <title>
呈现为 <title>List Movies</title>
。
提示
Page 对象
的 Page
一个有用功能是它是动态对象 , Title
属性不是固定或保留名称。 可以将 任何 名称用于 对象的值 Page
。 例如,可以使用名为 Page.CurrentName
或 Page.MyPage
的属性轻松传递标题。 唯一的限制是名称必须遵循可以命名的属性的常规规则。 (例如,名称不能包含 space.)
可以使用 对象传递任意数量的值 Page
。 如果要将电影信息传递到布局页,可以使用 和 Page.Genre
和 Page.MovieYear
之类的Page.MovieTitle
内容传递值。 (或你为存储信息而发明的任何其他名称。) 唯一的要求(可能很明显)是在内容页和布局页面中使用相同的名称。
使用 Page
对象传递的信息不仅限于要显示在布局页上的文本。 可以将值传递给布局页,然后布局页中的代码可以使用该值决定是否显示页面的某个部分、要使用的 .css 文件等。 在 对象中 Page
传递的值与在代码中使用的任何其他值类似。 只是值源自内容页,并传递到布局页。
打开 AddMovie.cshtml 页,并在代码顶部添加一行,为 AddMovie.cshtml 页面提供标题:
Page.Title = "Add a Movie";
运行 AddMovie.cshtml 页。 可看到新标题:
更新剩余页面以使用布局
现在,您可以完成网站中的剩余页面,以便它们使用新布局。 依次打开 EditMovie.cshtml 和 DeleteMovie.cshtml ,并在其中进行相同的更改。
添加链接到布局页的代码行:
Layout = "~/_Layout.cshtml";
添加一行以设置页面标题:
Page.Title = "Edit a Movie";
或:
Page.Title = "Delete a Movie";
删除所有无关的 HTML 标记 - 基本上,只保留元素内部 <body>
的位 (加上顶部) 的代码块。
将 <h1>
元素更改为 <h2>
元素。
进行这些更改后,请测试每个更改,并确保其正确显示且标题正确。
关于布局页面的分别想法
在本教程中,你创建了 一个 _Layout.cshtml 页面,并使用 RenderBody
方法合并了另一个页面中的内容。 这是在网页中使用布局的基本模式。
布局页面具有我们此处未介绍的其他功能。 例如,可以嵌套布局页 - 一个布局页可以依次引用另一个布局页。 如果使用需要不同布局的网站子部分,嵌套布局可能很有用。 还可以使用其他方法 (例如, RenderSection
) 在布局页中设置命名节。
布局页面和 .css 文件的组合功能非常强大。 正如你将在下一个教程系列中看到的,在 WebMatrix 中,你可以基于模板创建一个网站,该 模板为你提供了一个具有预生成功能的网站。 模板充分利用布局页面和 CSS 来创建外观极佳且具有菜单等功能的网站。 下面是基于模板的网站主页的屏幕截图,其中显示了使用布局页面和 CSS 的功能:
电影页面的完整列表 (更新为使用布局页面)
@{
Layout = "~/_Layout.cshtml";
Page.Title = "List Movies";
var db = Database.Open("WebPagesMovies") ;
var selectCommand = "SELECT * FROM Movies";
var searchTerm = "";
if(!Request.QueryString["searchGenre"].IsEmpty() ) {
selectCommand = "SELECT * FROM Movies WHERE Genre = @0";
searchTerm = Request.QueryString["searchGenre"];
}
if(!Request.QueryString["searchTitle"].IsEmpty() ) {
selectCommand = "SELECT * FROM Movies WHERE Title LIKE @0";
searchTerm = "%" + Request.QueryString["searchTitle"] + "%";
}
var selectedData = db.Query(selectCommand, searchTerm);
var grid = new WebGrid(source: selectedData, defaultSort: "Genre", rowsPerPage:3);
}
<h2>List Movies</h2>
<form method="get">
<div>
<label for="searchGenre">Genre to look for:</label>
<input type="text" name="searchGenre" value="@Request.QueryString["searchGenre"]" />
<input type="Submit" value="Search Genre" /><br/>
(Leave blank to list all movies.)<br/>
</div>
<div>
<label for="SearchTitle">Movie title contains the following:</label>
<input type="text" name="searchTitle" value="@Request.QueryString["searchTitle"]" />
<input type="Submit" value="Search Title" /><br/>
</div>
</form>
<div>
@grid.GetHtml(
tableStyle: "grid",
headerStyle: "head",
alternatingRowStyle: "alt",
columns: grid.Columns(
grid.Column(format: @<a href="~/EditMovie?id=@item.ID">Edit</a>),
grid.Column("Title"),
grid.Column("Genre"),
grid.Column("Year"),
grid.Column(format: @<a href="~/DeleteMovie?id=@item.ID">Delete</a>)
)
)
</div>
<p><a href="~/AddMovie">Add a movie</a></p>
添加影片页面的完整页面列表 (更新布局)
@{
Layout = "~/_Layout.cshtml";
Page.Title = "Add a Movie";
Validation.RequireField("title", "You must enter a title");
Validation.RequireField("genre", "Genre is required");
Validation.RequireField("year", "You haven't entered a year");
var title = "";
var genre = "";
var year = "";
if(IsPost){
if(Validation.IsValid()){
title = Request.Form["title"];
genre = Request.Form["genre"];
year = Request.Form["year"];
var db = Database.Open("WebPagesMovies");
var insertCommand = "INSERT INTO Movies (Title, Genre, Year) VALUES(@0, @1, @2)";
db.Execute(insertCommand, title, genre, year);
Response.Redirect("~/Movies");
}
}
}
<h2>Add a Movie</h2>
@Html.ValidationSummary()
<form method="post">
<fieldset>
<legend>Movie Information</legend>
<p><label for="title">Title:</label>
<input type="text" name="title" value="@Request.Form["title"]" />
@Html.ValidationMessage("title")
<p><label for="genre">Genre:</label>
<input type="text" name="genre" value="@Request.Form["genre"]" />
@Html.ValidationMessage("genre")
<p><label for="year">Year:</label>
<input type="text" name="year" value="@Request.Form["year"]" />
@Html.ValidationMessage("year")
<p><input type="submit" name="buttonSubmit" value="Add Movie" /></p>
</fieldset>
</form>
针对布局) 更新的“删除影片页面” (的完整页面列表
@{
Layout = "~/_Layout.cshtml";
Page.Title = "Delete a Movie";
var title = "";
var genre = "";
var year = "";
var movieId = "";
if(!IsPost){
if(!Request.QueryString["ID"].IsEmpty() && Request.QueryString["ID"].AsInt() > 0){
movieId = Request.QueryString["ID"];
var db = Database.Open("WebPagesMovies");
var dbCommand = "SELECT * FROM Movies WHERE ID = @0";
var row = db.QuerySingle(dbCommand, movieId);
if(row != null) {
title = row.Title;
genre = row.Genre;
year = row.Year;
}
else{
Validation.AddFormError("No movie was found for that ID.");
// If you are using a version of ASP.NET Web Pages 2 that's
// earlier than the RC release, comment out the preceding
// statement and uncomment the following one.
//ModelState.AddFormError("No movie was found for that ID.");
}
}
else{
Validation.AddFormError("No movie was found for that ID.");
// If you are using a version of ASP.NET Web Pages 2 that's
// earlier than the RC release, comment out the preceding
// statement and uncomment the following one.
//ModelState.AddFormError("No movie was found for that ID.");
}
}
if(IsPost && !Request["buttonDelete"].IsEmpty()){
movieId = Request.Form["movieId"];
var db = Database.Open("WebPagesMovies");
var deleteCommand = "DELETE FROM Movies WHERE ID = @0";
db.Execute(deleteCommand, movieId);
Response.Redirect("~/Movies");
}
}
<h2>Delete a Movie</h2>
@Html.ValidationSummary()
<p><a href="~/Movies">Return to movie listing</a></p>
<form method="post">
<fieldset>
<legend>Movie Information</legend>
<p><span>Title:</span>
<span>@title</span></p>
<p><span>Genre:</span>
<span>@genre</span></p>
<p><span>Year:</span>
<span>@year</span></p>
<input type="hidden" name="movieid" value="@movieId" />
<p><input type="submit" name="buttonDelete" value="Delete Movie" /></p>
</fieldset>
</form>
“编辑影片页面”的完整页面列表 (更新布局)
@{
Layout = "~/_Layout.cshtml";
Page.Title = "Edit a Movie";
var title = "";
var genre = "";
var year = "";
var movieId = "";
if(!IsPost){
if(!Request.QueryString["ID"].IsEmpty() && Request.QueryString["ID"].IsInt()) {
movieId = Request.QueryString["ID"];
var db = Database.Open("WebPagesMovies");
var dbCommand = "SELECT * FROM Movies WHERE ID = @0";
var row = db.QuerySingle(dbCommand, movieId);
if(row != null) {
title = row.Title;
genre = row.Genre;
year = row.Year;
}
else{
Validation.AddFormError("No movie was selected.");
// If you are using a version of ASP.NET Web Pages 2 that's
// earlier than the RC release, comment out the preceding
// statement and uncomment the following one.
//ModelState.AddFormError("No movie was selected.");
}
}
else{
Validation.AddFormError("No movie was selected.");
// If you are using a version of ASP.NET Web Pages 2 that's
// earlier than the RC release, comment out the preceding
// statement and uncomment the following one.
//ModelState.AddFormError("No movie was selected.");
}
}
if(IsPost){
Validation.RequireField("title", "You must enter a title");
Validation.RequireField("genre", "Genre is required");
Validation.RequireField("year", "You haven't entered a year");
Validation.RequireField("movieid", "No movie ID was submitted!");
title = Request.Form["title"];
genre = Request.Form["genre"];
year = Request.Form["year"];
movieId = Request.Form["movieId"];
if(Validation.IsValid()){
var db = Database.Open("WebPagesMovies");
var updateCommand = "UPDATE Movies SET Title=@0, Genre=@1, Year=@2 WHERE Id=@3";
db.Execute(updateCommand, title, genre, year, movieId);
Response.Redirect("~/Movies");
}
}
}
<h2>Edit a Movie</h2>
@Html.ValidationSummary()
<form method="post">
<fieldset>
<legend>Movie Information</legend>
<p><label for="title">Title:</label>
<input type="text" name="title" value="@title" /></p>
<p><label for="genre">Genre:</label>
<input type="text" name="genre" value="@genre" /></p>
<p><label for="year">Year:</label>
<input type="text" name="year" value="@year" /></p>
<input type="hidden" name="movieid" value="@movieId" />
<p><input type="submit" name="buttonSubmit" value="Submit Changes" /></p>
</fieldset>
</form>
<p><a href="~/Movies">Return to movie listing</a></p>
即将上一步
在下一教程中,你将了解如何将网站发布到 Internet,以便每个人都可以看到它。
其他资源
- 创建一致的外观 — 一篇文章提供有关使用布局的更多详细信息。 它还介绍如何将值传递给显示或隐藏部分内容的布局页面。
- 使用 Razor 嵌套布局页面 — Mike Brind 博客中提供了有关如何嵌套布局页面的示例。 (包括 pages 的下载。)