在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[] { "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[] { "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界面,因为指定主题下面找不到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() });
}
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实现多主题切换的更多相关文章

  1. 【转载】ASP.NET MVC重写URL制作伪静态网页,URL地址以.html结尾

    在搜索引擎优化领域,静态网页对于SEO的优化有着很大的好处,因此很多人就想把自己的网站的一些网页做成伪静态.我们现在在网络上发现很多博客网站.论坛网站.CMS内容管理系统等都有使用伪静态这一种情况,伪 ...

  2. asp.net mvc重写RequestValidator

    /// <summary> /// <httpRuntime requestValidationType="xxx.CustomRequestValidator" ...

  3. ASP.NET MVC Bundling and RequireJS

    关于ASP.NET MVC Bundling and RequireJS的取舍问题,最近比较困惑,我希望有一种方式可以结合两者的优点.作为.NET程序员,难道你没有过这方面的困惑吗? 因为我感觉各自都 ...

  4. ASP.NET MVC 4 Web编程

    http://spu.jd.com/11309606.html 第1章 入门第2章 控制器第3章 视图第4章 模型第5章 表单和HTML辅助方法第6章 数据注解和验证第7章 成员资格.授权和安全性第8 ...

  5. asp.net mvc 中 一种简单的 URL 重写

    asp.net mvc 中 一种简单的 URL 重写 Intro 在项目中想增加一个公告的功能,但是又不想直接用默认带的那种路由,感觉好low逼,想弄成那种伪静态化的路由 (别问我为什么不直接静态化, ...

  6. Asp.net Mvc 多级控制器 路由重写 及 多级Views目录 的寻找视图的规则 (多级路由) 如:Admin/Test/Index

    http://blog.csdn.net/buhuan123/article/details/26387427 目录(?)[-] 1那么我们再来看我们需要的访问方式如下图 razor视图的地址写成通配 ...

  7. ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase玩转URL

    http://www.cnblogs.com/John-Connor/archive/2012/05/03/2478821.html 引言-- 在初级篇中,我们介绍了如何利用基于ASP.NET MVC ...

  8. ASP.NET MVC URL重写与优化(1)-使用Global路由表定制URL

    ASP.NET MVC URL重写与优化(1)-使用Global路由表定制URL 引言--- 在现今搜索引擎制霸天下的时代,我们不得不做一些东西来讨好爬虫,进而提示网站的排名来博得一个看得过去的流量. ...

  9. [转载]ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase玩转URL

    引言-- 在初级篇中,我们介绍了如何利用基于ASP.NET MVC的Web程序中的Global文件来简单的重写路由.也介绍了它本身的局限性-依赖于路由信息中的键值对: 如果键值对中没有的值,我们无法将 ...

随机推荐

  1. (原创)如何在性能测试中自动生成并获取Oracle AWR报告

    版权声明:本文为原创文章,转载请先联系并标明出处 由于日常使用最多的数据库为Oracle,因此,最近又打起了Oracle的AWR报告的主意. 过去我们执行测试,都是执行开始和结束分别手动建立一个快照, ...

  2. poj 1056 IMMEDIATE DECODABILITY 字典树

    题目链接:http://poj.org/problem?id=1056 思路: 字典树的简单应用,就是判断当前所有的单词中有木有一个是另一个的前缀,直接套用模板再在Tire定义中加一个bool类型的变 ...

  3. MyEclipse2016统一字符编码

    MyEclipse一般没做修改,默认的字符编码是GBK,但是在实际的开发中常用的是utf-8,为了避免出现乱码等情况,我们需要把开发工具的编码都统一设置成utf-8,修改步骤如下: 1.打开MyEcl ...

  4. spring-boot整合dubbo:Spring-boot-dubbo-starter

    为什么要写这个小工具 如果你用过Spring-boot来提供dubbo服务,相信使用中有很多"不爽"的地方.既然使用spring boot,那么能用注解的地方绝不用xml配置,这才 ...

  5. 【原创】JQWidgets-TreeGrid 1、快速入门

    首先附上官方TreeGrid的传送门: http://www.jqwidgets.com/jquery-widgets-documentation/documentation/jqxtreegrid/ ...

  6. SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession作用域(Scope)和生命周期

    可以说每个MyBatis都是以一个SqlSessionFactory实例为中心的.SqlSessionFactory实例可以通过SqlSessionFactoryBuilder来构建.一是可以通过XM ...

  7. Docker4Windows -- 从外部(非本机host)访问 由docker container运行的程序

    背景 当我们在windows 上面运行docker container的时候,我们需要借助于模拟器(例如,Virtual box/Hyper V),她的目的主要是在我们的windows系统上面模拟出一 ...

  8. GTD:让大脑用来思考,而不是用来记事!

    前段时间听刘润大师分享了一套GTD时间管理方法理论,感觉非常受用!现拿来跟大家分享下,这套方法是刘润老师践行20多年总结提炼的精华,经亲自实践确实行之有效. 俗话说:工欲善其事,必先利其器!人生也是如 ...

  9. Hadoop中Hbase的体系结构

    HRegion 当一张表中的数据特别多的时候,HBase把表拆成多个块,每个块就是一个HRegion,每个region中包含这个表里的所有行 HRegionServer 数据库的数据存在HDFS文件系 ...

  10. Android 桌面不显示应用图标

    忽然有一天,运行自己的程序,发现桌面没有应用图标了. google了半天,也没什么发现. 最后发现是主Activity中: <action android:name="android. ...