ASP.NET 網頁簡介 - 建立一致的版面配置
本教學課程說明如何使用版面配置,為使用 ASP.NET 網頁的網站建立一致的頁面外觀。 它假設您已完成在 ASP.NET 網頁中刪除資料庫資料系列。
您將學到什麼:
- 什麼是版面配置。
- 如何結合版面配置頁面與動態內容。
- 如何將值傳遞至版面配置頁面。
關於版面配置
到目前為止所建立的頁面都已完成獨立頁面。 它們全都屬於相同的網站,但沒有任何通用元素或標準外觀。
大部分網站的外觀和版面配置都一致。 例如,如果您前往 Microsoft.com/web 網站並四處查看,您會看到頁面都遵守整體版面配置和視覺主題:
建立此版面配置會造成效率不佳的方式,就是在每個頁面上分別定義頁首、導覽列和頁尾。 您每次都會複製相同的標記。 如果您想要變更某個資訊 (例如,更新頁尾),則必須個別變更每個頁面。
這就是版面配置頁面發揮功用之處。 在 ASP.NET 網頁中,您可以定義版面配置頁面,以提供網站頁面的整體容器。 例如,版面配置頁面可以包含頁首、瀏覽區域和頁尾。 版面配置頁面包含主要內容所在的預留位置。
然後,您可以定義包含標記的個別內容頁面,以及只包含該頁面的程式碼。 內容頁面不一定是完整的 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 網頁顯示資料簡介教學課程中使用,讓 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 檔案現在使用版面配置頁面,因此您可以從 Movies.cshtml 頁面移除由 _Layout.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 頁面的內容合併至 _Layout.cshtml 頁面,即 RenderBody
方法所在位置。 當然,_Layout.cshtml 頁面會參考定義頁面外觀的 .css 檔案。
更新新增電影頁面以使用版面配置
版面配置的真正優點是您可以將其用於網站中的所有頁面。 開啟 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
的實用功能為它是動態物件,Title
屬性不是固定或保留的名稱。 您可以將任何名稱用於 Page
物件的值。 例如,您可以使用名為 Page.CurrentName
或 Page.MyPage
的屬性,輕鬆傳達標題。 唯一的限制是名稱必須遵循可命名屬性的一般規則。 (例如,名稱不能包含空格。)
您可以使用 Page
物件傳遞任意數目的值。 如果您想要將電影資訊傳遞至版面配置頁面,您可以使用 Page.MovieTitle
、Page.Genre
和 Page.MovieYear
之類的來傳遞值。 (或者您自創用來儲存資訊的任何其他名稱)。唯一的要求 (應該顯而易見),就是您必須在內容頁面和版面配置頁面中使用相同的名稱。
您使用 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";
or:
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 部落格說明如何巢狀化配置頁面的範例。 (包括頁面的下載。)