zoukankan      html  css  js  c++  java
  • asp.net MVC 切换网站主题

    首先要有一些定义后的CSS文件,本例是用Bootstrap作为前端框架,可以从http://bootswatch.com/网站上下载一些主题文件,也就是一些定义好的Bootstap.css的文件。

    然后将这些文件放到网站中Content文件夹Themes 下,目录组织结构如图:

    二、然后在网站根目录新建一个文件夹为Helper,在下面建立一个Bootstrap.cs文件。

    public class Bootstrap
    {
    public const string BundleBase = "~/Content/css";

    //类作为一个类的成员。
    public class Theme
    {
    public const string Cerulean = "Cerulean";
    public const string Darkly = "Darkly";
    public const string Flatly = "Flatly";
    public const string Spacelab = "Spacelab";
    public const string Stock = "Stock";
    public const string Superhero = "Superhero";
    }

    public static HashSet<string> Themes = new HashSet<string>
    {
    Theme.Cerulean,
    Theme.Darkly,
    Theme.Flatly,
    Theme.Spacelab,
    Theme.Stock,
    Theme.Superhero
    };


    public static string Bundle(string themename)
    {
    return BundleBase + themename;
    }
    }
    }

    三、打开app-start文件夹下BundleConfig文件,修改如下:将每个主题都捆绑

    foreach (var theme in Bootstrap.Themes)
    {
    var stylePath=string.Format("~/Content/Themes/{0}/bootstrap.css",theme);
    bundles.Add(new StyleBundle(Bootstrap.Bundle(theme)).Include( //创建捆绑 ,"~/Content/css+themename"
    stylePath,
    "~/Content/site.css"));

    第一种方案

     以下是多选一操作,假使把主题名字存放在Application 应用程序全局变量中。

    四、要更改主题,可以把主题的名字字符串放在session 当中,也可以放在数据库中。都有缺点。session["ThemeName"]易丢失,跟客户端浏览器有关,如果存放在数据库中,可以与用户相关联。但我们这里的需求是通过后台更改前台的显示样式,所有可以把主题的名字字符串放在Application 或cache当中,这是应用程序全局共享的。

    打开全局应用程序配置文件Global.asax.cs,

    public class MvcApplication : System.Web.HttpApplication
    {
    protected void Application_Start()
    {
    AreaRegistration.RegisterAllAreas();
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    Application["CssTheme"] = Bootstrap.Theme.Stock; //增加应用程序状态集合。
    }
    }

    五、更改布局页_Layout 。因为如果布局页引入了主题样式文件,更改了布局页,全站的样式都更改了。 

    @using MajorConstruction.Helpers;
    @{
    var theme = System.Web.HttpContext.Current.Application["CssTheme"] as string ?? Bootstrap.Theme.Stock; //当前请求上下文的应用程序状态。  //通过应用程序上下文对象的当前请求调用 Application对象。??为空值合并运算符,如果为空,就为默认的Stock 主题。
    }

    @Styles.Render(Bootstrap.Bundle(theme))
    @Scripts.Render("~/bundles/modernizr")

    六、更改主题了。定义一个ThemeContoller的控制器。get返回View,Post执行更改。

    public class ThemeController : Controller
    {
    // GET: Admin/Theme
    public ActionResult ChangeTheme()
    {

    return View();
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult ChangeTheme(string theme)
    {
    HttpContext.Application["CssTheme"] = theme;
    return View();
    }

    七、定义 更改视图,显示出全部的主题,供选择。因为这个页面只需要提交一个主题名字字符串到控制器就可以了,所有我们使用了一个表单里面有一个隐藏字段,通过AJax的方式提交。

    @using MajorConstruction.Helpers;

    @{
    ViewBag.Title = "更改主题";
    }

    <h2>@ViewBag.Title</h2>
    <hr />
    <div class="container">
    <div class="row">
    @foreach (var theme in Bootstrap.Themes)
    {
    <div class="col-md-3">
    <div class="panel panel-default">  //使用Bootstrap的面板 ,不要标题。因为只是为了加一个边框,所以只使用了panel-body
    <div class="panel-body">
    <h2>@theme</h2>
    <br />
    <br />
    <p>
    @{
    bool IsCurrent = theme == HttpContext.Current.Application["CssTheme"].ToString();   //判定当前遍历的主题是不是正在使用的,如果是正在使用的,更改按钮就变灰。把主题绑定在链接的上JavaScript函数
    string btnDisabled = IsCurrent ? "disabled" : null;
    }

    //下面是一个按钮形式的链接,执行Javascript脚本 ,绑定主题名字值。主题名字必须加引号。不然,会提示 Uncaught ReferenceError: XX 未定义 is not defined.
    <a class="btn btn-success @btnDisabled" href="javascript:SetActive('@theme')">应用主题</a> <!--把主题绑定在JavaScript函数上,通过调用Javascript函数提交隐藏表彰。-->
    <!--还需要特别注意,调用 javascript 函数的参数@theme必须加引号,如果不加引号,总是谷哥浏览器总是提示 Uncaught ReferenceError: XX 未定义 is not defined,因为@theme变量本来就不是值类型,而是HashSet类型-->

    以前就是没加引号,搞了半天,都提交不起,什么方法都试过了,还以为是服务端代码有问题。
    </p>
    </div>

    </div>
    </div>
    }

    </div>
    </div>


    @using (Html.BeginForm("ChangeTheme", "Theme", FormMethod.Post, new { id = "themeForm" }))
    {
    @Html.AntiForgeryToken()
    <input type="hidden" name="theme" id="themeInput" />
    }

    @section Scripts {
    @Scripts.Render("~/bundles/jqueryval")

    <script type="text/javascript">
    var changeThemeUrl = '@Url.Action("ChangeTheme")';


    function SetActive(themename) {
    $("#themeInput").val(themename); //将参数值传递给隐藏表单。

    $("#themeForm").submit(); //提交表单,将 theme=themename 发送至控制器POST 方法中。

    }

    </script>

    大功告成。
     
    第二种方案。也可以把主题的名字放在数据库当中。也就是从第四步重新开始。
    四、建立模型,在Model文件夹下建立一个BootstrapTheme.cs文件。

    namespace MajorConstruction.Models
    {
    public class BootstrapTheme
    {
    [Display(Name="主题ID")]
    public string BootstrapThemeID { get; set; }

    [Display(Name="主题名称")]
    [Required(AllowEmptyStrings=false)]
    [StringLength(50,ErrorMessage="{0}不能超过50个字符")]
    public string ThemeName { get; set; }

    [Display(Name="主题描述")]
    [StringLength(50,ErrorMessage="{0}不能超过50个字符")]
    public string ThemeDescription { get; set; }

    [Display(Name ="是否当前主题")]
    public bool? IsActived { get; set; }  

    public BootstrapTheme()
    {
    this.BootstrapThemeID = Guid.NewGuid().ToString();
    }

    }
    }

    五、将BootstrapTheme 类加入到数据库上下文当中。可以设置一个数据库上下文的DbContext 的静态构造函数和初始化类,用于在数据库生成初始值。静态构造方法调用这个初始化类。

    1、加入到DbContext 中。

    namespace MajorConstruction.Models
    {
    public class MajorContext:DbContext
    {
    public DbSet<Category> Categories { get; set; }

    public DbSet<Article> Articles { get; set; }

    public DbSet<Course> Courses { get; set; }

    public DbSet<Resource> Resources { get; set; }

    public DbSet<BootstrapTheme> BootstrapThemes { get; set; }

    public MajorContext()
    : base("MajorContext")
    { }


    //静态构造函数。MSDN:静态构造函数用于初始化任何静态数据,或用于执行仅需执行一次的特定操作。在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数  。使用静态构造函数感觉要比 在config 配置文件里修改<EntityFramework>节点下增加<contexts><contexts>来得方便。或者在gloabe.asax 下设置初始化类来得方便。

    <entityFramework>
      <contexts>
        <context type="ContosoUniversity.DAL.SchoolContext, ContosoUniversity">
          <databaseInitializer type="ContosoUniversity.DAL.SchoolInitializer, ContosoUniversity" />
        </context>
      </contexts>

    2、设置静态构造函数
    static MajorContext()
    {
    //设置数据库初始化器,它就在应用程序运行的时候加载。
    Database.SetInitializer<MajorContext>(new MajorContextInitializer());
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }

    }
    }

    3、在Model 文件下新建一个初始化为类,MajorContextInitializer。

    namespace MajorConstruction.Models
    {
    public class MajorContextInitializer:DropCreateDatabaseIfModelChanges<MajorContext>
    {
    protected override void Seed(MajorContext context)
    {
    var themeList = new List<BootstrapTheme>()
    {
    new BootstrapTheme { BootstrapThemeID=Guid.NewGuid().ToString(), ThemeName="Stock" ,ThemeDescription="默认主题。黑色背景导航条,蓝色背景标题", IsActived=true},
    new BootstrapTheme { BootstrapThemeID=Guid.NewGuid().ToString(), ThemeName="Cerulean" ,ThemeDescription="深蓝色背景导航条,浅蓝色背景标题", IsActived=false},
    new BootstrapTheme { BootstrapThemeID=Guid.NewGuid().ToString(), ThemeName="Cosmo" ,ThemeDescription="天蓝色背景导航条,天蓝色背景标题", IsActived=false},
    new BootstrapTheme { BootstrapThemeID=Guid.NewGuid().ToString(), ThemeName="Darkly" ,ThemeDescription="翠绿色背景导航条,蓝黑色背景标题,黑色背景", IsActived=false},
    new BootstrapTheme { BootstrapThemeID=Guid.NewGuid().ToString(), ThemeName="Flatly" ,ThemeDescription="翠绿色背景导航条,蓝黑色背景标题", IsActived=false},
    new BootstrapTheme { BootstrapThemeID=Guid.NewGuid().ToString(), ThemeName="Sandstone" ,ThemeDescription="绿色背景导航条,蓝黑色背景标题", IsActived=false},
    new BootstrapTheme { BootstrapThemeID=Guid.NewGuid().ToString(), ThemeName="Spacelab" ,ThemeDescription="深蓝色背景导航条,深蓝色背景标题", IsActived=false},
    new BootstrapTheme { BootstrapThemeID=Guid.NewGuid().ToString(), ThemeName="Superhero" ,ThemeDescription="橘黄色背景导航条,橘黄色背景标题,小号字", IsActived=false},
    new BootstrapTheme { BootstrapThemeID=Guid.NewGuid().ToString(), ThemeName="United" ,ThemeDescription="朱红色背景导航条,橘黄色背景标题", IsActived=false},
    new BootstrapTheme { BootstrapThemeID=Guid.NewGuid().ToString(), ThemeName="Yeti" ,ThemeDescription="蓝色背景导航条,蓝色背景标题,小号字", IsActived=false}
    };

    foreach (var theme in themeList)
    {
    var _theme = context.BootstrapThemes.Where(x => x.ThemeName == theme.ThemeName).FirstOrDefault();
    if (_theme == null)
    {
    context.BootstrapThemes.Add(theme);

    }

    }
    context.SaveChanges();
    base.Seed(context);
    }
    }
    }

    六、执行更改主题的操作。

    控制器:

    [Authorize(Roles = "SuperAdmin,Teacher")]
    public class ThemeController : Controller
    {
    /*
    private List<BootstrapViewModel> themeList = new List<BootstrapViewModel>()
    {
    new BootstrapViewModel { ThemeName="Stock" ,ThemeDescription="默认主题。黑色背景导航条,蓝色背景标题"},
    new BootstrapViewModel { ThemeName="Cerulean" ,ThemeDescription="深蓝色背景导航条,浅蓝色背景标题"},
    new BootstrapViewModel { ThemeName="Cosmo" ,ThemeDescription="天蓝色背景导航条,天蓝色背景标题"},
    new BootstrapViewModel { ThemeName="Darkly" ,ThemeDescription="翠绿色背景导航条,蓝黑色背景标题,黑色背景"},
    new BootstrapViewModel { ThemeName="Flatly" ,ThemeDescription="翠绿色背景导航条,蓝黑色背景标题"},
    new BootstrapViewModel { ThemeName="Sandstone" ,ThemeDescription="绿色背景导航条,蓝黑色背景标题"},
    new BootstrapViewModel { ThemeName="Spacelab" ,ThemeDescription="深蓝色背景导航条,深蓝色背景标题"},
    new BootstrapViewModel { ThemeName="Superhero" ,ThemeDescription="橘黄色背景导航条,橘黄色背景标题,小号字"},
    new BootstrapViewModel { ThemeName="United" ,ThemeDescription="朱红色背景导航条,橘黄色背景标题"},
    new BootstrapViewModel { ThemeName="Yeti" ,ThemeDescription="蓝色背景导航条,蓝色背景标题,小号字"}
    };
    */

    private BootstrapThemeService _bootstrapThemeService = new BootstrapThemeService(ContextFactory.GetCurrentContext());  //使用库模式,
    // GET: Admin/Theme
    public async Task<ActionResult> ChangeTheme()
    {
    var themeList = await _bootstrapThemeService.FindAll().OrderBy(c => c.ThemeName).ToListAsync(); //是使用异步方法,必须引入System.Data.Entity 和 System.Threading.Tasks 命名空间。
    return View(themeList);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> ChangeTheme(string theme)
    {
    //HttpContext.Application["CssTheme"] = theme;
    if (!string.IsNullOrEmpty(theme)) //找来好久错误,没想到是这个判断错了,少了一个"!"号。应该是如果此字符串非空,就来将该主题设为 选中状态。其他主题设为非选中状态。设置断点调试才发现此问题。
    {
    foreach (var item in await _bootstrapThemeService.FindAll().ToListAsync())
    {
    if (item.ThemeName == theme)
    {
    item.IsActived = true;
    }
    else
    {
    item.IsActived = false;

    }
    _bootstrapThemeService.Update(item);

    }
    }
    //return View(themeList);
    return View(await _bootstrapThemeService.FindAll().OrderBy(c =>c.ThemeName).ToListAsync());
    }


    public async Task<ActionResult> DefaultTheme()  //设置默认主题。
    {

    // HttpContext.Application["CssTheme"] = "Stock";
    // return View("ChangeTheme", themeList);
    foreach (var item in await _bootstrapThemeService.FindAll().ToListAsync())
    {
    if (item.ThemeName == "Stock")
    {
    item.IsActived = true;
    }
    item.IsActived = false;
    _bootstrapThemeService.Update(item);
    }

    return View("ChangeTheme",await _bootstrapThemeService.FindAll().OrderBy(c =>c.ThemeName).ToListAsync());


    }
    }
    }

    视图:跟前面第一种方案差不多,主要在页面中建立一个隐藏表单,利用ajax 的方式调用 控制器的方法。

    @using MajorConstruction.Helpers;
    @model IEnumerable<MajorConstruction.Models.BootstrapTheme>
    @{
    ViewBag.Title = "更改主题";
    }

    <h2>@ViewBag.Title</h2>
    <hr />
    <div class="container">
    <div class="row">
    @foreach (var theme in Model)
    {
    <div class="col-md-3">
    <div class="panel panel-default">
    <div class="panel-body">
    <h2>@theme.ThemeName</h2>
    <p class="lead">
    @theme.ThemeDescription
    </p>
    <p>
    @{
    // bool IsCurrent = @theme.ThemeName == HttpContext.Current.Application["CssTheme"].ToString();
    //string btnDisabled = IsCurrent ? "disabled" : null;

    string btnDisabled = theme.IsActived == true ? "disabled" : null;
    }
    <a class="btn btn-primary @btnDisabled" href="javascript:SetActive('@theme.ThemeName')">应用主题</a> <!--把主题绑定在JavaScript函数上,通过调用Javascript函数提交隐藏表彰。-->
    <!--还需要特别注意,调用 javascript 函数的参数@theme必须加引号,如果不加引号,总是谷哥浏览器总是提示 Uncaught ReferenceError: XX 未定义 is not defined,因为@theme变量本来就不是值类型,而是HashSet类型-->
    </p>
    </div>

    </div>
    </div>
    }

    </div>
    </div>


    @using (Html.BeginForm("ChangeTheme", "Theme", FormMethod.Post, new { id = "themeForm" }))
    {
    @Html.AntiForgeryToken()
    <input type="hidden" name="theme" id="themeInput" />
    }

    @section Scripts {
    @Scripts.Render("~/bundles/jqueryval")

    <script type="text/javascript">
    var changeThemeUrl = '@Url.Action("ChangeTheme")';


    function SetActive(themename) {
    $("#themeInput").val(themename); //将参数值传递给隐藏表单。

    $("#themeForm").submit(); //提交表单,将 theme=themename 发送至控制器POST 方法中。

    }

    </script>

    }

    七、更改前台页面的布局页_Layout页。

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - 省级重点专业</title>
    @using MajorConstruction.Helpers;
    @{
    //var theme = System.Web.HttpContext.Current.Application["CssTheme"] as string ?? Bootstrap.Theme.Stock; //当前请求上下文的应用程序状态。 将Theme值保存在全局应用程序变量Application中按理说不应该过期,但是事实上挂在IIS上时会过期,一段时间后,就会恢复默认值。还不如直接保存在数据库中。
    var theme = Bootstrap.Theme.Stock;
    if (ViewBag.ActiveTheme != null)  //   ViewBag.ActiveTheme 对象 表示从控制器操作方法传过来的当前选中的主题。但是为每个方法或每个控制器都返回一个ViewBag对象,一个一个的设置麻烦。所以下面使用了操作过滤器。
    {
    theme = ViewBag.ActiveTheme;
    }
    }

    @Styles.Render(Bootstrap.Bundle(theme))
    @Scripts.Render("~/bundles/modernizr")

    </head>

    八、使用操作过滤器返回ViewBag.ActiveTheme 对象。

    在网站根目录下建立一个Filters 文件夹,新建一个ThemeFilter 的cs文件,继承自ActionFilterAttribute 类。覆写OnActionExecuted方法。

    namespace MajorConstruction.Filters
    {
    public class ThemeFilter:ActionFilterAttribute //操作过滤器属性需要引用 System.Web.Mvc 命名空间;
    {
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
    string ActiveThemeName = "Stock";
    var _BootstrapThemeService = new BootstrapThemeService(ContextFactory.GetCurrentContext());
    var activeTheme = _BootstrapThemeService.FindAll().Where(x =>x.IsActived == true).FirstOrDefault();
    if (activeTheme != null)
    {
    ActiveThemeName = activeTheme.ThemeName;

    }

    filterContext.Controller.ViewBag.ActiveTheme = ActiveThemeName;   //该 过滤上下文的 控制器都返回一个ViewBag.ActiveTheme 对象,它的值是目前选中的主题的名字。
    base.OnActionExecuted(filterContext);
    }
    }
    }

    九:将使用该布局页的控制器的加上 [ThemeFilter] 的数据注解。那么该控制器的所有操作方法都将返回一个ViewBag.ActiveTheme 对象,并用它的值是目前选中的主题的名字。

    [ThemeFilter]
    public class HomeController : Controller
    {
    private CategoryService _categoryService = new CategoryService(ContextFactory.GetCurrentContext());
    private ArticleService _articleService = new ArticleService(ContextFactory.GetCurrentContext());
    private CourseServie _courseService = new CourseServie(ContextFactory.GetCurrentContext());
    private ResourceService _resourceService = new ResourceService(ContextFactory.GetCurrentContext());
    private MajorContext db =new MajorContext();
    public ActionResult Index()

    当然,也可以设置全局应用程序过滤器。打开根目录下的App_Start 文件夹下的FilterConfig.cs 文件。

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        filters.Add(new ThemeFilter());   //全局应用程序过滤器,程序中所有控制器的所有方法都将执行这个过滤操作。
    }

     这样,就将主题数据持久化的保存在了数据库中,应用程序重新启动,或者是过一段时间,都会保存以前的主题。

    截图如下:

  • 相关阅读:
    Censored! POJ
    POJ
    HDU
    ionic中的生命周期函数
    ionic项目相关的操作命令
    数组的join()函数操作
    pop()实现逐个删除数组最后一位并输出
    CSS制作彩虹效果
    ionic2 页面加载时图片添加的问题
    升级ionic版本后,创建新项目报Error Initializing app错误解决
  • 原文地址:https://www.cnblogs.com/liuyuanhao/p/4483659.html
Copyright © 2011-2022 走看看