當前位置:
首頁 > 知識 > ASP.NET MVC 重寫RazorViewEngine實現多主題切換

ASP.NET MVC 重寫RazorViewEngine實現多主題切換

在ASP.NET MVC中來實現主題的切換一般有兩種方式,一種是通過切換皮膚的css和js引用,一種就是通過重寫視圖引擎。通過重寫視圖引擎的方式更加靈活,因為我不僅可以在不同主題下面布局和樣式不一樣,還可以讓不同的主題下面顯示的數據條目不一致,就是說可以在某些主題下面添加一下個性化的東西。

本篇我將通過重寫視圖引擎的方式來進行演示,在這之前,我假設你已經具備了MVC的一些基礎,系統登錄後是默認主題,當我們點擊切換主題之後,左側菜單欄的布局變了,右側內容的樣式也變了,而地址欄是不變的。界面UI用的metronic,雖然官網是收費的,但是在天朝,總是可以找到免費的。metronic是基於bootstrap的UI框架,官網地址:http://keenthemes.com/preview/metronic/

我們先來看下效果:

ASP.NET MVC 重寫RazorViewEngine實現多主題切換

ASP.NET MVC 重寫RazorViewEngine實現多主題切換

在這裡,我使用了分區域、分模塊(按獨立的業務功能劃分)的方式,一個模塊就是一個獨立的dll,在這裡Secom.Emx.Admin和Secom.Emx.History就是兩個獨立的模塊,並分別創建了區域Admin和History,當然你可以在獨立模塊下面創建多個區域。

ASP.NET MVC 重寫RazorViewEngine實現多主題切換

ASP.NET MVC 重寫RazorViewEngine實現多主題切換

你會發現Secom.Emx.Admin模型下面的Areas目錄和Secom.Emx.WebApp中的目錄是一模一樣的,其實我最初不想在模塊項目中添加任何的View,但是為了方便獨立部署還是加了。右鍵單擊項目Secom.Emx.Admin,選擇「屬性」——「生成事件」添加如下代碼:

xcopy /e/r/y $(ProjectDir)AreasAdminViews $(SolutionDir)Secom.Emx.WebAppAreasAdminViews

這命令很簡單,其實就是當編譯項目Secom.Emx.Admin的時候,將項目中的Views複製到Secom.Emx.WebApp項目的指定目錄下。

ASP.NET MVC 重寫RazorViewEngine實現多主題切換

區域配置文件我放置到了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

ASP.NET MVC 重寫RazorViewEngine實現多主題切換

重寫後,我們的路由規則將是這樣的:當沒有選擇主題的情況下,沿用原來的路由規則,如果選擇了主題,則使用重寫後的路由規則。

新的路由規則:在選擇了主題的情況下,優先查找thems/主題名稱/views/Areas/區域名稱/控制器名稱/視圖名稱.cshtml,如果找不到再按照默認的路由規則去尋找,也就是Areas/區域名稱/Views/控制器名稱/視圖名稱.cshtml。

可以看到我們查找模板頁的方式也被修改了,所以對於一些通用的,只要換模板頁就可以了,不需要添加view界面,因為指定主題下面找不到view會去默認主題下面找,而view界面會引用模板頁的,對於一些個性化的東西,再去指定的主題下面添加新的view,不知道我這樣表述你有明白沒,感覺比較饒,反正就是你可以按照你自己的規則去找視圖,而不是asp.net mvc默認的規則。

ASP.NET MVC 重寫RazorViewEngine實現多主題切換

當用戶登錄成功的時候,從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

實現方式實在是太簡單,簡單得我不知道如何表述才好,我還是記下來,方便有需要的人可以查閱,希望可以幫到你們。由於項目引入了龐大的各種相關文件以致文件比較大,網速原因無法上傳源碼還望見諒!

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 科技優家 的精彩文章:

限制input「type=number」的輸入位數策略整理
我的面試準備過程——JVM相關
運維腳本:文件的空間使用和IO統計
Vulkan Tutorial 23 Descriptor layout and buffer
Akka(10): 分布式運算:集群-Cluster

TAG:科技優家 |

您可能感興趣

Github 項目推薦 用PyTorch 實現 OpenNMT
「CVPR Oral」TensorFlow實現StarGAN代碼全部開源,1天訓練完
用PyTorch實現Mask R-CNN
netty整合springMVC,實現高效的HTTP服務請求
CentOS+OpenVZ+Vtonf實現Linux虛擬化
ASP.NET Core Web API下事件驅動型架構的實現(三):基於RabbitMQ的事件匯流排
Occipital推出MR創作工具Bridge Engine,實現密實3D映射
網友客制的Virgil Abloh x NIKE 「Chicago」配色真的實現了?Air Max 98新配色蓄勢待發!
HTC高管展示SteamVR Tracking 2.0技術 實現跨房間VR使用
用PaddlePaddle和Tensorflow實現GoogLeNet InceptionV2/V3/V4
NET Core微服務之基於Ocelot+IdentityServer實現統一驗證與授權
與AMD合作,Premiere原生支持Radeon Pro SSG,實現更高效視頻製作
FAIR最新視覺論文集錦:FPN,RetinaNet,Mask 和 Mask-X RCNN(含代碼實現)
SpaceTime Enterprises 用VR實現你的太空夢
1.29 VR掃描:HTC申請Cardboard類移動VR專利;Facebook或將在移動AR/VR中實現全身追蹤
Google的「ARCore 1.2」實現對iPhone系統設備的兼容
世界最小VR座椅Yaw VR登陸Kickstarter,可實現3DoF追蹤
SAP Cloud for Customer Extensibility的設計與實現
LVS/DR+keepalived負載均衡實現
淺入淺出TensorFlow 6—實現AlexNet和VGG等經典網路