第15章 主题和皮肤

在ASP.NET中,通过主题和皮肤((Sin)特性能够把样式和布局存放到一组独立的文件中,总称为主题((Teme)。然后就可以把这个主题应用到所需要的任何站点,用于改变该站点内的页面和页面内控件的外观样式。通过改变主题的内容,而不用改变站点的单个页面,就可以轻易地改变站点的样式,从而达到页面内容与样式分离的效果。同时,主题也可以在开发者之间共享。

15.1 使用ASP.NET中的主题

主题类似于前面所讲的CSS(Cascading Style Sheets,层叠样式表),因为它们都可以为Web页面定义各种样式。但主题比CSS更进一步,它允许给应用程序的页面应用样式、图形,甚至CSS文件。可以任意在应用程序、页面或服务器控件级别上应用ASP.NET中的主题。

15.1.1 主题与CSS的区别

虽然使用CSS来控制页面的显示是一件非常容易的事情,但它也存在着一定的局限性。CSS规则只限于一组固定的样式特性,它允许你重用特定的格式化细节(如字体、边框、前景色和背景色等),但它却不能够控制ASP.NET控件的其他方面。例如,CheckBoxList控件有一些用于控制如何把选择项组织为行或列的属性。虽然这些属性影响的是控件的可视外观,但它们在CSS的范围之外,所以必须手工设置它们。此外,可能还希望在定义控件格式的同时定义控件的部分行为。例如,可能希望标准化Calendar控件的选择模式或者TextBox控件的折行等。很显然,这些都不可能通过CSS实现,因为它们不属于CSS控制的范畴。

面对上面的问题,ASP.NET中的主题弥补了这一缺陷。和CSS相似,主题也允许定义一组作用于多个页面中控件的样式特性。但和CSS不同的是,主题不是由浏览器实现的。相反,它们是在服务器上实现的ASP.NET自有的解决方案。虽然主题不会代替CSS,但它们可以提供一些CSS不能提供的特性。因此,主题与CSS存在着如下区别:

1)主题是基于控件的,而不是HTML;而CSS则是完全基于HTML的。主题是ASP.NET中独有的特性,它允许定义和重用几乎所有的ASP.NET控件属性;而CSS只是直接作用于HTML的样式特性。

2)主题应用在服务器上。主题作用到页面时,样式化后的最终页面被传送给用户;而使用CSS时,浏览器同时接收到页面和样式信息,并在客户端合并它们。

3)可以通过配置文件来应用主题。同母版页一样,可以通过配置文件来应用主题。这样不必修改任何一个页面就可以对整个文件或整个网站应用主题;而CSS只能够在页面里进行引用。

4)主题不能够像CSS那样层叠。如果在一个主题和一个控件里同时指定了一个属性,那么主题里定义的值会覆盖控件的属性。不过,有一个办法可以改变这个行为—提高页面属性的优先级,这样主题的行为将更像CSS了。

5)在主题中可以包含CSS。相对于CSS来讲,主题代表了一个更高层次的模型。因此,可以把CSS作为主题的一部分来在主题中应用它。

15.1.2 主题文件夹和外观

所有的主题都是与应用程序相关的,要在Web应用程序里面使用主题,就必须在项目里创建一个定义它的文件夹,这个文件夹必须放在一个叫做App_Themes的文件夹里,而App_Themes又必须位于Web应用程序的最上层。通常,可以通过如下两种方法在项目中添加App_Themes文件夹:

1)在Visual Studio中右击项目,选择“Add”|“New Folder”命令来添加一个新文件夹,命名为App_Themes。

2)在Visual Studio中右击项目,选择“Add”|“Add ASP.NET Folder”|“Theme”命令来创建这个文件夹。

值得注意的是,App_Themes文件夹中的主题文件夹不使用通常的文件夹图标,而使用包含一个画笔的文件夹图标。

创建好App_Themes文件夹之后,就可以在App_Themes文件夹里面为应用程序中使用的每个主题都创建一个主题((teme)文件夹。只要每个主题都在一个单独的文件夹里,应用程序就可以定义多个主题,如图15-1所示。

figure_0516_0400

图 15-1 主题文件夹结构

对于一个给定的页面,每次只能有一个主题处于活动状态。通常情况下,在一个主题文件夹里都必须包含如下三种元素:

1)一个Skin文件:即皮肤文件,如default.skin。

2)CSS文件:需要在Skin文件中使用的CSS文件。

3)图像:需要在Skin文件中使用的图像文件。

在主题文件夹里面,可以决定是创建多个Skin文件还是把所有的控件标签都放到一个Skin文件里。其实,这两种方式是等效的,因为ASP.NET把同一个主题目录里的所有Skin文件都看成是同一个主题定义的一部分。

除此之外,ASP.NET还支持全局主题。一般情况下,全局主题是放置在C:\Inetpub\wwwroot\aspnet_client\system_web\4_0_21006\Themes目录里的主题,这里假设c:\Inetpub\wwwroot是IIS Web服务器的根。不过,即使打算创建多个使用相同主题的网站,仍然建议使用本地主题。使用本地主题更便于发布Web应用程序,也为将来引入网站差异带来灵活性。

如果有一个和全局主题同名的本地主题,那么本地主题的优先级高于全局主题,全局主题被忽略。因此,全局主题和本地主题并不会相互合并。同CSS一样,ASP.NET并没有提供任何预定义的主题供使用。也就是说,必须从零开始创建主题,或者从相关网站下载示例主题。在图15-1中,已经提供了一些能够直接使用的主题,供在本节学习。在15.2节中,将详细阐述如何创建自己的主题。

15.1.3 给单个ASP.NET页面应用主题

如图15-1所示,已经为项目15-1添加了一些主题。为了说明如何应用这些主题,接下来先创建一个基本页面,该页面包含一些文本和一个时间控件,如代码清单15-1所示。

代码清单15-1没添加主题的ThemeTest.aspx页面


<%@Page Language="C#"AutoEventWireup="true"

CodeBehind="ThemeTest.aspx.cs"

Inherits="_15_1.ThemeTest"%>

<!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head runat="server">

<title></title>

</head>

<body>

<form id="form1"runat="server">

<br/>

<p>

ASP.NET主题测试

</p>

<hr>

<h1>

主题简介

</h1>

<div>

在ASP.NET中,主题和皮肤((Sin)特性使你能够把样式和布局存放

到一组独立的文件中,总称为主题((Teme)……

<br/>

</div>

<asp:Calendar ID="Calendar1"runat="server"/>

</form>

</body>

</html>


运行代码清单15-1,会发现页面的所有的文本和控件显示都是页面默认值,如图15-2所示。

figure_0517_0401

图 15-2 没添加主题的ThemeTest.aspx页面运行结果

要让一个主题对Web页面起作用,就需要在Page指令内把Theme属性指定为主题所在的文件夹名称。这样,ASP.NET将会自动扫描该主题内的所有皮肤文件。Theme属性的使用方法如下面的代码所示:


<%@Page Language="C#"AutoEventWireup="true"

CodeBehind="ThemeTest.aspx.cs"Inherits="_15_1.ThemeTest"

Theme="MyTheme"%>


对于Theme属性的设置,可以手动修改,也可以通过在设计时从“属性”窗口中选择DOCU MENT对象,然后设置Theme属性(它提供一个带有应用程序内全部主题的便捷的下拉框),Visual Studio将自动修改相应的Page指令。属性设置如图15-3所示。

如图15-3所示,把MyTheme主题应用到页面后,ASP.NET会考虑到Web页面上的每个控件,并检查皮肤文件,以便查看是否为控件定义了属性。如果ASP.NET在皮肤文件里发现了匹配的标签,那么从皮肤文件获得的信息就会覆盖控件的当前属性。

添加MyTheme主题之后的ThemeTest.aspx页面运行结果如图15-4所示。

figure_0518_0402

图 15-3 给页面设置主题

figure_0518_0403

图 15-4 添加MyTheme主题的ThemeTest.aspx页面运行结果

如图15-4所示,应用了MyTheme主题之后,可以看到页面里所有的内容,包括字体、字体的颜色、时间控件等外观都改变了,页面变得比以前漂亮多了。

15.1.4 StyleSheetTheme属性

在ASP.NET中,如果一个控件在声明时就定义了的相关属性,而后又在主题里定义了自己的相关属性,那么这两种属性定义将会产生冲突。这时,ASP.NET会优先使用主题里定义的属性,从而忽略其在声明时定义的属性。

但有时可能会希望改变这一优先级,让控件自定义的属性优先于主题里定义的属性,这样控件可以覆盖某些特定的细节,从而优化主题。ASP.NET允许你这么做,其实现方法也很简单,只要在Page指令里用StyleSheetTheme属性替代Theme属性即可。

为了演示这个属性,下面在代码清单15-1的Page指令中用StyleSheetTheme属性替代Theme属性。如下面的代码所示:


<%@Page Language="C#"AutoEventWireup="true"

CodeBehind="ThemeTest.aspx.cs"

Inherits="_15_1.ThemeTest"

StylesheetTheme="MyTheme"%>


设置好StyleSheetTheme属性之后,继续在页面的Calendar控件里设置一个BackColor属性。如下面的代码所示:


<asp:Calendar ID="Calendar1"runat="server"

BackColor="Blue"/>


现在Calendar控件自定义的蓝色背景则优先于主题内定义的背景色,运行结果如图15-5所示。

figure_0519_0404

图 15-5 ThemeTest.aspx页面使用StyleSheetTheme属性的运行结果

15.1.5 把主题应用于整个应用程序

除了在Page指令中使用Theme属性或者StyleSheetTheme属性把ASP.NET主题应用于ASP.NET页面之外,还可以通过Web.config文件把它应用于整个应用程序。配置方法如下面的代码所示:


<system.web>

<pages theme="MyTheme"/>

</system.web>


如果希望使用样式表行为以便主题不会覆盖自定义的控件属性,那么可以使用styleSheetTheme属性代替theme属性。如下面的代码所示:


<system.web>

<pages styleSheetTheme="MyTheme"/>

</system.web>


无论采用哪种方式,只要在Web.config文件里指定了主题,而页面里又没有单独设置主题,那么该主题就会自动应用到网站的所有页面。如果某个页面单独指定了Theme属性或StyleSheetTheme属性,页面设置的优先级将高于Web.config的设置。

15.1.6 禁用服务器控件中的主题

无论主题是在Web.config文件里设置的,还是在页面上设置的,有时都希望某些特殊的控件使用自己已定义的属性,而不被主题里定义的属性所替换。例如,在代码清单15-1中,希望页面上的其他所有文本或者控件都使用主题定义的属性样式,而Calendar控件则使用自己定义的属性样式。如定义Calendar控件的BackColor属性为Blue:


<asp:Calendar ID="Calendar1"

runat="server"

BackColor="Blue"/>


在上面的代码中,如果在Page指令中使用的是Theme属性,那么Calendar控件的BackColor属性定义将被主题里定义的BackColor属性所覆盖;但如果使用的是StyleSheetTheme属性,虽然会采用BackColor属性的定义,但这里却存在两个问题:

1)页面所有的控件都会优先采用它们自定义的属性,从而还是达不到上面的要求(页面上的其他所有文本或者控件都使用主题定义的属性样式,而Calendar控件则使用自定义的属性样式)。

2)它是一种覆盖关系,而并非一种全部禁用的关系。也就是说,在Calendar控件中除了BackColor属性使用控件自己定义的值外,其他未定义的属性仍然采用主题里定义的属性。

基于上面两种原因,很显然StyleSheetTheme属性不能够满足要求,这时就需要使用控件里的EnableTheming属性,即将EnableTheming属性的值设置成false。如下面的代码所示:


<asp:Calendar ID="Calendar1"

runat="server"

BackColor="Blue"

EnableTheming="false"/>


这样,就在Calendar控件里面禁用了所有主题里设置的属性样式,从而完全采用自定义的样式。而MyTheme主题却仍然应用于整个页面,该主题就会应用于该页面上除该Calendar控件之外的所有控件。运行结果如图15-6所示。

figure_0520_0405

图 15-6 ThemeTest.aspx页面禁用Calendar

运用这种方法,如果要关闭页面上多个控件的主题特性,可以使用Panel控件(或其他容器控件)封装这些控件,并把Panel控件的Enable Theming属性设置为false。这将禁止把主题特性应用于Panel控件包含的所有控件。

控件主题的运行结果

15.1.7 禁用Web页面上的主题特性

如果在Web.config文件中设置了整个应用程序的主题,那么如何使单个ASP.NET页面不应用主题特性呢?答案很简单,可以在页面级别上禁用theme设置,就像在服务器控件级别上禁用它一样。

与控件一样,Page指令同样也包含EnableTheming属性,它可用于从ASP.NET页面中禁用theme设置。为了禁用通过Web.config文件中的theme设置应用的主题特性,可以按照如下方式进行设置:


<%@Page Language="C#"EnableTheming="false"

AutoEventWireup="true"CodeBehind="ThemeTest.aspx.cs"

Inherits="_15_1.ThemeTest"%>


这个语句把主题设置为空,也就禁用了在Web.config文件中指定的任何设置。EnableTheming属性在页面或控件上设置为false时,就不搜索Theme目录,从而不应用相关的皮肤文件。如果它在页面或控件上设置为true,就会搜索Theme目录,应用皮肤文件。

因此,如果在页面上把EnableTheming属性设置为false,从而禁用了主题。但这时仍可以把该页面上某个控件的EnableTheming属性设置为true,给它应用主题。如下面的代码所示:


<asp:Calendar ID="Calendar1"runat="server"

EnableTheming="true"/>