ASP.NET MVC 重写RazorViewEngine实现多主题切换
??? 在ASP.NET MVC中來實現主題的切換一般有兩種方式,一種是通過切換皮膚的css和js引用,一種就是通過重寫視圖引擎。通過重寫視圖引擎的方式更加靈活,因為我不僅可以在不同主題下面布局和樣式不一樣,還可以讓不同的主題下面顯示的數據條目不一致,就是說可以在某些主題下面添加一下個性化的東西。
??? 本篇我將通過重寫視圖引擎的方式來進行演示,在這之前,我假設你已經具備了MVC的一些基礎,系統登錄后是默認主題,當我們點擊切換主題之后,左側菜單欄的布局變了,右側內容的樣式也變了,而地址欄是不變的。界面UI用的metronic,雖然官網是收費的,但是在天朝,總是可以找到免費的。metronic是基于bootstrap的UI框架,官網地址:http://keenthemes.com/preview/metronic/
??? 我們先來看下效果:
??? 在這里,我使用了分區域、分模塊(按獨立的業務功能劃分)的方式,一個模塊就是一個獨立的dll,在這里Secom.Emx.Admin和Secom.Emx.History就是兩個獨立的模塊,并分別創建了區域Admin和History,當然你可以在獨立模塊下面創建多個區域。
?
??? 你會發現Secom.Emx.Admin模型下面的Areas目錄和Secom.Emx.WebApp中的目錄是一模一樣的,其實我最初不想在模塊項目中添加任何的View,但是為了方便獨立部署還是加了。右鍵單擊項目Secom.Emx.Admin,選擇“屬性”——“生成事件”添加如下代碼:
xcopy /e/r/y $(ProjectDir)Areas\Admin\Views $(SolutionDir)Secom.Emx.WebApp\Areas\Admin\Views??? 這命令很簡單,其實就是當編譯項目Secom.Emx.Admin的時候,將項目中的Views復制到Secom.Emx.WebApp項目的指定目錄下。
??? 區域配置文件我放置到了Secom.Emx.WebApp中,其實你完全可以獨立放置到一個類庫項目中,因為注冊區域路由的后,項目最終會尋找bin目錄下面所有繼承了AreaRegistration類的,然后讓WebApp引用這個類庫項目,Secom.Emx.WebApp項目添加Secom.Emx.Admin、Secom.Emx.History的引用。
??? AdminAreaRegistration代碼如下:
using System.Web.Mvc;namespace Secom.Emx.WebApp {public class AdminAreaRegistration : AreaRegistration {public override string AreaName {get {return "Admin";}}public override void RegisterArea(AreaRegistrationContext context) {context.MapRoute("Admin_default","Admin/{controller}/{action}/{id}",new { action = "Index", id = UrlParameter.Optional },namespaces:new string[1] { "Secom.Emx.Admin.Areas.Admin.Controllers" });}} }??? 注意命名空間和后面添加的 namespaces:new string[1] { "Secom.Emx.Admin.Areas.Admin.Controllers" },這個命名空間就是獨立模塊Secom.Emx.Admin下面的控制器所在的命名空間。HistoryAreaRegistration代碼如下:
using System.Web.Mvc;namespace Secom.Emx.WebApp {public class HistoryAreaRegistration : AreaRegistration {public override string AreaName {get {return "History";}}public override void RegisterArea(AreaRegistrationContext context) {context.MapRoute("History_default","History/{controller}/{action}/{id}",new { action = "Index", id = UrlParameter.Optional },namespaces:new string[1] { "Secom.Emx.History.Areas.History.Controllers" });}} } View Code??? 我們先看下RazorViewEngine的原始構造函數如下:
public RazorViewEngine(IViewPageActivator viewPageActivator) : base(viewPageActivator) { AreaViewLocationFormats = new[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" }; AreaMasterLocationFormats = new[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" }; AreaPartialViewLocationFormats = new[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" }; ViewLocationFormats = new[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" }; MasterLocationFormats = new[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" }; PartialViewLocationFormats = new[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" }; FileExtensions = new[] { "cshtml", "vbhtml", }; } View Code??? 然后新建CustomRazorViewEngine繼承自RazorViewEngine,對View的路由規則進行了重寫,既然可以重寫路由規則,那意味著,你可以任意定義規則,然后遵守自己定義的規則就可以了。需要注意的是,要注意路由數組中的順序,查找視圖時,是按照前后順序依次查找的,當找到了視圖就立即返回,不會再去匹配后面的路由規則。為了提升路由查找效率,我這里刪除了所有vbhtml的路由規則,因為我整個項目中都采用C#語言。
using System.Web.Mvc;namespace Secom.Emx.WebApp.Helper {public class CustomRazorViewEngine : RazorViewEngine{public CustomRazorViewEngine(string theme){if (!string.IsNullOrEmpty(theme)){AreaViewLocationFormats = new[]{//themes"~/themes/"+theme+"/views/Areas/{2}/{1}/{0}.cshtml","~/themes/"+theme+"/Shared/{0}.cshtml""~/Areas/{2}/Views/{1}/{0}.cshtml","~/Areas/{2}/Views/Shared/{0}.cshtml"};AreaMasterLocationFormats = new[]{//themes"~/themes/"+theme+"/views/Areas/{2}/{1}/{0}.cshtml","~/themes/"+theme+"/views/Areas/{2}/Shared/{0}.cshtml","~/themes/"+theme+"/views/Shared/{0}.cshtml","~/Areas/{2}/Views/{1}/{0}.cshtml","~/Areas/{2}/Views/Shared/{0}.cshtml"};AreaPartialViewLocationFormats = new[]{//themes"~/themes/"+theme+"/views/Shared/{0}.cshtml","~/Areas/{2}/Views/{1}/{0}.cshtml","~/Areas/{2}/Views/Shared/{0}.cshtml"};ViewLocationFormats = new[]{//themes"~/themes/"+theme+"/views/{1}/{0}.cshtml","~/Views/{1}/{0}.cshtml","~/Views/Shared/{0}.cshtml"};MasterLocationFormats = new[]{//themes"~/themes/"+theme+"/views/Shared/{0}.cshtml","~/Views/{1}/{0}.cshtml","~/Views/Shared/{0}.cshtml"};PartialViewLocationFormats = new[]{//themes"~/themes/"+theme+"/views/Shared/{0}.cshtml","~/Views/{1}/{0}.cshtml","~/Views/Shared/{0}.cshtml"};FileExtensions = new[]{"cshtml"};}}} } View Code??? 重寫后,我們的路由規則將是這樣的:當沒有選擇主題的情況下,沿用原來的路由規則,如果選擇了主題,則使用重寫后的路由規則。
新的路由規則:在選擇了主題的情況下,優先查找thems/主題名稱/views/Areas/區域名稱/控制器名稱/視圖名稱.cshtml,如果找不到再按照默認的路由規則去尋找,也就是Areas/區域名稱/Views/控制器名稱/視圖名稱.cshtml。
可以看到我們查找模板頁的方式也被修改了,所以對于一些通用的,只要換模板頁就可以了,不需要添加view界面,因為指定主題下面找不到view會去默認主題下面找,而view界面會引用模板頁的,對于一些個性化的東西,再去指定的主題下面添加新的view,不知道我這樣表述你有明白沒,感覺比較饒,反正就是你可以按照你自己的規則去找視圖,而不是asp.net mvc默認的規則。
?
??? 切換主題View代碼:
<div class="btn-group"><button type="button" class="btn btn-circle btn-outline red dropdown-toggle" data-toggle="dropdown"><i class="fa fa-plus"></i> <span class="hidden-sm hidden-xs">切換主題 </span> <i class="fa fa-angle-down"></i></button><ul class="dropdown-menu" role="menu"><li><a href="javascript:setTheme('default')"><i class="icon-docs"></i> 默認主題</a></li><li><a href="javascript:setTheme('Blue')"><i class="icon-tag"></i> 藍色主題</a></li></ul></div><script type="text/javascript">function setTheme(themeName){window.location.href = "/Home/SetTheme?themeName=" + themeName + "&href=" + window.location.href;}</script>??? 當用戶登錄成功的時候,從Cookie中讀取所選主題信息,當Cookie中沒有讀取到主題記錄時,則從Web.config配置文件中讀取配置的主題名稱,如果都沒有讀取到,則說明是默認主題,沿用原有的視圖引擎規則。在后臺管理界面,每次選擇了主題,我都將主題名稱存儲到Cookie中,默認保存一年,這樣當下次再登錄的時候,就能夠記住所選的主題信息了。
using System; using System.Web.Mvc; using Secom.Emx.WebApp.Helper; using System.Web; using Secom.Emx.Common.Controllers;namespace Secom.Emx.WebApp.Controllers {public class HomeController : BaseController{string themeCookieName = "Theme";public ActionResult Index(){ViewData["Menu"] = GetMenus();return View();}public ActionResult SetTheme(string themeName,string href){if (!string.IsNullOrEmpty(themeName)){Response.Cookies.Set(new HttpCookie(themeCookieName, themeName) { Expires = DateTime.Now.AddYears(1) });}else{themeName = Request.Cookies[themeCookieName].Value ?? "".Trim();}Utils.ResetRazorViewEngine(themeName);return string.IsNullOrEmpty(href)? Redirect("~/Home/Index"):Redirect(href);}public ActionResult Login(){string themeName = Request.Cookies[themeCookieName].Value ?? "".Trim();if (!string.IsNullOrEmpty(themeName)){Utils.ResetRazorViewEngine(themeName);}return View();}} }??? Utils類:
using System.Configuration; using System.Web.Mvc;namespace Secom.Emx.WebApp.Helper {public class Utils{private static string _themeName;public static string ThemeName{get{if (!string.IsNullOrEmpty(_themeName)){return _themeName;}//模板風格_themeName =string.IsNullOrEmpty(ConfigurationManager.AppSettings["Theme"])? "" : ConfigurationManager.AppSettings["Theme"];return _themeName;}}public static void ResetRazorViewEngine(string themeName){themeName = string.IsNullOrEmpty(themeName) ? Utils.ThemeName : themeName;if (!string.IsNullOrEmpty(themeName)){ViewEngines.Engines.Clear();ViewEngines.Engines.Add(new CustomRazorViewEngine(themeName));}}} } View Code???? 實現方式實在是太簡單,簡單得我不知道如何表述才好,我還是記下來,方便有需要的人可以查閱,希望可以幫到你們。由于項目引入了龐大的各種相關文件以致文件比較大,網速原因無法上傳源碼還望見諒!
轉載于:https://www.cnblogs.com/jiekzou/p/7084827.html
總結
以上是生活随笔為你收集整理的ASP.NET MVC 重写RazorViewEngine实现多主题切换的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MQ的使用场景
- 下一篇: ASP.NET MVC下的异步Actio