創建自定義視圖引擎 一般情況下直接使用MVC框架自帶的內建視圖引擎即可,但如果想知道視圖引擎是如何工作的,就需要從建立一個自定義視圖引擎開始了。通過之前的學習我們都知道了內建視圖引擎包括Razor和ASPX兩種,ASPX是針對舊版本MVC程式的,他主要是維護舊版本MVC應用程式,保持系統的相容性而保 ...
創建自定義視圖引擎
一般情況下直接使用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")
像這樣對分