1. 程式人生 > >定製屬於你自己的ViewEngine(一套邏輯多套UI)

定製屬於你自己的ViewEngine(一套邏輯多套UI)

ASP.NET MVC出來這麼久了,心中卻又很多的疑惑:為什麼所有的View都要放在Views目錄下? 為什麼Shared資料夾下面的頁面可以被共享? 為什麼Page既可以是*.cshtml,也可以是*.aspx? 

其實上面的幾個問題歸結起來都是檢視引擎的功效。

在傳統的ASP.NET中,可能還沒有ViewEngine的概念。因為在Web From裡面,實現Page實現了IHttpHanlder的介面,所以Page既是響應的處理類,也是檢視的渲染類。在ASP.NET MVC中,檢視的概念被抽象了出來,試圖引擎的概念也被抽象成了一個介面。

首先來看一下IViewEngine介面的定義

1
namespace System.Web.Mvc 2 { 3 public interface IViewEngine 4 { 5 ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache); 6 ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool
useCache); 7 void ReleaseView(ControllerContext controllerContext, IView view); 8 } 9 }

總共3個函式,總結起來大概就是兩個功能:Find & Release。

預設情況下,ASP.NET MVC提供了兩個檢視引擎:WebFormViewEngine和RazorViewEngine

 1 namespace System.Web.Mvc
 2 {
 3     public static class ViewEngines
 4     {
5 private static readonly ViewEngineCollection _engines = new ViewEngineCollection 6 { 7 new WebFormViewEngine(), 8 new RazorViewEngine(), 9 }; 10 11 public static ViewEngineCollection Engines 12 { 13 get { return _engines; } 14 } 15 } 16 }

這就是為什麼ASP.NET MVC既支援*.aspx,又支援*.cshtml的原因了(個人覺得如果已經確定要使用RazorView的話,不如把WebFormViewEngine給移除,可能對效能會有所幫助)。

那為什麼所有的檢視都要放在Views目錄下呢,這個就要拜RazorViewngines所賜了。

下面是RazorViewEngine的建構函式:

 1         public RazorViewEngine(IViewPageActivator viewPageActivator)
 2             : base(viewPageActivator)
 3         {
 4             AreaViewLocationFormats = new[]
 5             {
 6                 "~/Areas/{2}/Views/{1}/{0}.cshtml",
 7                 "~/Areas/{2}/Views/{1}/{0}.vbhtml",
 8                 "~/Areas/{2}/Views/Shared/{0}.cshtml",
 9                 "~/Areas/{2}/Views/Shared/{0}.vbhtml"
10             };
11             AreaMasterLocationFormats = new[]
12             {
13                 "~/Areas/{2}/Views/{1}/{0}.cshtml",
14                 "~/Areas/{2}/Views/{1}/{0}.vbhtml",
15                 "~/Areas/{2}/Views/Shared/{0}.cshtml",
16                 "~/Areas/{2}/Views/Shared/{0}.vbhtml"
17             };
18             AreaPartialViewLocationFormats = new[]
19             {
20                 "~/Areas/{2}/Views/{1}/{0}.cshtml",
21                 "~/Areas/{2}/Views/{1}/{0}.vbhtml",
22                 "~/Areas/{2}/Views/Shared/{0}.cshtml",
23                 "~/Areas/{2}/Views/Shared/{0}.vbhtml"
24             };
25  
26             ViewLocationFormats = new[]
27             {
28                 "~/Views/{1}/{0}.cshtml",
29                 "~/Views/{1}/{0}.vbhtml",
30                 "~/Views/Shared/{0}.cshtml",
31                 "~/Views/Shared/{0}.vbhtml"
32             };
33             MasterLocationFormats = new[]
34             {
35                 "~/Views/{1}/{0}.cshtml",
36                 "~/Views/{1}/{0}.vbhtml",
37                 "~/Views/Shared/{0}.cshtml",
38                 "~/Views/Shared/{0}.vbhtml"
39             };
40             PartialViewLocationFormats = new[]
41             {
42                 "~/Views/{1}/{0}.cshtml",
43                 "~/Views/{1}/{0}.vbhtml",
44                 "~/Views/Shared/{0}.cshtml",
45                 "~/Views/Shared/{0}.vbhtml"
46             };
47  
48             FileExtensions = new[]
49             {
50                 "cshtml",
51                 "vbhtml",
52             };
53         }

所有的定址路徑都被格式化了,是不是很眼熟呢,關於這裡為啥用陣列而不用List,個人覺得,陣列的定址效率要更高些,遍歷速度更快。

好了,找了“罪魁禍首”,就好好地調教一個,讓它乖乖聽話,小樣讓去哪就去哪裡。

 1  /// <summary>
 2     /// razor檢視引擎擴充套件
 3     /// </summary>
 4     public class CustomerViewEngine : RazorViewEngine
 5     {
 6         /// <summary>
 7         /// 可以分開部署不同語種
 8         /// </summary>
 9         /// <param name="engineName"></param>
10         public CustomerViewEngine(string engineName)
11         {
12             base.ViewLocationFormats = new[]
13                 {
14                     "~/Views" + engineName + "/{1}/{0}.cshtml",
15                     "~/Views" + engineName + "/Shared/{0}.cshtml"
16                 };
17 
18             base.PartialViewLocationFormats = new[]
19                 {
20                     "~/Views" + engineName + "/{1}/{0}.cshtml",
21                     "~/Views" + engineName + "/Shared/{0}.cshtml"
22                 };
23 
24             base.AreaViewLocationFormats = new[]
25                 {
26                     "~Areas/{2}/Views" + engineName + "/{1}/{0}.cshtml",
27                     "~Areas/{2}/Views" + engineName + "/Shared/{0}.cshtml"
28                 };
29 
30             base.AreaPartialViewLocationFormats = new[]
31                 {
32                     "~Areas/{2}/Views" + engineName + "/{1}/{0}.cshtml",
33                     "~Areas/{2}/Views" + engineName + "/Shared/{0}.cshtml"
34                 };
35         }
36 
37         public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
38         {
39             this.SetEngine(controllerContext);
40             return base.FindView(controllerContext, viewName, masterName, useCache);
41         }
42 
43         public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
44         {
45             this.SetEngine(controllerContext);
46             return base.FindPartialView(controllerContext, partialViewName, useCache);
47         }
48 
49         /// <summary>
50         /// 根據條件自行設定,目前是chrome瀏覽器就展示預設的
51         /// 不是chrome瀏覽器的話就展示/Themes/Eleven下的
52         /// 可以直接測試是移動端還是pc端
53         /// 然後寫入cookie
54         /// </summary>
55         private void SetEngine(ControllerContext controllerContext)
56         {
57             string engineName = "/Themes/Eleven";
58             if (controllerContext.HttpContext.Request.UserAgent.IndexOf("Chrome/65") >= 0)
59             {
60                 engineName = null;
61             }
62 
63             //if (controllerContext.HttpContext.Request.IsMobile())//檢測是不是移動端
64             //{
65             //    engineName = null;
66             //}
67 
68             base.ViewLocationFormats = new[]
69                {
70                     "~/Views" + engineName + "/{1}/{0}.cshtml",
71                     "~/Views" + engineName + "/Shared/{0}.cshtml"
72                 };
73 
74             base.PartialViewLocationFormats = new[]
75                 {
76                     "~/Views" + engineName + "/{1}/{0}.cshtml",
77                     "~/Views" + engineName + "/Shared/{0}.cshtml"
78                 };
79 
80             base.AreaViewLocationFormats = new[]
81                 {
82                     "~Areas/{2}/Views" + engineName + "/{1}/{0}.cshtml",
83                     "~Areas/{2}/Views" + engineName + "/Shared/{0}.cshtml"
84                 };
85 
86             base.AreaPartialViewLocationFormats = new[]
87                 {
88                     "~Areas/{2}/Views" + engineName + "/{1}/{0}.cshtml",
89                     "~Areas/{2}/Views" + engineName + "/Shared/{0}.cshtml"
90                 };
91         }

接下去就很簡單了,只需要把原來的檢視引擎清空,載入自己的檢視引擎就可以了

 1         protected void Application_Start()
 2         {
 3             AreaRegistration.RegisterAllAreas();
 4  
 5             ViewEngines.Engines.Clear();
 6  
 7             ViewEngines.Engines.Add(new CustomViewEngine());
 8  
 9             RegisterGlobalFilters(GlobalFilters.Filters);
10             RegisterRoutes(RouteTable.Routes);
11         }