全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

ASP.NET MVC重写RazorViewEngine实现多主题切换

在ASP.NET MVC中来实现主题的切换一般有两种方式,一种是通过切换皮肤的css和js引用,一种就是通过重写视图引擎。通过重写视图引擎的方式更加灵活,因为我不仅可以在不同主题下面布局和样式不一样,还可以让不同的主题下面显示的数据条目不一致,就是说可以在某些主题下面添加一下个性化的东西。

本篇我将通过重写视图引擎的方式来进行演示,在这之前,我假设你已经具备了MVC的一些基础,我们先来看下效果:

系统登录后是默认主题,当我们点击切换主题之后,左侧菜单栏的布局变了,右侧内容的样式也变了,而地址栏是不变的。界面UI用的metronic,虽然官网是收费的,但是在天朝,总是可以找到免费的。官方地址: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" }
   );
  }
 }
}

我们先看下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", 
  }; 
 }

然后新建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"};
   }

  }
 }
}

重写后,我们的路由规则将是这样的:当没有选择主题的情况下,沿用原来的路由规则,如果选择了主题,则使用重写后的路由规则。

新的路由规则:在选择了主题的情况下,优先查找thems/主题名称/views/Areas/区域名称/控制器名称/视图名称.cshtml,如果找不到再按照默认的路由规则去寻找,也就是Areas/区域名称/Views/控制器名称/视图名称.cshtml

切换主题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));
   }
  }
 }
}

实现方式实在是太简单,简单得我不知道如何表述才好,我还是记下来,方便有需要的人可以查阅,希望可以帮到你们。由于项目引入了庞大的各种相关文件以致文件比较大,网速原因无法上传源码还望见谅!


# ASP.NET  # MVC  # RazorViewEngine  # 主题切换  # 详解ASP.NET Razor 语法  # 详解ASP.NET MVC 利用Razor引擎生成静态页  # ASP.NET MVC4 Razor模板简易分页效果  # ASP.NET Razor模板引擎中输出Html的两种方式  # ASP.NET MVC使用RazorEngine解析模板生成静态页  # asp.net模板引擎Razor调用外部方法用法实例  # asp.net模板引擎Razor中cacheName的问题分析  # 使用Asp.net Mvc3 Razor视图方式扩展JQuery UI Widgets方法介绍  # 详细分析ASP.NET Razor之C# 变量  # 重写  # 在这里  # 选择了  # 配置文件  # 所选  # 的人  # 的是  # 类库  # 情况下  # 因为我  # 我不  # 还可以  # 你可以  # 在这  # 找不到  # 右键  # 要注意  # 将是  # 很简单  # 我将 


相关文章: 建站主机是否等同于虚拟主机?  宝塔面板创建网站无法访问?如何快速排查修复?  建站中国官网:模板定制+SEO优化+建站流程一站式指南  建站之星伪静态规则如何设置?  关于BootStrap modal 在IOS9中不能弹出的解决方法(IOS 9 bootstrap modal ios 9 noticework)  盘锦网站制作公司,盘锦大洼有多少5G网站?  创业网站制作流程,创业网站可靠吗?  建站之星24小时客服电话如何获取?  微信网站制作公司有哪些,民生银行办理公司开户怎么在微信网页上查询进度?  如何在Golang中使用encoding/gob序列化对象_存储和传输数据  建站之星收费标准详解:套餐费用及年费价格表一览  动图在线制作网站有哪些,滑动动图图集怎么做?  想学网站制作怎么学,建立一个网站要花费多少?  青浦网站制作公司有哪些,苹果官网发货地是哪里?  h5网站制作工具有哪些,h5页面制作工具有哪些?  网站制作多少钱一个,建一个论坛网站大约需要多少钱?  图册素材网站设计制作软件,图册的导出方式有几种?  微信小程序制作网站有哪些,微信小程序需要做网站吗?  如何在万网开始建站?分步指南解析  开封网站制作公司,网络用语开封是什么意思?  制作销售网站教学视频,销售网站有哪些?  Python如何创建带属性的XML节点  建站之星如何快速更换网站模板?  义乌企业网站制作公司,请问义乌比较好的批发小商品的网站是什么?  建站之星如何配置系统实现高效建站?  无锡营销型网站制作公司,无锡网选车牌流程?  如何在阿里云虚拟主机上快速搭建个人网站?  如何通过服务器快速搭建网站?完整步骤解析  网站图片在线制作软件,怎么在图片上做链接?  建站之星安装后如何自定义网站颜色与字体?  建站之星如何实现PC+手机+微信网站五合一建站?  临沂网站制作企业,临沂第三中学官方网站?  海南网站制作公司有哪些,海口网是哪家的?  官网自助建站平台指南:在线制作、快速建站与模板选择全解析  三星网站视频制作教程下载,三星w23网页如何全屏?  如何在云主机上快速搭建多站点网站?  如何高效完成独享虚拟主机建站?  建站之星代理费用多少?最新价格详情介绍  如何在香港免费服务器上快速搭建网站?  为什么Go需要go mod文件_Go go mod文件作用说明  平台云上自助建站如何快速打造专业网站?  如何在阿里云虚拟服务器快速搭建网站?  建站主机如何选?性能与价格怎样平衡?  如何在宝塔面板创建新站点?  网站视频怎么制作,哪个网站可以免费收看好莱坞经典大片?  详解jQuery中基本的动画方法  企业网站制作费用多少,企业网站空间一般需要多大,费用是多少?  ,有什么在线背英语单词效率比较高的网站?  公众号网站制作网页,微信公众号怎么制作?  胶州企业网站制作公司,青岛石头网络科技有限公司怎么样? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。