008_视图
創建自定義視圖引擎
一般情況下直接使用MVC框架自帶的內建視圖引擎即可,但如果想知道視圖引擎是如何工作的,就需要從建立一個自定義視圖引擎開始了。通過之前的學習我們都知道了內建視圖引擎包括Razor和ASPX兩種,ASPX是針對舊版本MVC程序的,他主要是維護舊版本MVC應用程序,保持系統的兼容性而保留的Web Form視圖引擎;Razor是在MVC3引入的,它的語法更加簡潔。
現在我們就先從自定義視圖引擎開始,了解一下視圖引擎的工作機制。視圖引擎的接口是IViewEngine,其結構如下:
命名空間:System.Web.Mvc
方法:
1、? FindPartialView
參數[類型]:
- controllerContext[ControllerContext]
- partialViewName[string]
- useCache[bool]
返回值:ViewEngineResult
2、? FindView
參數[類型]:
- controllerContext[ControllerContext]
- viewName[string]
- masterName[string]
- useCache[bool]
返回值:ViewEngineResult
3、? ReleaseView
參數[類型]:
- controllerContext[ControllerContext]
- view[IView]
返回值:ViewEngineResult
???????? 前兩個方法(FindPartialView、FindView)接收的參數是描述請求的:處理該請求的控制器、視圖名及布局。當框架對ViewResult進行處理時,會調用這兩個方法。最后一個方法(ReleaseView)在視圖不再需要時被調用,其功能就是要釋放視圖所占用的資源。
注:MVC框架對視圖引擎的支持是由ControllerActionInvoker(控制器動作調用器)類實現的,這是IActionInvoker接口的內建實現。如果已經直接通過IActionInvoker或IControllerFactory接口實現了自己的動作調用器或控制器工廠,將無法自動地訪問視圖引擎特性。
當請求一個視圖時,ViewEngineResult類使試圖引擎能夠對MVC框架作出響應。當視圖引擎能夠對請求提供視圖時,將通過如下構造函數創建一個ViewEngineResult:
public ViewEngineResult(IView view, IViewEngine viewEngine)
???????? 當視圖不能對請求提供視圖時,則使用如下構造函數:
public ViewEngineResult(IEnumerable<string> searchedLocations)
???????? 該重載版本的構造函數是通過參數的視圖位置的集合進行枚舉查找并創建ViewEngineResult的,如果找不到視圖,則該枚舉的信息會顯示給用戶。
???????? 視圖引擎系統的最后一個構造塊是IVew接口:
namespace System.Web.Mvc {public interface IView{void Render(ViewContext viewContext, TextWriter writer);} }???????? 該接口中定義的Render方法的ViewContext類型參數傳遞了客戶端請求的信息,以及動作方法的輸出。TextWriter類型參數則用于將輸出寫給客戶端。
???????? 在了解了視圖引擎的構造組成后,就來創建一個簡單的視圖引擎做一下深入的研究,我們的視圖引擎簡單到何種地步呢,我們只讓其返回一個視圖,該視圖將渲染關于請求的信息,以及動作方法產生的視圖數據。這樣一來既能演示視圖引擎的操作方式,也不會陷入解析視圖模板的困境。
創建示例項目
項目模板:Empty
項目名稱:Views
控制器:Home
Home控制器代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc;namespace Views.Controllers {public class HomeController : Controller{//// GET: /Home/public ActionResult Index(){ViewData["Message"] = "Hello, World";ViewData["Time"] = DateTime.Now.ToShortTimeString();return View("DebugData");}public ActionResult List(){return View();}} }?
實現自定義的IView
自定義IView實現類:DebugDataView
位置:Infrastructure
代碼清單:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.IO;namespace Views.Infrastructure {public class DebugDataView : IView{public void Render(ViewContext viewContext, System.IO.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/>");}} }該演示代碼中演示了Render方法的兩個參數的用法:取得ViewContext,并用TextWriter向客戶端寫出響應。在后面的自定義視圖引擎的實現中,我們慢慢地會明白該類的功能。
實現自定義的IViewEngine
???????? 一定要明白視圖引擎的目的是產生一個ViewEngineResult對象,它或者包含一個IView,或是一個用于搜索適當視圖的位置列表。
自定義IView實現類:DebugDataViewEngine
位置:Infrastructure
代碼清單:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc;namespace Views.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){// do nothing... }} }本示例僅實現了針對單一的視圖DebugData的支持,如果實現的是更嚴格的視圖引擎,可以進行模板的搜索、考慮布局和提供緩存設置。
IviewEngine接口假設視圖引擎有它需要查找的地方。但這里不需要查找任何地方,因此只返回一個啞元位置(Dummy Location),以表明不能交付視圖。
該自定義視圖還不支持分部視圖,因此,通過FindPartialView方法返回一個結果,以表明其不能提供視圖。
由于這里沒有需要釋放的資源,我們也就沒有實現ReleaseView方法。
注冊自定義視圖引擎
視圖引擎需要在Global.asax的Application_Start方法中注冊,如:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Routing; using Views.Infrastructure;namespace Views {public class MvcApplication : System.Web.HttpApplication{protected void Application_Start(){AreaRegistration.RegisterAllAreas();ViewEngines.Engines.Add(new DebugDataViewEngine()); WebApiConfig.Register(GlobalConfiguration.Configuration);FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);RouteConfig.RegisterRoutes(RouteTable.Routes);}} }靜態的ViewEngines.Engines集合中包含一組程序中按照的視圖引擎。MVC框架也支持在一個程序中存在多個引擎。當處理一個ViewResult時,動作調用器獲取這組已安裝的視圖引擎,并依次調用它們的FindView方法。
一旦動作調用器接收到一個含有IView的ViewEngineResult對象,便會停止調用FindView方法。如果有兩個或多個引擎能夠對同視圖名的請求進行服務,這意味著在ViewEngines.Engines集合中添加引擎的順序是重要的。如果希望引擎取得優先,可以將它插入在該集合的開始部分,如:
ViewEngines.Engines.Insert(0,new DebugDataViewEngine());
測試自定義視圖引擎
此時啟動程序,便可測試這個視圖引擎了。效果如圖:
? ? ? ? ? ? ? ? ? ? ? ?
這是Home控制器的Index方法通過View方法返回了指向DebugData視圖的ViewResult產生的結果。但如果導航到:/Home/List,由于該動作方法返回了一個不受支持的默認視圖,將會得到如下結果:
?
???????? 從上圖紅框的位置可以看出,消息是作為一條搜索視圖的位置來報告的。注意Razor和ASPX視圖也出現在列表中,這是因為這些視圖引擎仍然起作用。如果只希望使用自定義的視圖引擎,則必須在Global.asax的Application_Start方法中將其清除,具體做法如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Routing; using Views.Infrastructure;namespace Views {public class MvcApplication : System.Web.HttpApplication{protected void Application_Start(){AreaRegistration.RegisterAllAreas();ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(new DebugDataViewEngine());WebApiConfig.Register(GlobalConfiguration.Configuration);FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);RouteConfig.RegisterRoutes(RouteTable.Routes);}} }現在,重新導航到/Home/list將會看到下圖的效果:
?
使用Razor引擎
前面實現的自定義視圖僅僅是生成了一個十分簡陋的視圖,而且對于視圖引擎的復雜性方面的實現一點都沒有做,但是,這已經足夠讓我們明白視圖引擎的工作機制了。
視圖引擎的復雜度真正來源于視圖模板系統,包括:代碼片段、支持布局,以及為優化性能而對模板進行的編譯等。
Razor幾乎可以滿足所有的MVC應用程序,只有十分罕見的項目需要創建自定義視圖。
示例項目
對于后面想演示,需要再創建一個新的示例項目,使用的模板是Basic模板,項目名稱為WorkingWithRazor,并創建一個Home控制器:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc;namespace WorkingWithRazor.Controllers {public class HomeController : Controller{public ActionResult Index(){string[] names = { "Apple", "Orange", "Pear" };return View(names);}} }???????? 該Home控制器的Index動作方法對應的Index視圖如下:
@model string[]@{ViewBag.Title = "Index"; }This is a list of fruit names:@foreach (string name in Model) {<span><b>@name</b></span> }?
Razor視圖的渲染
Razor視圖引擎會將視圖轉換成C#類,然后將其進行編譯。這樣做的目的其一就是為了改善性能,同時這也是在視圖中能夠如此方便地包含C#代碼片段的原因。
在程序啟動之前,MVC中的視圖不會被編譯。因此,要查看Razor創建的類,需要啟動程序,并導航到/Home/Index動作。發送給MVC程序的最初請求會觸發所有視圖的編譯過程。下圖中可以看出該請求的輸出:
?
出于方便,會將視圖文件生成的類寫成磁盤上的C#代碼文件,然后進行編譯,也就是說我們可以在本機磁盤中找到這個文件,但是要想找到這個文件還是很不容易的——因為,需要通常為隱藏的文件夾,而且這些.cs文件名與它們所包含的類名不對應。但對于WIN10系統的存放位置一般是在:C:\Users\Administrator(這是我的機器登錄用戶名)\AppData\Local\Temp\Temporary ASP.NET Files目錄下。對于該示例在本人機器中的路徑為:root\0e20f253\3ae26c3f,其對應的視圖為(為了方便閱讀做了些整理):
namespace ASP {using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Net;using System.Web;using System.Web.Helpers;using System.Web.Security;using System.Web.UI;using System.Web.WebPages;using System.Web.Mvc;using System.Web.Mvc.Ajax;using System.Web.Mvc.Html;using System.Web.Optimization;using System.Web.Routing;public class _Page_Views_Home_Index_cshtml : System.Web.Mvc.WebViewPage<string[]>{public _Page_Views_Home_Index_cshtml(){}protected ASP.global_asax ApplicationInstance{get{return ((ASP.global_asax)(Context.ApplicationInstance));}}public override void Execute(){ViewBag.Title = "Index";WriteLiteral("\r\n\r\nThis is a list of fruit names:\r\n\r\n");foreach (string name in Model){WriteLiteral(" <span><b>");Write(name);WriteLiteral("</b></span>\r\n");}}} }???????? 其實對于我在查找的時候,有一個讓我很欣慰的是當我打開文件時,對于視圖對應編譯后的類文件都有原文件路徑的指示,類似于這樣:
#pragma checksum "E:\XXX(你的項目路徑)\WorkingWithRazor\Views\Home\Index.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "0709F63862595E77163CDD7CA8667BF6CAC0664A"
???????? 從上面可以看出,這個類派生于WebViewPage<T>(這里的T為:string[]),而且從類名也能看出視圖文件的路徑已經被編譯到類名之中(_Page_Views_Home_Index_cshtml)。
在Execute方法中可以看出視圖的語句和元素的處理時這樣的:
- 以@符號為前綴的代碼片段被直接表示成了C#語句。如:
@{
? ViewBag.Title = "Index";
}
被轉為了:
ViewBag.Title = "Index";
- HTML元素則以WriteLiteral方法處理,它將參數的內容寫成了這些元素所給出的結果。這與Write方法相反,WriteLiteral方法用于C#變量并對字符串值進行編碼,以使它們能夠安全地用于HTML頁面。
如:This is a list of fruit names:
被轉換成了:
WriteLiteral("\r\n\r\nThis is a list of fruit names:\r\n\r\n");
Write方法和WriteLiteral方法都是將內容寫到一個TextWriter對象(這是傳遞給IView.Render方法的同一個對象)。編譯Razor視圖的目的是生成靜態和動態內容,并通過TextWriter將內容發送給客戶端。
配置視圖搜索位置
Razor視圖引擎在查找視圖時遵循的是MVC框架早期版本建立約定。如Home控制器中的Index動作方法的視圖,將會查找~/Views/Home/和~/Views/Shared/路徑下的.cshtml(一個含有C#語句的模板)和.vbhtml(一個含有Visual Basic語句的模板)文件。Razor實際上不會在磁盤上查找這些視圖文件(因為它們還沒有被編譯成C#類),而是查找表示這些視圖的編譯類。
???????? 通過實現RazorViewEngine類的子類可以改變Razor搜索的視圖文件。該類是IViewEngine的Razor實現。它建立于一系列基類之上,這些類定義了一組用來確定搜索視圖文件的屬性,具體如下:
- 頂層視圖的查找(相對于區域層的視圖,位于項目的Views文件夾下):
1.屬性:
?ViewLocationFormats
?MasterLocationFormats
?PartialViewLocationFormats
2.描述:查找視圖、分部視圖以及布局的位置
3.默認值:
?~/Views/{1}/{0}.cshtml
?~/Views/{1}/{0}.vbhtml
?~/Views/Shared/{0}.cshtml
?~/Views/Shared/{0}.vbhtml
- 區域層的視圖查找:
1.屬性:
?AreaViewLocationFormats
?AreaMasterLocationFormats
?AreaPartialViewLocationFormats
2.描述:為一個區域查找視圖、分部視圖以及布局的位置
3.默認值:
?~/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的子類,并修改上述屬性的一個或多個屬性值即可。下面在示例項目中添加一個Infrastructure文件夾,并添加一個視圖引擎CustomLocationViewEngine來看看具體的操作,如:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc;namespace WorkingWithRazor.Infrastructure {public class CustomLocationViewEngine : RazorViewEngine{public CustomLocationViewEngine(){ViewLocationFormats = new string[]{"~/Views/{1}/{0}.cshtml","~/Views/Common/{0}.cshtml"};}} }???????? 現在需要做的是在Global.asax的Application_Start方法中進行注冊即可:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WorkingWithRazor.Infrastructure;namespace WorkingWithRazor {public class MvcApplication : System.Web.HttpApplication{protected void Application_Start(){AreaRegistration.RegisterAllAreas();ViewEngines.Engines.Clear();ViewEngines.Engines.Add(new CustomLocationViewEngine());WebApiConfig.Register(GlobalConfiguration.Configuration);FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);RouteConfig.RegisterRoutes(RouteTable.Routes);BundleConfig.RegisterBundles(BundleTable.Bundles);}} }???????? 如果想要僅使用自定義視圖引擎,則先需要使用Clear方法將可能已被注冊的視圖引擎清除,然后使用Add方法添加自定義的實現。
???????? 為了能夠使自定義視圖引擎能夠正常工作,需要在Views文件夾中創建一個Common文件夾,并在其中實現一個List.cshtml視圖文件,如:
@{ViewBag.Title = "List"; }<h3>This is the /Views/Common/List.cshtml</h3>???????? 然后繼續在Home控制器中添加一個List動作方法來顯示該視圖:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc;namespace WorkingWithRazor.Controllers {public class HomeController : Controller{public ActionResult Index(){string[] names = { "Apple", "Orange", "Pear" };return View(names);}public ActionResult List(){return View();} } }???????? 現在啟動程序并導航至/Home/List時,將會使用自定義的位置查找/Views/Common文件夾中的List.cshtml視圖文件,如圖:
?
對Razor視圖添加動態內容
所謂動態內容就是在運行時生成,并且隨每一個請求而不同。這與靜態內容恰好相反,在編寫應用程序時,它的內容就已經生成了,且對每一次請求其內容都是一樣的。添加動態內容的方式有多種,如下:
- 內聯代碼
用于小型的、自包含視圖邏輯片段,如if和foreach語句。這是在視圖中創建動態內容的基本手段,也是一些其他辦法的基礎。
- HTML輔助器方法
用于生成一個獨立的HTML元素或小片元素集合,典型地,是基于視圖模型或視圖數據的值。MVC包含了許多有用的HTML輔助器方法,而且創建自己的輔助器方法也很容易。
- 分段
用于創建內容分段,這種分段用于插入到布局特定位置。
- 分部視圖
用于在視圖之間共享的子片段標記。分部視圖也可以含有內聯代碼、HTML輔助器方法,以及引用其他分部視圖。分部視圖不調用動作方法,因此它們不能用來執行事務邏輯。
- 子動作
用于創建可重用的UI控件,或需要含有事務邏輯的小部件。當使用子動作時,它調用一個動作方法,返回一個視圖,并把結果注入到響應流中。
使用分段
分段(Section)是用來在布局中提供內容區域的,它能靈活地控制將視圖的哪一部分插入到布局中,以及將它們插入何處。請看下面一個示例:
在視圖中定義分段:
@model string[]@{ViewBag.Title = "Index"; }@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 list of fruit names:@foreach (string name in Model){<span><b>@name</b></span>} </div>@section Footer{<div class="view">This is the footer</div> }?
???????? 這個示例修改了Index視圖,其采用的定義格式為:@section <分段名稱>(如示例中的“Header”和“Footer”分段)。分段的內容可以混用HTML標記和Razor標簽。
???????? 還可以通過@RenderSection輔助器方法指定分段要插入的位置。如對_Layout布局的修改:
提示:此時使用的仍是自定義視圖引擎,盡管共享視圖放在了/Views/Common文件夾,但共享布局仍位于/Views/Shared文件夾。
???????? 在布局中使用分段:
<!DOCTYPE html> <html> <head><meta charset="utf-8" /><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 the layout</div>@RenderSection("Footer")<div class="layout">This is part of the layout</div> </body> </html>???????? 在Razor對布局進行解析時,RenderSection輔助器方法會顯示視圖中指定名稱的分段內容。視圖中未包含分段的內容,會插入在布局中使用RenderBody輔助器的地方。效果如圖:
?
注:一個視圖只能定義在布局中被引用的分段。如果試圖在視圖中定義布局中無對應的@ RenderSection輔助器調用的分段,MVC框架將會拋出異常。同時,默認情況下,視圖必須含有布局中調用@RenderSection的所有分段,如果缺少,MVC框架同樣會拋出異常。
???????? 一般情況下,不用把分段和視圖的其余部分混雜在一起。約定是在視圖的開始或結尾部分定義分段,以便更容易看到哪些內容區域被處理成分段,以及哪些將要由RenderBody輔助器來捕捉。推薦一種做法:把視圖定義成一個個獨立的分段,并包括一個體分段,如:
@model string[]@{ViewBag.Title = "Index"; }@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 list 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捕捉無關內容的情況。下面是對這種方式的使用示例:
<!DOCTYPE html> <html> <head><meta charset="utf-8" /><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 the layout</div>@RenderSection("Footer")<div class="layout">This is part of the layout</div> </body> </html>?
如果一個視圖不需要或不希望提供特定內容,那么可以采取對一個分段提供默認內容的方式,如:??
@if (IsSectionDefined("Footer")) {@RenderSection("Footer")}else{<h4>This is the default footer</h4>}???????? IsSectionDefined輔助器可以使用要檢查的分段名來判斷是否定義了這個分段,如果定義了則返回一個真值(true)。
由于默認情況下,視圖必須含有布局中調用RenderSection的所有分段,否則將會拋出異常。為了查看這種異常的出現,現在對_Layout布局進行修改,如:
<!DOCTYPE html> <html> <head><meta charset="utf-8" /><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 the layout</div>@if (IsSectionDefined("Footer")){@RenderSection("Footer")}else{<h4>This is the default footer</h4>}@RenderSection("scripts")<div class="layout">This is part of the layout</div> </body> </html>???????? 這樣,將會看到如下異常:
?
???????? 當然可以使用前面提到的IsSectionDefined方法來避免這種情況的發送,但還有一個更好的辦法,就是給RenderSection方法傳遞一個附加的false值,即使用可選分段,如:
??? @RenderSection("scripts", false)
使用分部視圖
???????? 使用分部視圖可以實現在程序中的不同地方使用同樣的Razor標簽和HTML標記片段,這一點在前面已經有了對應的介紹,就不多說了。分部視圖具有以下幾個特點:
- 含有標簽
- 含有標記片段
- 具有獨立性,是獨立的視圖文件
- 可以被包含在其他視圖之中
1.創建分部視圖
首先,在/Views/Shared文件夾創建一個名為MyPartial的分部視圖:
<div>This is the message from the partial view.@Html.ActionLink("This is a link to the Index action", "Index") </div>???????? 其次,通過HTML輔助器在另一個視圖中調用這個分部視圖:
@{ViewBag.Title = "List"; Layout = null; }<h3>This is the /Views/Common/List.cshtml</h3>@Html.Partial("MyPartial")???????? 像這樣對分部視圖不指定擴展名的方式使用時,視圖引擎會在常規位置處查找分部視圖,即“/Views/Home”和“/Views/Shared”文件夾下查找。這里將Layout設置為null是為了避免使用之前布局中定義的分段。
???????? 下面來看一下效果:
?
提示:在上述分部視圖中,對ActionLink輔助器方法的調用會根據所處理的請求,采用其控制器器的信息。也就是說會根據讓該分部視圖進行渲染的控制器生成對應的引用。
?
2.使用強類型分部視圖
現在看一下強類型的分部視圖的創建和使用,分部視圖名:MyStrongTypedPartial.cshtml。內容如下:
@model IEnumerable<string><div>This is the message from the partial view.<ul>@foreach (string str in Model){<li>@str</li>}</ul> </div>???????? 更新一下Common下的List視圖,以便查看這一效果:
@{ViewBag.Title = "List";Layout = null; }<h3>This is the /Views/Common/List.cshtml</h3>@Html.Partial("MyStrongTypedPartial", new[] { "Apple", "Orange", "Pear" })效果圖:
?
使用子動作
子動作(Child Action)是通過視圖調用的動作方法。如果需要將某種控制器邏輯用于應用程序的多個地方,子動作可以使我們避免重復的控制器邏輯。它與動作之間的關系就像分部視圖和視圖一樣。
比如,如果要顯示某些數據驅動的“小部件”,這些“小部件”要出現在多個頁面上,且含有與主動作無關的數據時,可能就要用到子動作。
1.創建子動作
任何動作都可以作為一個子動作。現在通過在Home控制器添加一個新的動作方法來進行演示:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc;namespace WorkingWithRazor.Controllers {public class HomeController : Controller{//// GET: /Home/public ActionResult Index(){string[] names = { "Apple", "Orange", "Pear" };return View(names);}public ActionResult List(){return View();}[ChildActionOnly]public ActionResult Time(){return PartialView(DateTime.Now);} } }???????? 上面代碼中動作方法Time通過調用PartialView方法渲染一個分部視圖。ChildActionOnly注解屬性可以確保一個動作方法只能在一個視圖中作為一個子動作進行調用。動作方法并不一定非要使用這種方式才能成為一個子動作,但建議這么做,原因是這樣可以防止該動作方法作為用戶請求的一個結果被調用。
???????? 現在就可以看看如何調用了。注意,子動作典型地與分布視圖相關聯,但這不是必須的。下面是該動作方法的視圖:
@model DateTime<p>The time is: @Model.ToShortTimeString()</p>2.渲染子動作
可以用Html.Action輔助器調用子動作。我們修改一下List視圖文件,用來渲染剛剛做的子動作:
@{ViewBag.Title = "List";Layout = null; }<h3>This is the /Views/Common/List.cshtml</h3>@Html.Partial("MyStrongTypedPartial", new[] { "Apple", "Orange", "Pear" })@Html.Action("Time")?
?
???????? 如果要調用的是其他控制器中的動作方法,需要提供控制器名稱,如:
@Html.Action("Time", "MyController")
???????? 通過提供一個匿名類的對象,其屬性對應于子動作方法的參數名,可以將參數傳遞給動作方法。如:
???????? 子動作方法:
[ChildActionOnly]public ActionResult Time(DateTime time){return PartialView(time);}???????? 調用方法:
@Html.Action("Time", new { time =DateTime.Now })
?
轉載于:https://www.cnblogs.com/KeSaga/p/5553316.html
總結
- 上一篇: 用mansard对cell的子控件设置约
- 下一篇: 内核:多内核操作模式