Razor是微軟在MVC3中引入的視圖引擎的名字,在MVC4中對其進行了改進(盡管改動非常小)。視圖引擎處理ASP.NET內容、尋找指令,典型地用於插入動態數據並輸出到瀏覽器中。微軟維持了兩個視圖引擎——ASPX視圖引擎工作與<%%>標簽,ASP.NET已經依賴它多年;RAZOR引擎工作與@字符后的內容塊上。
總的來說,如果你熟悉<%%>語法,那么你就不會在使用Razor時有太多問題,盡管Razor中有一些新的規則。在本章,我們將為你介紹Razor語法,以使你可以在看到它們的時候能認出這些新元素。在本章,我們並不會提供大量的Razor參考,因為這么做會破壞課程結構。但我們在本書后續章節中深入介紹Razor
1創建示例項目
為了演示Razor的特性和語法,我們需要創建一個新的MVC4工程。
定義模型
publicclassProduct { publicint ProductID { get; set; } publicstring Name { get; set; } publicstring Description { get; set; } publicdecimal Price { get; set; } publicstring Category { get; set; } } |
定義控制器
publicclassHomeController : Controller { Product product = newProduct { ProductID = 1, Name = "Kayak", Description = "A boat for one person", Category = "Watersports", Price = 275M };
publicActionResult Index() { return View(product); }
} |
創建視圖
@model MvcRazor.Models.Product
@{ Layout = null; }
<!DOCTYPEhtml>
<html> <head> <metaname="viewport"content="width=device-width"/> <title>Index</title> </head> <body> <div>
</div> </body> </html> |
2使用模型對象
我們在視圖中添加如下的一行(粗體代碼)
@model MvcRazor.Models.Product
@{ Layout = null; }
<!DOCTYPEhtml>
<html> <head> <metaname="viewport"content="width=device-width"/> <title>Index</title> </head> <body> <div> @Model.Name </div> </body> </html> |
Razor語句以@字符開始。在我們的例子中,@model語句聲明將傳遞給視圖使用的來自於行為方法的模型對象。然后通過@Model,我們就可以調用模型對象的方法,字段和屬性。此時你運行工程,那么將會得到如下的結果:
通過使用@model表達式,我們告訴MVC使用什么類型的對象,同時Visual Studio也可從中獲益。首先,當你在編寫視圖中,一旦你在Visual Studio中輸入@model后,Visual Studio將自動列出該對象的屬性、字段以及方法
此外,如果你輸入一個該對象不存在的成員,那么Visual Studio將提示錯誤
3 使用布局
當前,在Index.cshtml中,有如下一句話
…… @{ Layout = null; } …… |
這是一個Razor代碼塊,它允許我們在視圖中使用C#語句。代碼塊以@{開始,以}結束。當呈現視圖的時候,視圖中的代碼塊被執行。在我們的例子中,代碼設置Layout屬性的值為null。在MVC程序中Razor視圖被編譯成C#類,而其基類(RazorView)定義了一個Layout屬性,我們在18章中我們將介紹更詳細的內容。在這里,我們只需要知道當該屬性設為null表明,當前視圖是自我包含的,並且將呈現我們所需的所有內容到客戶端。
自我包含的視圖對於簡單的應用已經足夠,但是一個真正的項目會包含大量的視圖。布局是一種有效的模板,這些模板包含的標記內容可以使你的多個網頁保持一致性——這就可以確保正確JavaScript庫被使用,或者創建通用的模塊供你的項目使用。
創建布局
為了創建布局,你可以在視圖文件夾上點擊右鍵,然后選擇添加,然后選擇MVC4布局頁面(Razor)模板
在出現的對話框中,把布局文件命名為_BasicLayout.cshtml
然后點擊確認按鈕。下面的代碼列出布局文件的基本內容
<!DOCTYPEhtml>
<html> <head> <metaname="viewport"content="width=device-width"/> <title>@ViewBag.Title</title> </head> <body> <div> @RenderBody() </div> </body> </html> |
布局是一種特定類型的視圖,你可以發現它也包含@表達式。調用@RenderBody方法將把由行為方法指定的視圖的內容插入到布局文件中的對應的標記中。另外一個Razor表達式用於查找Viewbag中的Title屬性,然后把其值設置到頁面的title元素中。
布局文件中的所有元素都將應用到使用該布局文件的視圖中,這也就是為什么說視圖就是模板。在下面的代碼中,我們添加了一些標記以演示它們是如何工作的
<!DOCTYPEhtml>
<html> <head> <metaname="viewport"content="width=device-width"/> <title>@ViewBag.Title</title> </head> <body> <div> <h1>Product Information</h1> <divstyle="border:1pxsolidred; line-height:30px;"> @RenderBody() </div> <h2>Visit <ahref="http://apress.com">Apress</a></h2> </div> </body> </html> |
我們向布局文件中添加了兩個標題元素,和一個DIV,然后把@renderbody方法放在DIV中。這樣我們可以區分哪些內容來自布局文件,哪些內容來自視圖。
應用布局
為了應用視圖,我們需要設置Index.cshtml的layout屬性,此外我們還應該刪除HTML標記語言,因為現在由布局文件提供。你可以參考下面的代碼應用布局文件到視圖
@model MvcRazor.Models.Product
@{ ViewBag.Title = "Product Name"; Layout = "~/Views/_BasicLayout.cshtml"; }
@Model.Name |
你可以發現,即使是一個簡單的視圖,改動也是巨大的。我們現在只留下我們最關心的並且要呈現給用戶的數據。所有的html標記都已經刪除。使用布局文件有許多好處,它允許我們簡化數圖;允許我們創建通用的HTML供多個視圖使用;它還使維護變得簡單因為我們可以值在一個共用的地方更改HTML,更改后的結果就會應用到所有使用該布局文件的視圖。下圖展示了使用布局文件的效果
使用視圖開始文件
還有一個小疑惑我們需要指出,那就是我們需要在每個視圖文件中指出我們需要使用的布局文件。這就意味着如果我們需要重命名布局文件,那么我們就需要查找每個使用了該布局文件的視圖然后做出相應的更改,在這個過程中很容易發生錯誤,這也違背了MVC框架的易維護性。
我們可以通過使用視圖開始文件來解決這個問題。當呈現一個視圖時,MVC框架將查找名為_ViewStart.cshtml的文件。該文件的內容被當作它好像包含在視圖文件自身中,我們可以使用這個特性來自動地設置layout屬性的值。
為了創建一個視圖開始文件,添加一個新視圖到Views文件夾下,然后將其命名為_ViewStart.cshtml,然后設置其內容
@{ Layout = "~/Views/_BasicLayout.cshtml"; } |
然后我們更改Index.cshtml的內容
@model MvcRazor.Models.Product
@{ ViewBag.Title = "Product Name"; }
@Model.Name |
我們不必指定我們想使用的視圖開始文件。MVC框架將自動找到該文件並自動地使用視圖開始文件的內容。請注意,視圖文件中的layout的優先級更高。因此,當在視圖文件中指定了Layout之后,會自動覆蓋視圖開始文件中的layout屬性。
演示共享布局
為了演示共享布局,我們添加一個新的行為方法NameAndPrice到Home控制器中。
publicActionResult NameAndPrice() { return View(product); } |
然后,在NameAndPrice上點擊右鍵,選擇創建視圖,執行視圖所使用的模型類為Product,並選擇_BasicLayout.cshtml為該視圖的布局文件。生產視圖后,添加下面加粗的代碼
@model MvcRazor.Models.Product
@{ ViewBag.Title = "NameAndPrice"; Layout = "~/Views/_BasicLayout.cshtml"; }
<h2>NameAndPrice</h2> The product name is @Model.Name and its costs $@Model.Price |
啟動應用層序,然后得到如下的結果
4使用Razor表達式
現在,我們已經展示了視圖和布局的基本使用方法,接下來我們將把注意力集中到Razor所支持的各種表達式上並了解如何使用這些表達式。
在一個優秀的MVC程序中,在不同的行為方法和視圖的執行之間有清楚的界限。在本章,規則很簡單,我們把它總結成下表所示的內容
組件 |
應該賦予的角色 |
不應該賦予的角色 |
行為方法 |
傳遞視圖模型對象到視圖 |
傳遞格式化的數據到視圖 |
視圖 |
使用視圖模型對象,把其內容呈現給用戶 |
更改視圖模型對象的任何方法 |
在本書后續的章節中,我們將不斷的回顧上表所述的規則。為了最大化地利用MVC框架,你應該在程序中的各個部分重視並強制實現隔離。因為你將看到,你可以使用Razor做很多事情,包括在Razor中使用C#語句,但是你絕對不應該使用Razor去執行業務邏輯,或者使用任何方式更改域模型對象。
同樣地,你不應該在行為方法中格式化數據,然后將其傳遞給視圖。相反地,應該讓視圖按照所需的方式呈現數據。回顧本章之前的小節,你會發現我們定義的行為方法NameAndPrice,它用於顯示Product對象的Name屬性和Price屬性。即使我們知道將在頁面上顯示哪些屬性。我們也應傳遞一個完整的Product對象到視圖模型。正如這樣:
publicActionResult NameAndPrice() { return View(product); } |
當我們在視圖中使用Razor @Model表達式去獲取屬性的時候,采用了下面的方式
The product name is @Model.Name and its costs $@Model.Price |
我們可以通過在視圖方法中創建一個字符串顯示我們需要的結果,並將其作為視圖模型對象傳遞給視圖。它也可以實現同樣的結果,但是這種實現方式破壞了MVC模式,並且減少了隨需變化的能力。正如我們說過的,我們需要回顧行為方法和視圖之前的准則。你應當記住,雖然MVC框架並沒有要求正確使用MVC模式,但是我們還是應該在設計和編碼時遵循MVC模式。
插入數據值
使用Razor表達能做的最簡單的事情就是向標記語言中插入數據。你可以使用@Model表達式引用視圖模型對象的屬性和方法,或使用@ViewBag表達式引用所定義的動態屬性。
你已經見過上面兩種情形的例子。但為了完整性,我們在Home控制器中,添加一個名為DemoExpressions的行為方法,它向視圖傳遞模型對象和viewbag。
publicActionResult DemoExpressions() { ViewBag.ProductCount = 1; ViewBag.ExpressShip = true; ViewBag.ApplyDiscount = false; ViewBag.Supplier = null;
return View(product); } |
然后,我們創建強類型的視圖DemoExpression.cshtml
@model MvcRazor.Models.Product
@{ ViewBag.Title = "DemoExpressions"; }
<table> <thead> <tr> <th>Property</th> <th>Value</th> </tr> </thead> <tbody> <tr> <td>Name</td> <td>@Model.Name</td> </tr> <tr> <td>Price</td> <td>@Model.Price</td> </tr> <tr> <td>Stock Level</td> <td>@ViewBag.ProductCount</td> </tr> </tbody> </table> |
我們創建了一個簡單的HTML table,並獲取模型對象和viewbag的屬性的值。該視圖的結果如下:
頁面看起來不太好,因為我們沒有對HTML元素應用CSS樣式。但這個例子強調了如何使用Razor表達式來顯示從行為方法傳遞到視圖的數據,
設置特性值
到目前為止的四個例子都是想元素設置內容,此外你還可以使用Razor表達式設置原色的特性。下面我們基於DemoExpress視圖,添加代碼實現對元素特性的設置
@model MvcRazor.Models.Product
@{ ViewBag.Title = "DemoExpressions"; }
<table> <thead> <tr> <th>Property</th> <th>Value</th> </tr> </thead> <tbody> <tr> <td>Name</td> <td>@Model.Name</td> </tr> <tr> <td>Price</td> <td>@Model.Price</td> </tr> <tr> <td>Stock Level</td> <td>@ViewBag.ProductCount</td> </tr> </tbody> </table>
<divdata-discount="@ViewBag.ApplyDiscount"data-express="@ViewBag.ExpressShip" data-supplier="@ViewBag.Supplier"> The containing element has data attributes </div>
Discount:<inputtype="checkbox"checked="@ViewBag.ApplyDiscount"/> Express:<inputtype="checkbox"checked="@ViewBag.ExpressShip"/> Supplier:<inputtype="checkbox"checked="@ViewBag.Supplier"/> |
我們使用基本的Razor表達式為div設置data-*特性的值。Data特性,它們是以data-為前綴的特性,已經成為非正式的創建自定義特性的方式很多年了,現在已經逐漸成為HTML5的正式的標准。我們通過ViewBag的屬性ApplyDiscount,ExpressShip和Supplier的值為DIV設置了對應的特性。
如果你運行程序,那么在瀏覽器中,你會看到DIV的特性的值已經正確地呈現出
False和True對應Viewbag的布爾值,請注意Razor已經對值為NULL的屬性做了特別的處理,因此data-supplier的值為空字符串。
當我們再次觀察生成的頁面,你會發現一件有趣的事情,那就是checkbox的checked特性
在MVC4中,Razor可以采用一種更有意識的方式使用像checked這樣的特性,其使用方式就是是否呈現該特性,而不是呈現該特性的值。如果向Razor插入了一個False、null或空白字符串作為chekced特性的值,那么顯示在瀏覽器中的checked的特性將被刪除。否則,將顯示為已選中的狀態
使用條件語句
Razor還可以處理條件語句,這就意味着我們可以從數圖中基於視圖數據的值調整輸出結果。我們開始接觸Razor的核心,它可以允許你創建愛你復雜並流暢的布局,同時它十分簡單,不僅容易閱讀還便於維護。下面的視圖展示了一個使用條件語句的視圖:
<tr> <td>Stock Level</td> <td> @switch ((int)ViewBag.ProductCount) { case 0: @: out of Stock break; case 1: <b>Low stock (@ViewBag.ProductCount)</b> break; default: @ViewBag.ProductCount break; } </td> </tr> |
若要使用條件語句,你應該放置@符號在C#條件關鍵字前,在我們的例子中,就是在switch前放置@符號。如同C#一樣,你使用}結束代碼片段。
在Razor代碼片段中,你可以通過HTML和Razor表達式使用HTML元素和視圖中的數據值。比如上面例子中的
@:<b>Low stock (@ViewBag.ProductCount)</b> |
我們並沒有把這些表達式放在引號或其他特殊的符號中,因為Razor引擎可以識別這些表達式,使其看起來仿佛按照常規方式處理的結果。但是,如果你先插入文本到視圖中,並且這個文本沒有包含在一個HTML元素中,那么你需要使用下面這種方式:
@: Out of stock |
@:使Razor將其后的內容當作C#語句,這也是Razor遇到文本輸出時的默認行為。運行應用程序,你可以在瀏覽器中看到如下的結果
條件表達式在Razor視圖中非常重要,因為它允許你根據行為方法產生的數據值調整視圖的內容。下面是另外一個例子,演示了如何使用if語句
<td> @if (ViewBag.ProductCount == 0) { @: Out of stock } elseif (ViewBag.ProductCount == 1) { <b>Low stock (@ViewBag.ProductCount)</b> }else{ @ViewBag.ProductCount } </td> |
上面的條件與switch語句產生相同的結果,但我們希望向你演示如何使用C#條件語句。在第18章中,我們將做更詳細的介紹。
枚舉數據和集合
當編寫一個MVC程序時,你可能經常希望枚舉一個數組或一些其他類型的集合,然后根據每個子項生成內容。為了演示如何實現這個目的,我們在Home控制器中定義一個新的行為方法DemoArray
publicActionResult DemoArray() { Product[] products = { newProduct {Name = "Kayak", Price = 275M}, newProduct {Name = "Lifejacket", Price = 48.95M}, newProduct {Name = "Soccer ball", Price = 19.50M}, newProduct {Name = "Corner flag", Price = 34.95M} };
return View(products); } |
該行為方法創建一個Product[]對象,它包含一些簡單的數據值並傳遞給View方法,以使數據可以通過默認的視圖呈現。
在創建視圖時,Visual studio並沒有提供數組和集合的支持,因此你需要手動設置模型類的類型
然后在生成的視圖中,你可以看到model的類型為:@model MvcRazor.Models.Product[]。下面是生產視圖的代碼
@model MvcRazor.Models.Product[]
@{ ViewBag.Title = "DemoArray"; Layout = "~/Views/_BasicLayout.cshtml"; }
@if (Model.Length > 0) { <table> <thead><tr><th>Product</th><th>Price</th></tr></thead> <tbody> @foreach (MvcRazor.Models.Product product in Model) { <tr> <td>@product.Name</td> <td>@product.Price</td> </tr> } </tbody> </table> } |
我們使用@if語句去判斷返回的數組長度,然后根據長度生成對應的內容。然后使用@foreach表達式枚舉數組的內容並在HTML的table中每條數據生成一個html的行。你可以從上面的代碼中看到,這些表達式是如何與C#對應的,還可以看到我們在foreach循環中創建了一個本地變量p,然后通過該變量@p.Name和@p.Price引用了該變量的屬性。
運行結果為:
處理命名空間
你可能已經注意到,我們在foreach循環中,引用了Product的完整名(包含了命名空間)【注:其實我們在MVC4中,也可以直接使用var】。
@foreach (MvcRazor.Models.Product product in Model) { |
這在復雜的視圖中,會令人懊惱,因為很可能你會在多個地方都需要引用視圖模型和其他類(那么每個都需要包含命名空間)。我們可以通過應用@using表達式來簡化我們的代碼
@using MvcRazor.Models @model Product[]
@{ ViewBag.Title = "DemoArray"; Layout = "~/Views/_BasicLayout.cshtml"; }
@if (Model.Length > 0) { <table> <thead><tr><th>Product</th><th>Price</th></tr></thead> <tbody> @foreach (Product product in Model) { <tr> <td>@product.Name</td> <td>@product.Price</td> </tr> } </tbody> </table> } |
視圖可以包含多個@using表達好似。上面的例子中我們使用@using表達式引入了MvcRazor.Modes命名空間,這就意味着我們可以在foreach循環中取出命名空間。
總結
在本章,我們概覽了Razor視圖引擎,已經如何使用它來生成HTML。我們還為你展示了如何通過視圖模型對象和Viewbag對象引用控制器傳遞過來的數據,此外我們還介紹了如何使用Razor表達式呈現數據。在本書的后續章節你還會看到Razor的一些其他的例子;此外在第十八章,我們會詳細介紹MVC視圖的工作機制。在下一章,我們將描述開發和測試MVC的一些基本的工具;從而使你可以在你的項目中更好的使用這些工具。
【請尊重勞動成果、轉載請注明來源】