精通ASP.NET MVC ——视图
文章非常長,僅僅用于記錄自己學習。?
創建自定義視圖引擎
創建自定義視圖引擎的價值是,演示請求處理管道如何工作,并完善關于MVC架構如何操作的知識,視圖引擎實現IViewEngine接口,如下圖所示:
public interface IViewEngine{ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache);ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache);void ReleaseView(ControllerContext controllerContext, IView view);}視圖引擎的作用是將對視圖的請求轉化成ViewEngineResult對象。?這個接口中兩個方法是FindView? 和 FindPartialView,給它們傳遞的是描述請求的參數:處理該請求的控制器(ControllerContext對象)、視圖名及其布局,以及是否允許視圖引擎重用其緩存結果。當框架對ViewResult進行處理時,會調用這個兩個方法。最后一個方法是ReleaseView(釋放視圖),當視圖不在需要時被調用(從其名稱不難看出,該方法的作用是釋放視圖所占用的資源)。
當請求一個視圖時,ViewEngineResult類使視圖引擎能夠對MVC框架做出響應。代碼如下圖所示:
using System.Collections.Generic;namespace System.Web.Mvc {public class ViewEngineResult{public IEnumerable<string> SearchedLoactions{get;private set;}public IView View {get;private set;}public IViewEngine ViewEngine{get;private set;}public ViewEngineResutlt(IEnumerable<string> searchedLoactions){if(searchedLoactions == null){throw new ArgumentNullException("searchedLocations");}SearchedLoactions = searchedLoactions}public ViewEngineResutlt(IView view,IViewEngine viewEngine){if(view == null){throw new ArgumentNullException("view");}if(viewEngine == null){throw new ArgumentNullException("viewEngine");}View = view;ViewEngine = viewEngine;}} }可以通過兩個構造器中的其中一個來表示一個結果,如果視圖引擎能夠對請求提供視圖,那么可以用以下構造器創建一個ViewEngineResult:?
public ViewEngineResutlt(IView view,IViewEngine viewEngine)如果視圖引擎不能對請求提供視圖,那么可以使用如下構造器: 這一版本是查找視圖位置的一個枚舉。如果找不到視圖,就會將枚舉的信息顯示給用戶。
public ViewEngineResutlt(IEnumerable<string> searchedLoactions)視圖引擎的最后一個構造塊是IView接口,如下圖所示:?
public interface IView{void Render(ViewContext viewContext, TextWriter writer);}把一個IView實現傳遞給ViewEngineResult對象的構造器,然后它會被視圖引擎方法所返回。MVC框架會調用Render方法(把IView實現傳遞給ViewEngineResult對象構造器時,自然便會調用IView接口中的這個Render方法)。 到目前為止,ViewContext對象定義了一些屬性,這些屬性給你提供請求信息以及MVC框架如何處理它的細節。
| 名稱 | 描述 |
| Controller | 返回處理當前請求的IController實現 |
| RequestContext | 返回當前請求的細節 |
| RouteData | 為當前請求返回的路有數據 |
| TempData | 返回和請求相關的臨時數據 |
| View | 返回將要處理請求的IView接口的實現。很明顯,如果你正在創建一個自定義視圖實現,它將是當前類。 |
| ViewBag | 返回一個表示視圖的object |
| ViewData | 返回一個包含模式視圖包和元數據的視圖模型數據字典。 |
這些屬性中最有趣的是ViewData,它返回一個ViewDataDictionary對象。定義了一些有用的屬性,如下圖所示:
| 名稱 | 描述 |
| keys | 為字典中的數據返回鍵值集合,他們可用來訪問視圖包屬性 |
| Model | 為請求返回視圖模型對象 |
| ModelMetadata | 返回一個可以用來反映模型類型的ModelMetadata對象 |
| ModelState | 返回有關模型的狀態信息 |
根據以上知識,創建一個自定義視圖引擎,?首先新建一個空白項目,創建一個Home控制器,如下圖所示:
public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Messge = "Hello,World";ViewBag.Time = DateTime.Now.ToShortTimeString();return View("DebugData");}public ActionResult List(){return View();}}創建一個自定義IView,在項目中新建一個Infrastructure的文件夾,創建一個DebugDataView.cs的新的類文件,如下圖所示:?
namespace WebApplication1.Infrastructure {public class DebugDataView : IView{public void Render(ViewContext viewContext, TextWriter writer){Write(writer, "---Routing Data---");foreach (string key in viewContext.RouteData.Values.Keys){Write(writer, "key:{0},value:{1}", key, viewContext.RouteData.Values[key]);}Write(writer, "---View Data---");foreach (string key in viewContext.ViewData.Keys){Write(writer,"key:{0},value:{1}",key,viewContext.ViewData[key]);}}private void Write(TextWriter writer, string template, params object[] values){writer.Write(string.Format(template,values) + "<p>");}} }?
視圖引擎的目的是產生一個ViewEngineResult對象,它或者包含一個IView,或者是一個用于搜索適當視圖的位置列表。現在已經有了IView的實現,于是可以創建視圖引擎,在Infrastructure的文件夾下新建一個DebugDataViewEngine.cs的類文件,代碼如下圖所示:
namespace WebApplication1.Infrastructure {public class DebugDataViewEngine : IViewEngine{public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache){return new ViewEngineResult(new string[] { "No View (Debug Data View Engine)" });}public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache){if (viewName == "DebugData"){return new ViewEngineResult(new DebugDataView(), this);}else{return new ViewEngineResult(new string[] { "No view (Debug Data View Engine)" });}}public void ReleaseView(ControllerContext controllerContext, IView view){}} }注冊自定義視圖引擎?
視圖引擎需要在 Global.asax 的 Application_Start 方法中進行注冊,代碼如下圖所示:
ViewEngines.Engines.Add(new DebugDataViewEngine());靜態的ViewEngine.Engines集合含有一組在應用程序中安裝的視圖引擎。MVC框架支持在一個單一的應用程序中安裝多個引擎。當處理一個ViewResult時,動作調用器獲取這組已經安裝的視圖引擎,并依次調用它們的FindView方法。
一旦動作調用接收到一個含有IView方法的ViewEngineResult對象。便會停止調用FindView方法。如果有兩個或者多個引擎能夠對同視圖的請求進行服務,這意味著在ViewEngines.Engines集合中添加引擎的順序是很重要的,代碼如下圖所示:
ViewEngines.Engines.Insert(0,new DebugDataViewEngine());測試視圖引擎?
運行程序,效果如下圖所示:
? ? ? ? ? ?
如果導航到/Home/List,該動作方法調用View方法請求其默認視圖,這是一個我們不支持的視圖,就會報錯,如下圖所示:?
? ? ? ? ? ??
從上述報錯來看,Razor和ASPX視圖也在列表中,可以清除其他類型的視圖引擎,?代碼如下:
ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(new DebugDataViewEngine());理解Razor視圖渲染
Razor視圖引擎會編譯應用程序中的視圖,以改善性能。視圖會被轉化成C#類,然后被編譯。這是在視圖中能夠如此方便地包含C#代碼片段的原因。
程序啟動前,視圖不會被編譯。只有程序啟動后才能觸發視圖編譯過程。出于方便,會將視圖文件生成的類寫成磁盤上的C#代碼,然后進行編譯,這意味著能夠看到一個視圖的C#語句。這些語句可以在C盤的某些隱蔽的目錄下被找到,并且文件名和它們所包含的類名不對應。
配置視圖搜索位置
在查找視圖時,Razor視圖引擎遵循MVC框架早期版本建立起來的約定。例如,如果請求與Home控制器先關的Index視圖時,Razor會審查一下視圖列表:
/Views/Home/Index.cshtml
/Views/Home/Index.vbhtml
/Views/Shared/Index.cshtml
/Views/Shared/Index.vbhtml
Razor實際上不會在磁盤上查找這些視圖文件,因為它們還沒有被編譯成C#文件。Razor查找的是表示這些視圖的編譯類。.cshtml文件是含有C#語句的模板,而.vbhtml文件含有Visual Basic語句。
| 屬性 | 描述 | 默認值 |
| ViewLoactionFormats MasterLocationFormatsPartiaView LocationFormats | 查找視圖、分部視圖、以及布局的位置 | ~/ Views / {1} / {0}.cshtml, ~/ Views/ {1} / {0}.vbhtml, ~/ Views/ Shared / {0}.cshtml, ~/ Views/ Shared/ {0}.vbhtml, |
| AreaViewLocationFormats AreaMasterLocationFormats AreaPartialViewLocationForms | 為一個區域查找視圖、分部視圖,以及布局位置 | ~/ Areas / {2} / Views / {1} / {0}.cshtml, ~/ Areas / {2} / Views / {1} / {0}.vbhtml, ~/ Areas / {2} / Views / Shared / {0}.cshtml, ~/ Areas / {2} / Views / Shared / {0}.vbhtml |
這些屬性是在Razor之前引入的,這是每組三個屬性具有相同值的原因。每個屬性都是一個字符串數組,它們是用合成字符串格式化符號來表示的。以下是與占位符對應的參數值:
{0} 表示視圖名。
{1} 表示控制器名。
{2} 表示區域名。
通過創建一個RazorViewEngine子類,可以改變Razor搜索的視圖文件。?在Infrastructrue文件夾中,創建了一個名稱為CustomerLocationViewEngine的視圖引擎,代碼如下圖所示:
public class CustomLocationViewEngine:RazorViewEngine{public CustomLocationViewEngine(){ViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml","~/Views/Common/{0}.cshtml"};}}以上代碼對?ViewLoactionFormats設置了一個新值。新數組只包含用于.cshtml文件的條目。此外,已經將查找共享視圖的位置修改為了View/Common,而不是Views/Shared。并且在Global.asax的Application_Start方法中,注冊此引擎:
ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(new CustomLocationViewEngine());最后創建/View/Common文件夾,并添加一個名稱為List.cshtml的視圖文件。?代碼如下圖所示:
@{Layout = null; }<!DOCTYPE html><html> <head><meta name="viewport" content="width=device-width" /><title>List</title> </head> <body><div> <h3>This is the /Views/Common/List.cshtml View</h3></div> </body> </html>啟動程序,并導航到/Home/List時,執行效果如下圖所示:
? ? ? ? ? ? ?
?
?使用分段
Razor引擎支持分段的概念,Razor分段能夠靈活的控制將視圖的哪一個部分插入到布局之中,以及把他們插入到哪里。
下面對/Views/Home/Index.cshtml文件進行編輯,如下圖所示:
@model string[] @{Layout = "~/Views/Shared/_Layout.cshtml"; }@section Header{<div class="view">@foreach (string str in new[] {"Home","List","Edit" }){@Html.ActionLink(str,str,null,new { style = "margin:5px"})}</div> }<div class="view">This is a lists of fruit names@foreach (string name in Model){<span><b>@name</b></span>} </div>@section Footer {<div class="view">This is the footer</div> }在上圖代碼中,創建了名稱為“Header” 和 “Footer”的分段。分段的內容可以混用HTML標記和Razor標簽。你可以在布局中使用@RenderSection 輔助器方法來指定分段要插入的位置。修改Views/Shared/_Layout.cshtml文件,代碼如下圖所示:
<!DOCTYPE html><html> <head><meta name="viewport" content="width=device-width" /><style type="text/css">div.layout {background-color:lightgray;}div.view {border:thin solid black;margin:10px 0;}</style><title>@ViewBag.Title</title> </head> <body>@RenderSection("Header")<div class="layout">This is part of the layout</div>@RenderBody()<div class="layout">This is part of Layout</div>@RenderSection("Footer")<div class="layout">This is part of Layout</div> </body> </html>在Razor對布局進行解析時,RenderSection輔助器方法會顯示視圖中指定名稱的分段內容。視圖中未包含在分段中的內容會插入布局中使用RenderBody輔助器方法。?運行程序,結果如下圖所示:
? ? ? ? ? ? ? ? ? ???
注意:一個視圖只能定義在布局中被引用的分段。如果視圖在視圖布局中午沒有對應的@RendSection輔助器調用分段,MVC框架會報錯。?
一般情況下,不要分段與視圖的其余的部分混雜在一起。其約定是,在視圖的開始或者結尾部分定義分段,以便更容易看到哪些內容區域被處理成分段,對于那些需要RenderBody輔助器捕捉的內容,可以把這些定義成一個獨立的分段,如下圖所示:
@model string[] @{Layout = "~/Views/Shared/_Layout.cshtml"; }@section Header{<div class="view">@foreach (string str in new[] {"Home","List","Edit" }){@Html.ActionLink(str,str,null,new { style = "margin:5px"})}</div> }@section Body {<div class="view">This is a lists of fruit names@foreach (string name in Model){<span><b>@name</b></span>}</div> }@section Footer {<div class="view">This is the footer</div> }以上這種辦法有利于建立更清晰的視圖,并減少了RenderBody捕捉無關內容的情況。為了使用這種方法,我們得用Rend而Section("Body")替換對RenderBody輔助器的調用。?代碼如下圖所示:
<!DOCTYPE html><html> <head><meta name="viewport" content="width=device-width" /><style type="text/css">div.layout {background-color:lightgray;}div.view {border:thin solid black;margin:10px 0;}</style><title>@ViewBag.Title</title> </head> <body>@RenderSection("Header")<div class="layout">This is part of the layout</div>@RenderSection("Body")<div class="layout">This is part of Layout</div>@RenderSection("Footer")<div class="layout">This is part of Layout</div> </body> </html>對分段進行測試
檢查一個視圖是否已經定義了布局中的一個特定的分段,可以使用如下代碼,修改了_layout.cshtml:
@if (IsSectionDefined("Footer")) {@RenderSection("Footer") } else {<h4> This is the deafault footer </ht> }渲染可選分段?
默認情況下,視圖中必須含有布局中的RendSection的所有分段。如果缺少分段,MVC框架將會報錯,修改_layout.cshtml代碼并運行,結果如下圖所示:
?
定義可選分段,只需加上一個false值即可:如果視圖定義了它,其內容將被插入到結果中,否則也不會拋出異常,代碼如下圖所示:
@RenderSection("scripts",false)?
使用分部視圖
通常需要在應用程序中多個不同的地方,使用同樣的Razor標簽 和 HTML 標記片段。采取的辦法不是重復這些標記,而是采用分部視圖(Partial View).
新建一個名稱為MyPartial.cshtml分部視圖,代碼如下圖所示:
<div>This is the massage from the partial view.@Html.ActionLink("This is a link to the Index action", "Index"); </div>修改List.cshtml頁面,代碼如下圖所示:?
@{Layout = null; }<!DOCTYPE html><html> <head><meta name="viewport" content="width=device-width" /><title>List</title> </head> <body><div> <h3>This is the /Views/Common/List.cshtml View</h3></div> </body> </html> @Html.Partial("MyPartial")提示:Razor視圖引擎對分部視圖的查找方式,于規則視圖相同?(即在~/Views/<controller> 和 ~/Views/Shared文件夾中進行查找)。這意味著,可以創建控制器專用的的特殊版本的分部視圖,它會覆蓋Shared文件夾下進行查找。
運行程序,效果如下:?
? ? ? ? ? ?
使用強類型分部視圖?
可以創建強類型分部視圖,然后在渲染這個分部視圖時,傳遞要使用的視圖模型對象。
新建一個名為MyStrongTypedPartial.cshtml的分部視圖,代碼如下圖所示:
@model IEnumerable<string><div>This is message from the partial view.<ul>@foreach (string str in Model){<li>@str</li>}</ul> </div>并修改/View/Common/List.cshtml文件,代碼如下所示:?
@{Layout = null; }<!DOCTYPE html><html> <head><meta name="viewport" content="width=device-width" /><title>List</title> </head> <body><div> <h3>This is the /Views/Common/List.cshtml View</h3></div> </body> </html> @Html.Partial("MyStronglyTypedPartial",new[] {"Apple","Orange","Pear" })運行效果如下圖所示:
? ? ? ? ? ? ?
?
使用子動作?
子動作是通過視同調用的動作方法。當你希望將某種控制器邏輯用于應用程序的多個地方時,子動作可以讓你避免重復的控制器邏輯。子動作和動作之間的關系,如同分部視圖和視圖的關系一樣。無論何時,當希望顯示某些數據驅動的“小部件”,這些“小部件”要出現在多個頁面上,而且含有與主動作無關的數據時,你可能就會希望使用子動作。
創建一子動作,代碼如下圖所示:
[ChildActionOnly]public ActionResult Time(){return PartialView(DateTime.Now);}新增Time.cshtml頁面,代碼如下圖所示:?
@model DateTime<p> The time is @Model.ToShortDateString()</p>啟動程序,再次導航到/Home/List,效果如下圖所示:
? ? ? ? ? ?
通過提供一個匿名類型對象,其屬性對應于子動作方法的參數名,可以將參數傳遞給動作方法,代碼如下圖所示:
[ChildActionOnly]public ActionResult Time(DateTime time){return PartialView(DateTime.Now);} @Html.Action("Time",new { time = DateTime.Now})?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
總結
以上是生活随笔為你收集整理的精通ASP.NET MVC ——视图的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Win7搭建http文件共享
- 下一篇: linux中如何统计目录中的文件,[Li