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

来源:http://www.cnblogs.com/jiekzou/archive/2017/06/28/7084827.html
-Advertisement-
Play Games

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


    在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>&nbsp;
                        <span class="hidden-sm hidden-xs">切換主題&nbsp;</span>&nbsp;
                        <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

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Let's Encrypt免費又好用的證書,廢話不多說。 假設我的功能變數名稱為:163.org 1、克隆代碼 git clone https://github.com/letsencrypt/letsencrypt # 沒有git的先安裝git # yum install git # apt-get in ...
  • 1、回顧基礎命令 2、腳本 3、變數 4、別名 5、條件判斷 6、test判斷 一、回顧基礎命令 shutdown --關機/重啟 exit --退出當前shell rmdir --刪除空目錄 du --查看目錄占用的存儲空間 df --查看已 經掛載的文件系統的空間使用情況 ln --創建鏈接 c ...
  • 很多電腦愛好者對於Win10內置的PIN碼功能不太瞭解,很多朋友都還沒有使用。其實,創建PIN碼可以提到密碼使用,當你登錄到Windows和其它應用服務時,可以通過PIN碼替代輸入賬戶密碼,提升安全性。話不多說,以下是Win10開啟PIN碼設置使用教程,步驟如下。 一、從Win10左下角的開始菜單, ...
  • 對於游戲玩家來說,對顯卡的關註度要高於電腦其它硬體,一般來說,顯卡越好,游戲性能往往越強。不過要持續發揮顯卡的最佳游戲性能,經常更新顯卡驅動也是很有必要的。那麼筆記本顯卡驅動怎麼更新?下麵小編以自己的Win10筆記本為例,教大家如何升級筆記本顯卡驅動。 Win10筆記本顯卡驅動更新升級方法 升級筆記 ...
  • 1.下載最新的openssh包 http://www.openssh.com/portable.html#http 2.升級openssh之前要先打開伺服器telnet,通過telnet登錄伺服器,因為升級過程中會導致ssh暫時不能用 打開linux telnet服務: 查看telnet是否已經安裝 ...
  • 環境:筆記本 + 家用WIFI + 公司WIFI + VMware + CentOS6.8 + Xshell 問題描述:初學Linux時,用筆記本裝了虛擬機(單網卡),想實現linux在家和公司都能夠無線連網,但又不想上網地點變動之後每次手動輸入IP登錄Xshell。 解決思路:增加一塊網卡(eth ...
  • 一、簡介 1、認識 加密網頁(https): tcp:443 明文網頁(http): tcp:80 survey.netcraft.net --這個網站上可以查到最新的網站伺服器的使用率 超文本傳輸協議(HTTP,HyperText Transfer Protocol)是互聯網上應用最為廣泛的一種網 ...
  • select系統調用的的用途是:在一段指定的時間內,監聽用戶感興趣的文件描述符上可讀、可寫和異常等事件。 select 機制的優勢 為什麼會出現select模型? 先看一下下麵的這句代碼: 這是用來接收數據的,在預設的阻塞模式下的套接字里,recv會阻塞在那裡,直到套接字連接上有數據可讀,把數據讀到 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...