ASP.NET Web Pages簡介 - 建立一致的版面配置
本教學課程說明如何使用版面配置,在使用 ASP.NET Web Pages 的網站上建立一致的頁面外觀。 假設您已完成透過刪除 ASP.NET Web Pages中的資料庫資料系列。
您將學到什麼:
- 什麼是版面配置頁面。
- 如何結合版面配置頁面與動態內容。
- 如何將值傳遞至版面配置頁面。
關於版面配置
您到目前為止建立的頁面都已完成獨立頁面。 它們全都屬於相同的網站,但沒有任何通用元素或標準外觀。
大部分的網站都有一致的外觀和版面配置。 例如,如果您移至 Microsoft.com/web 網站並查看,您會看到頁面都遵守整體版面配置和視覺主題:
建立此版面配置 的效率不佳 的方式,就是在每個頁面上個別定義頁首、導覽列和頁尾。 您每次都會複製相同的標記。 例如,如果您想要變更某個 (,請更新頁尾) ,您必須個別變更每個頁面。
這就是 版面配置頁面 的所在位置。 在 ASP.NET Web Pages中,您可以定義版面配置頁面,為網站上的頁面提供整體容器。 例如,版面配置頁面可以包含頁首、流覽區域和頁尾。 版面配置頁面包含主要內容的預留位置。
然後,您可以定義包含標記的個別內容頁面,以及只包含該頁面的程式碼。 內容頁面不一定是完整的 HTML 頁面;他們甚至不需要有 元素 <body>
。 它們也有一行程式碼,可告訴 ASP.NET 您想要顯示內容的版面配置頁面。 下圖大致顯示此關聯性的運作方式:
當您看到作用中的互動時,很容易瞭解此互動。 在本教學課程中,您將變更電影頁面以使用版面配置。
新增版面配置頁面
首先,您將建立一個版面配置頁面,以定義具有頁首、頁尾和主要內容區域的一般版面配置。 在 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 規則的事項。 其中一個是除了設定字型和大小之外,規則還會使用絕對位置來建立頁首、頁尾和主要內容區域的位置。 如果您不熟悉 CSS 中的定位,您可以在 W3Schools 網站閱讀 CSS 定位 教學課程。
另一件要注意的是,我們已複製最初在 Movies.cshtml 檔案中個別定義的樣式規則。 這些規則用於使用 ASP.NET Web Pages教學課程來顯示資料簡介,讓 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 當 Movies 頁面執行時,它應該與 _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>
設定為 「My Movie Site」 的專案。 大部分的瀏覽器都會將此元素的內容顯示為索引標籤上的文字:
此標題資訊為泛型。 假設您想要讓標題文字更具體到目前的頁面。 (搜尋引擎也會使用標題文字來判斷頁面的用途。) 您可以從 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>
即將推出下一個
在下一個教學課程中,您將瞭解如何將網站發佈至網際網路,讓每個人都可以看到它。
其他資源
- 建立一致的外觀 — 提供使用版面配置的詳細資料一文。 它也會描述如何將值傳遞至顯示或隱藏部分內容的版面配置頁面。
- 使用 Razor 的巢狀版面配置頁面 — Mike Brind 部落格了如何巢狀配置頁面的範例。 (包含 pages 的下載。)
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應