转自:http://www.bcbbs.net/news/Content31274.aspx
对于ASP.NET学习的中期,TheBeerHouse 项目是一个不错的选择,这个项目几乎囊括了所有ASP.NET 2.0下所有的技术点,而且其设计的类图架构知识值得我们借鉴.关于此项目的介绍,在此不罗嗦,可以参看如下地址:
1. 源码下载: http://www.asp.net/Downloads/starter-kits/the-beer-house
2. 功能技术点介绍: http://www.codeplex.com/TheBeerHouse
3. 该项目真实网站: http://www.ericengler.com/ (该项目的总体界面可以在此处看到)
这一篇主要总结该网站换肤技术,如下图的下拉框的功能(由于本总结只有部分代码及其注释,因此需要对照着源码查看)
一. 文件说明
整个网站都需要Template.master这个母版页的支持,换肤所用到的文件和技术点
比较多,先罗列如下:
1.~/App_Code/BasePage.cs ---几乎所有页面都继承自此类
2.~/App_Code/Globals.cs ---获取自定义配置节,这个自定义配置节被映射到TheBeerHouseSection中
3.~/App_Code/Helpers.cs ---提供一些通用方法
4.~/App_Code/ConfigSection.cs ---实现自定义的节类型
5.~/Web.config ---配置文件
6.~/Web.sitemap --网站地图
7.~/Controls/ThemeSelector.ascx ---主题选择下拉框(此处做成用户控件)
8. ~/App_Themes/.... ---该目录下是整个网站主题选择的文件集合,是换肤的"原料",此技术下面会介绍
9. ~/Default.aspx --整个网站的首页,也是应用了上述母版也的内容页
二. 文件说明
1.BasePage.cs 类说明
此类是几乎所有内容页(*.aspx.cs)类的父类,也就是说,继承了此类的页面,运行时都得先到这个类来"报到".
那么此时 protected override void OnPreInit(EventArgs e) 事件就发挥作用了,由于此事件在页面的
Page_Load事件之前发生,那么我们就以主页Default.aspx的运行流程来查看效果.
主页Default.aspx基本没有后台代码,它一运行必须先执行BasePage.cs类中OnPreInit事件,代码见下面的流程分析.
2.Globals.cs说明
该静态类存放两个静态成员,ThemesSelectorID接受主题下拉框的控件ID,Settings是Web.Config自定义配置节点theBeerHouse的映射,而Setting类型是一个自定义的类TheBeerHouseSection,该类定义是在ConfigSection.cs文件中完成的.
Globals.cs
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Configuration;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace MB.TheBeerHouse.UI
{
public static class Globals
{
public static string ThemesSelectorID = "";
//获取自定义配置节,这个自定义配置节被映射到TheBeerHouseSection中,位于
//ConfigSection.cs文件中
public readonly static TheBeerHouseSection Settings =
(TheBeerHouseSection)WebConfigurationManager.GetSection("theBeerHouse");
}
}
3. Helper.cs说明
此类目前就提供一个方法GetThemes,该方法从App_Themes目录中动态获取里面所有的主题名称,并返回一个字符串数组,作为母版页上主题下拉框的数据源
4. ConfigSection.cs说明
Code
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace MB.TheBeerHouse
{
[说明]#region[说明]
//可以使用 ConfigurationSection 实现自定义的节类型。扩展 ConfigurationSection 类,
//以提供对自定义配置节的自定义处理和编程访问。
//http://msdn.microsoft.com/zh-cn/library/system.configuration.configurationsection(VS.80).aspx
//如下两个类需要和Globals.cs配合使用,作用就是在Web.config中使用自定义配置节,
//并把这个自定义配置节用自定义类的属性表示出来
#endregion
public class TheBeerHouseSection : ConfigurationSection
{
//用自定义类ContactFormElement代替节点
[ConfigurationProperty("contactForm", IsRequired = true)]
public ContactFormElement ContactForm
{
get { return (ContactFormElement)base["contactForm"]; }
}
}
public class ContactFormElement : ConfigurationElement
{
[ConfigurationProperty("mailSubject",
DefaultValue = "Mail from TheBeerHouse: {0}")]
public string MailSubject
{
get { return (string)base["mailSubject"]; }
set { base["mailSubject"] = value; }
}
[ConfigurationProperty("mailTo", IsRequired = true)]
public string MailTo
{
get { return (string)base["mailTo"]; }
set { base["mailTo"] = value; }
}
[ConfigurationProperty("mailCC")]
public string MailCC
{
get { return (string)base["mailCC"]; }
set { base["mailCC"] = value; }
}
}
}
三. 页面主题技术
本网站使用了两套主题皮肤,这两套主题皮肤名字分别是PlainHtmlYellow 和 TemplateMonster,它们已经在Helper.cs中获取并填充.
其中Controls.skin文件设定页面控件的外观,凡是看到页面控件HTML部分代码声明中带有SkinID字样的控件就是应用了该皮肤文件中对应控件的外观说明.
Default.css文件提供不同样式定义,不同的是需要应用该样式的页面不是通过<link> ....... 引用该样式文件的,而是直接通过页面主题访问的,得到了某个主题的页面就可以直接应用该主题里的样式文件和皮肤文件里定义的样式类选择器或者皮肤ID号了.结果是凡是应用了某个主题(如TemplateMonster主题名)的界面,都会应用该主题下所有样式设定,具体技术请查看关于主题设定的相关技术.
四. 流程介绍
上述说明,只是文件功能的罗列,我个人感觉还是从运行的主线出发,容易说明上述代码之间的关联关系.
1. 页面首页是Default页面,此页面后台无任何代码,关键是它继承了BasePage类,按理说首先执行的是BasePage类的OnPreInit事件,该事件重写了Page类的OnPreInit事件,但不然,由于BasePage类需要用到Globals这个静态类,那么第一次运行该网站时(如果不是,把右下角网站图标关闭在启动),其实最先运行的是Gloabals.cs类中的 public static string ThemesSelectorID = ""; 语句,鉴于此,我们可以在如下的地方设置两个个断点:
(1) Globals.cs 中在 public static string ThemesSelectorID = ""; 上设置断点
(2) BasePage.cs中 string id = Globals.ThemesSelectorID; 上设置断点 ‚
好,现在来看运行流程:
首先运行到断点, 声明一个静态成员变量ThemesSelectorID 用来保存页面上改变主题的控件的ID,接着获取自定义配置节,这个自定义配置节被映射到类TheBeerHouseSection中,位于ConfigSection.cs文件中,这里有些难以理解,需要同时对照着Web.Config,ConfigSection.cs,和此段代码来理解,具体解释见上述Globals.cs详细代码注释.
2. 接着跳入BasePage类的OnPreInit事件,下面的代码可以单步调试看运行状况,注释在BasePage类中已经说明的很详细,如下:
BasePage.cs
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace MB.TheBeerHouse.UI
{
public class BasePage:System.Web.UI.Page
{
protected override void OnPreInit(EventArgs e)
{
string id = Globals.ThemesSelectorID;
if (id.Length > 0)
{
//如果这次回传是由方案选择下拉框选择后引起的刷新,那么就将此次选择的方案
//应用上去,并将此次的方案存入个性化配置中,ProfileCommon是在运行的
//时候产生的一个类,Preferences是Web.Config配置节中的一个Profile下的一个扩展
//Group节点,专门用于存储方案和本地资源的语言
//判断此事件是否是由于选择皮肤的下拉框控件引起的回发
//如果是,就将页面主题 this.Theme修改为选择的主题名称,也就是
//App_Themes 中某个目录名称 PlainHtmlYellow / TemplateMonster,否则
//直接到会话中去取主题的值
if (this.Request.Form["__EVENTTARGET"] == id && !string.IsNullOrEmpty(this.Request.Form[id]))
{
this.Theme = this.Request.Form[id];
(HttpContext.Current.Profile as ProfileCommon).Preferences.Theme = this.Theme;
}
else
{
// if not a postback, or a postback caused by controls other then the theme selector,
// set the page's theme with the value found in the user's profile, if present
if (!string.IsNullOrEmpty((HttpContext.Current.Profile as ProfileCommon).Preferences.Theme))
this.Theme = (HttpContext.Current.Profile as ProfileCommon).Preferences.Theme;
}
}
base.OnPreInit(e);
}
}
}
其实换肤并且下次在启动可以自动记住上次换肤的结果的关键代码就是上面代码的如下语句:
this.Theme = this.Request.Form[id]; (1)
(HttpContext.Current.Profile as ProfileCommon).Preferences.Theme = this.Theme; (2)
this.Theme = (HttpContext.Current.Profile as ProfileCommon).Preferences.Theme; (3)
前两条语句和第三条语句分别位于If和Else中, (1) 用于将下拉框选择的值赋予当前页面的主题属性
(2)用于保存当前主题到个性配置中(关于个性配置知识点请参考我有关权限管理的两篇文章以及本学习笔记的下一篇),以便下次运行具有记忆主题的作用,此语句在(3)中实现
而页面在第一次匿名启动时用到的主题是在Web.Config中配置节
<pages theme="TemplateMonster" masterPageFile="~/Template.master"/> 中实现的.
这篇就介绍到这里,其实很多知识点还是自己边运行边理解的,由于本人表达能力有限,所以显得有些罗嗦,希望对些许人有帮助.就其价值而言,这些讨论其实还是有些表面工作的意思,因为毕竟我们还是在讨论应用级别的论题,也就是强调会的角度,而理论理解的东西可以参考博客园里排名靠前的牛人们的见解.