6.4 主题

       Windows XP诞生的时候,看起来就像卡通一样--大大的绿色按钮、蓝色渐变的背景、充满了曲线效果的边框。微软的工程师显然知道在做什么,因为此后(只是几年之后),之前给人印象深刻的Windows 2000再看起来就相当的丑陋不堪了。Windows XP的视觉效果是由主题驱动的。默认的Luna主题有三个版本:蓝色、充满金属质感的银色和看起来有点肉麻的绿色。

       不过主题的实现有点ugly。主题一方面需要和已经存在的那些应用程序兼容,那些应用程序对主题一无所知;一方面需要提供给新的应用程序使用主题的优越性能。XP的主题不但编码风格很复杂,还依赖于Win32 HWND模式。而WPF不是这个样子的。当你允许一个WPF应用程序,窗体的主框架依然是一个和Windows相关的“视窗”。但在窗体内部的每一处都是属于WPF的,Windws主题代码不能对其操作。

       幸运的是,尽管在传统的Windows里实现主题犹如梦魇,WPF的构建充满了样式的思想。如果主题只是样式的集合会怎样呢?为WPF提供主题就像构建并引用一个资源目录那么简单。很不幸,WPF(尚且)没有统治世界,因此WPF应用程序夹在Windows主题和WPF样式之间了。

       WPF团队解决这个问题的方法是模仿Windows主题构建WPF样式(注:依此解决WPF和Windows主题不一致的问题)。当WPF应用程序启动时,WPF根据当前使用的Windows主题加载与之相应的自己的主题。这样,你的应用程序看起来和其它程序一样--前提是你使用的是Windows的标准主题(注:这些主题在WPF里有相应的主题)。这个方法在一定程度上已经被打破了。微软在Zune版封装了一个新的主题。WPF对Zune主题一无所知,当WPF应用程序在Zune主题下运行时,效果使用经典的Windows 2000外观效果。

 

6.4.1指定使用某个主题

        你可以不使用默认对应的主题。如果需要,你可以明确的指定使用某个主题。引用主题的方式和引用其它独立的资源目录大致相同。唯一的区别是需要引用包含主题的assembly,且必须使用assembly的全名,包括它的识别码。列表6.11显示了窗体引用Windows Vista Aero主题时的关键源代码。

<Window.Resources>
<ResourceDictionary Source="/PresentationFramework.Aero, Version=3.0.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35
;component/themes/aero.normalcolor.xaml" />
</Window.Resources>
Listing 6.11 Referencing the Vista Aero theme

        这段代码引用了微软提供的一个assembly,PresentationFramework.Aero,在其中包含了Aero主题。Version,Culture和PublicKeyToken都是assembly全名中的一部分。主题资源存储在component/themes/aero.normalcolor.xaml。

如果拥有此代码的程序运行在XP系统,其效果也如在Vista的Aero主题下相同,只是标题栏和边框还是XP的风格。

 

何时使用不同的主题

       通常使用和操作系统一致的主题比较好。如果一个程序和别的程序风格迥异的话,给用户的感觉不会很好。

有时,别具一格也是有必要的。程序的外观效果也是其卖点之一,为你的程序构建一个主题也是不错的选择。

 

       微软提供了六种主题。表6.1显示如何引用这些主题。必须告知的是微软不赞成私自在内置主题之间切换。

 

Aero— Vista默认主题
<ResourceDictionary
Source="/
PresentationFramework.Aero,
Version=3.0.0.0,
Culture=neutral,
PublicKeyToken=31bf3856ad364e35
;component/themes/
aero.normalcolor.xaml" />


Luna, Default—XP默认蓝色主题

<ResourceDictionary
Source="/
PresentationFramework.Luna,
Version=3.0.0.0,
Culture=neutral,
PublicKeyToken=31bf3856ad364e35
;component/themes/
luna.normalcolor.xaml" />

 

Luna, Metallic—The XP 银色风格的主题

<ResourceDictionary
Source="/
PresentationFramework.Luna,
Version=3.0.0.0,
Culture=neutral,
PublicKeyToken=31bf3856ad364e35
;component/themes/
luna.metallic.xaml" />


Luna, Homestead— XP 绿色主题
<ResourceDictionary
Source="/
PresentationFramework.Luna,
Version=3.0.0.0,
Culture=neutral,
PublicKeyToken=31bf3856ad364e35
;component/themes/
luna.homestead.xaml" />


Royale— Windows Media Center使用的主题
<ResourceDictionary
Source="/
PresentationFramework.Royale,
Version=3.0.0.0,
Culture=neutral,
PublicKeyToken=31bf3856ad364e35
;component/themes/
royale.normalcolor.xaml" />


Windows Classic—Windows 2000/2003风格的主题
<ResourceDictionary
Source="/
PresentationFramework.Classic,
Version=3.0.0.0,
Culture=neutral,
PublicKeyToken=31bf3856ad364e35
;component/themes/
Classic.xaml" />

       可能还会有很多主题可以下载到。你也可以创建自己的主题,在一个assembly中创建资源目录。但这里我们不再详细研究。

 

6.4.2 在代码中更改主题

       通过编程更改主题和在XAML中一样简单。比如,你可以允许你的用户为你的程序选择一个来自于系统或自定义主题集的主题。如果相关主题找不到,你也可以编程设置一个默认主题。不过这样做,代码会比较难以处理。

       在代码中设置主题,首先要从所在的assembly中加载资源目录并将其设为你的资源目录。列表6.12将窗体的主题修改为Aero主题。

Uri uriToTheme = new Uri("/PresentationFramework.Aero,
Version=3.0.0.0,Culture=neutral,
PublicKeyToken=31bf3856ad364e35
;component/themes/aero.normalcolor.xaml",UriKind.Relative);
object theme = Application.LoadComponent(uriToTheme);
this.Resources = (ResourceDictionary)theme;

Listing 6.12 Changing the theme to Aero

       在这段代码中,创建一个指向所需资源的URI,其中的内容和在XAML中使用主题是一样的。然后加载主题并用其替换掉窗体的资源。

       为了更加有趣,我们将form中的一个按钮和一个方法绑定。方法中在不同主题之间来回切换。点击按钮就可以实现主题不断切换的效果。

       尽管代码已经可用了,我们仍有一些问题需要解决。首先,我们替换的是窗体的资源而不是应用程序的资源。这个很好改:

Application.Current.Resources=(ResourceDictionary)theme;

       我们再次点击按钮,整个程序里的每一个控件都会发生变化。第二个问题有点麻烦:当你替换窗体或应用程序的资源时,把已经存在的资源全部清除掉了。如果整个资源就是存了一个主题的话,倒还没什么关系。此问题也有解决方案:我们可以使用合并目录的方法而不是替换掉整个资源目录。

       例如,我们有一个主题引用和一个本地资源目录的引用:

<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/PresentationFramework.Classic,
Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35;component/themes/Classic.xaml" />
<ResourceDictionary Source="MyLocalResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>

       这样我们可以更新资源集合而不是替换掉所有资源。

if(this.Resources.MergedDictionaries.Count > 0)
this.Resources.MergedDictionaries[0] = (ResourceDictionary)theme;

       这样我们只是替换掉主题引用,本地资源依然存在。你可能注意到了,我们依靠MergedDictionaries集合里的特定位置来定位主题的引用。不幸的是没有提供区分那个是主题资源目录,哪个是本地集合的方法。对于系统来说它们没有实质的区别。你可以尝试一些小技巧,比如在你自己的资源目录中存储一个特定的资源,此资源不会在你的系统资源中。你也可以检测ResourceDictionary的Source属性来找到系统引用,Source属性其中包含URI。

       值得称道的是主题没有什么特别之处--它们其实是被包含在各自assembly中的资源目录。其强大之处在于对于一个系统主题或你自己的主题,或者你自己的本地定义的资源,它们的操作方式是一样的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值