Asp.Net MVC4 系列--進階篇之View
自定義一個ViewEngine
一般的,不需要自定義建立一個View Engine,但為了說明View部分的全貌,先從自定義ViewEngine開始,逐漸全面瞭解MVCFramework的View部分實現。
需要實現的介面
public interface IViewEngine {
ViewEngineResult FindPartialView(ControllerContextcontrollerContext,
string partialViewName, bool useCache);
ViewEngineResult FindView(ControllerContext controllerContext,
string viewName, string masterName, bool useCache);
void ReleaseView(ControllerContext controllerContext, IViewview);
}
介面說明:
1.查詢PartialView
2.查詢View
3.釋放View
除了最後一個函式返回值為空外,前兩個函式都需要返回一個ViewEngineResult ,程式碼如下:
public class ViewEngineResult {
public ViewEngineResult(IEnumerable<string> searchedLocations) {
if (searchedLocations == null) {
throw new ArgumentNullException("searchedLocations");
}
SearchedLocations = searchedLocations;
}
public ViewEngineResult(IView view , IViewEngine viewEngine) {
if (view == null) { throw newArgumentNullException("view");}
if (viewEngine == null) { throw new ArgumentNullException("viewEngine");}
View = view;
ViewEngine = viewEngine;
}
public IEnumerable<string> SearchedLocations { get;private set; }
public IView View { get; private set; }
public IViewEngine ViewEngine { get; private set; }
}
可以看到,ViewEngineResult的構造可以通過兩種建構函式:
1.如果找不到View,那麼給一個已經找了哪些路徑(最後會show給user,如果找了所有的路徑,到最後都沒有找到)
2.如果找到了,那麼給這個view物件和它的ViewEngine(由誰來render)
IView介面:
public interface IView {
void Render(ViewContext viewContext, TextWriter writer);
}
一個函式:一手拿著ViewContext(包含了這次請求的資訊),一手拿著writer,把資訊寫成html,render到客戶端,推送到瀏覽器直接消費。
示例實現:
1.Controller
public class TestController : Controller
{
public ActionResult Index()
{
ViewData["Key1"] ="Value1";
ViewData["Key2"] =DateTime.Now;
ViewData["Key3"] = 3;
return View("Test");
}
}
實現了TestController,裡面有一個Index Action,返回了一個Test View(在Viewsfolder中不存在)。
2.Customize View
public class ViewDataPrinter : IView
{
public void Render(ViewContext viewContext, TextWriter writer)
{
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/>");
}
}
功能:把viewContext中的ViewData列印出來
3.Customize View Engine 實現
public class TestViewEngine : IViewEngine
{
Public ViewEngineResult FindView(ControllerContext controllerContext,
String viewName, string masterName, bool useCache)
{
if (viewName == "Test")
{
return new ViewEngineResult(new ViewDataPrinter(), this);
}
return new ViewEngineResult(new [] { "Sorry , Only service for TestView" });
}
public ViewEngineResult FindPartialView(ControllerContext controllerContext,
string partialViewName, bool useCache)
{
return new ViewEngineResult(new[] { "Sorry , Not Support ParcialView Currently" });
}
public void ReleaseView(ControllerContext controllerContext, IView view)
{
}
}
功能:
我們只實現了FindView,不支援FindPartialView,FindView中只支援名字為“Test”的View,如果名字匹配,返回一個ViewDataPrinter例項,把自己也傳進去。
4.註冊ViewEngine
MVC Framework中支援多個ViewEngine,如果只希望新增到ViewEngine中的最後一個,那麼直接用Add就可以了:
ViewEngines.Engines.Add(new TestViewEngine());
當然,如果考慮到Apply的順序,那麼也可以insert到第一個:
ViewEngines.Engines.Insert(0, new TestViewEngine());
如果想把預設的ViewEngine刪掉,只保留自己customize的:
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new TestViewEngine());
5.測試
訪問TestController的Index Action,會呼叫剛才customize的ViewEngine,建立一個ViewDataResult,裡面傳遞的是ViewDataPrinter,render時列印ViewData的資訊。
測試2:
在 Test Controller 新增一個Action,指向一個不存在的View
public ActionResult NoExist()
{
return View("NoExist");
}
public ActionResult NoExist()
{
return View("NoExist");
}
訪問這個Action :
<2>
可以看到,出現了”Sorry , Only Service For TestView “ ,是我們Customize的ViewEngine列印的資訊。
注意,如果試圖訪問一個根本不存在的Controller,或者是Action,會得到404。
Razor
透過一個例子初探語法:
Controller :
public class HomeController : Controller {
public ActionResult Index() {
string[] names = { "Apple", "Orange", "Pear" };
return View(names);
}
}
View:
@model string[]
@{
ViewBag.Title = "Index";
}
This is a list of fruit names:
@foreach (string name in Model) {
<span><b>@name</b></span>
}
測試:
Razor的工作原理:
1.搜尋View,順序:
Views/<ControllerName>/<ViewName>.cshtml
Views/Shared/<ViewName>.cshtml
對於AreaView,順序為:
Area/<AreaName>/Views/<ControllerName>/<ViewName>.cshtml
Area/<AreaName>/Views/Shared/<ViewName>.cshtml
2.解析View程式碼,生成臨時c#code,存在臨時目錄,生成程式碼取樣:
public class _Page_Views_Home_Index_cshtml :System.Web.Mvc.WebViewPage<string[]> {
public _Page_Views_Home_Index_cshtml() {
}
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");
}
}
}
可見,Razor把我們的frontend程式碼完全解析為了c#。
3.當瀏覽器開始Render View時,會動態編譯這個臨時檔案,把內容寫成html傳送到瀏覽器。
Customize Razor
我們可以改變Razor哪些行為?搜尋範圍。例子如下:
Customize Razor實現
public class CustomizeRazor : RazorViewEngine
{
public CustomizeRazor()
{
ViewLocationFormats =new[] { "~/Views/{1}/{0}.cshtml","~/Views/Common/{0}.cshtml" };
}
}
程式碼說明: 希望razor的查詢範圍和順序為:
Views/<ControllerName>/<ViewName>.cshtml
Views/Common/<ViewName>.cshtml
在global檔案中註冊
ViewEngines.Engines.Add(new CustomizeRazor());
1.新增Common資料夾
2.新增View ,名稱為NoExist.cshtml在Common資料夾
View 程式碼:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<div>
This View is located under common folder , which is supposed be found by Custmized Razor
</div>
</body>
</html>
3.在Test Controller 新增Action:
public ActionResult NoExist()
{
return View("NoExist");
}
4.測試:
按照Razor預設的行為,會去:
Views/<ControllerName>/<ViewName>.cshtml
Views/<ControllerName>/Shared/<ViewName>.cshtml
由於我們都沒有提供,只是把NoExistView放在了Common,我們在global檔案註冊了我們的CustomizeRazor,Razor的查詢行為成功的被customize了。
Razor dynamic Content
目前支援:
Inline Code
Html Helper Method
Sections
Partial view
Child Actions
使用Section
1.在View中定義section
@{
ViewBag.Title ="View1";
Layout ="~/Views/Shared/_LayoutPage1.cshtml";
}
@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>
}
<!--
Suppose to be caught as body ,Body Start
-->
<div class="view">
This is a list of fruit names(let Razor Catch Body part):
@foreach (string name in Model) {
<span><b>@name</b></span>
}
</div>
<!--
Body end
-->
@section Footer {
<div class="view">
This is the footer
</div>
}
程式碼說明:定義了兩個action:header 和footer, 並期望中間部分被razor成功的解析為 body部分。
2.新增一個layout(就是上面View指向的_LayoutPage1):
<!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>
4.測試
可以看到,razor已經成功的把header,footer解析了,並且把我們期望它解析的body部分也捕捉到了,但是通常我們為了穩妥,可以顯示的把body部分也定義為section裡面:
@section Body {
<div class="view">
This is a list of fruit names(Body As Section,recommended):
@foreach (string name in Model) {
<span><b>@name</b></span>
}
</div>
}
在Layout的Render程式碼相應改為:
@RenderSection("Body")
Optional Section
考慮場景:
1.如果View中定義了Section : Footer才顯示footer,否則顯示一個預設的footer:
@if (IsSectionDefined("Footer")) {
@RenderSection("Footer")
} else {
<h4>This is the default footer</h4>
}
2.如果View中定義了scripts則render,否則不要render:
@RenderSection("scripts",false)
Partial View
定義一個PartialView
<div>
This is the message from the partial view.
@Html.ActionLink("This is a link to the Index action", "Index")
</div>
Consume partial view
@{
ViewBag.Title = "List";
Layout = null;
}
<h3>This is the/Views/Common/List.cshtml View</h3>
@Html.Partial("MyPartial")
語法很簡單,就是使用html helper method中的renderpartial,關於htmlhelper method,下一章會講。
Strong type的partialview 也類似:
1.定義
@model IEnumerable<string>
<div>
This is the message from the partial view.
<ul>
@foreach (string str in Model) {
<li>@str</li>
}
</ul>
</div>
2.在View中消費
@{
ViewBag.Title = "List";
Layout = null;
}
<h3>This is the /Views/Common/List.cshtml View</h3>
@Html.Partial("MyStronglyTypedPartial", new []{"Apple", "Orange", "Pear"})
View在消費時主要注意引數要給對。
Child Action
1.定義Action:
[ChildActionOnly]
public ActionResult Time() {
return PartialView(DateTime.Now);
}
2.定義配套的PartialView:
<p>The time is: @Model.ToShortTimeString()</p>
3.消費child action
@{
ViewBag.Title = "List";
Layout = null;
}
<h3>This is the /Views/Common/List.cshtml View</h3>
@Html.Partial("MyStronglyTypedPartial", new []{"Apple", "Orange", "Pear"})
@Html.Action("Time")
這樣,Action會被觸發,並render出partialView 。
與PartialView不同,這裡HtmlHelper觸發的是Action,而PartialView那裡是直接render一個PartialView。
Action通常和partialView一起使用的,主要針對一些場景,希望不僅僅封裝View,並希望把Action這部分也重用了,這時考慮使用child Action。但是child Action 是不能被請求消費的。
相關文章
- Asp.Net MVC4系列--進階篇之AJAXASP.NETMVC
- Asp.Net MVC4 系列--進階篇之Model(1)ASP.NETMVC
- Asp.Net MVC4 系列--進階篇之Model(2)ASP.NETMVC
- Asp.Net MVC4系列--進階篇之Helper(1)ASP.NETMVC
- Asp.Net MVC4 系列--進階篇之Helper(2)ASP.NETMVC
- Asp.Net MVC4 系列--進階篇之Controller(2)ASP.NETMVCController
- Asp.Net MVC4 系列-- 進階篇之路由(1)ASP.NETMVC路由
- Asp.Net MVC4 系列--進階篇之路由 (2)ASP.NETMVC路由
- Asp.Net MVC 系列--進階篇之FilterASP.NETMVCFilter
- Asp.Net MVC系列--進階篇之controller(1)ASP.NETMVCController
- Asp.Net MVC4 系列--基礎篇(1)ASP.NETMVC
- Asp.Net MVC4系列---基礎篇(5)ASP.NETMVC
- Asp.Net MVC4系列---基礎篇(4)ASP.NETMVC
- 【webpack 系列】進階篇Web
- 自定義View事件之進階篇(四)-自定義Behavior實戰View事件
- 自定義View進階篇《十》——Matrix詳解View
- 自定義View事件篇進階篇(三)-CoordinatorLayout與BehaviorView事件
- 安卓自定義 View 進階:Path 完結篇(偽)安卓View
- .NET進階系列之四:深入DataTable
- 【HenCoder Android 開發進階】自定義 View 1-7:屬性動畫(進階篇)AndroidView動畫
- 帶你深度解鎖Webpack系列(進階篇)Web
- 正規表示式系列之中級進階篇
- 自定義View事件之進階篇(一)-NestedScrolling(巢狀滑動)機制View事件巢狀
- 自定義View事件篇進階篇(二)-自定義NestedScrolling實戰View事件
- Java多執行緒之進階篇Java執行緒
- Membership三步曲之進階篇
- Java進階篇 設計模式之十四 ----- 總結篇Java設計模式
- Android進階——自定義View之雙向選擇SeekbarAndroidView
- to debug asp.net mvc4ASP.NETMVC
- Android進階系列:八、自定義View之音訊抖動動效AndroidView音訊
- 你所不知道的ASP.NET Core進階系列(三)ASP.NET
- create table進階學習系列(十一)之cluster
- Linux ACL 許可權之進階篇Linux
- Dagger 2 系列(五) -- 進階篇:@Scope 和 @Singleton
- ASP.NET進階:認清控制元件 之 Button (轉)ASP.NET控制元件
- 高階前端進階系列 - webview前端WebView
- [一天一個進階系列] - MyBatis基礎篇MyBatis
- Three.js進階篇之6 - 碰撞檢測JS