说明:本系列基本上是《WPF揭秘》的读书笔记。在结构安排与文章内容上参照《WPF揭秘》的编排,对内容进行了总结并加入一些个人理解。

皮肤

皮肤是应用程序中样式与模板的集合,可以被整体动态的替换,从而让应用看上去是一种全新的风格。通过皮肤允许第三方任意改变应用程序的外观,WPF并没有内建一个叫皮肤的机制。皮肤及换肤功能可以通过动态资源机制加Style和模板来实现。

下面介绍一个WPF较常见的皮肤定制机制。如果我们希望一个元素的外观可以通过皮肤来定制,我们要将此元素的样式定义为引用动态资源,并把默认样式的定义放在App.xaml的<Resources>中,样式的key为元素属性定义中DictionaryResource所引用的名称。第三方定制皮肤时,将定制的元素的样式放在根元素为<ResourceDictionary>的"皮肤"文件中。

最后要做的是动态加载皮肤所在的XAML文件,并用其内容替换当前的Application.Resources字典:

 ResourceDictionary resource = null;
using (FileStream fs = new FileStream("CustomSkin.xaml", FileMode.Open, FileAccess.Read))
{
//XAML的根元素必须是ResourceDictionary
resource = (ResourceDictionary)XamlReader.Load(fs);
}
Application.Current.Resources = resource;

下面是由Internet上获得皮肤XAML的例子,其中用到我们网络部分介绍过的WebClient类。

 ResourceDictionary resource = null;
System.Net.WebClient client = new WebClient();
using (Stream s = client.OpenRead("http://cnblogs.com/sample.xaml"))
{
//XAML的根元素必须是ResourceDictionary
resource = (ResourceDictionary)XamlReader.Load(s);
}
Application.Current.Resources = resource;

另外,如果需要可以恢复默认皮肤的功能,则应当保存当前Resouce。

提示:如果按上文方式应用了新皮肤,而对于某元素的一个属性,新皮肤中没有对应的定义,这时WPF会将此元素切换为默认样式,输出一个调试跟踪记录:

System.Windows.ResourceDictionary Warning: 9 : Resource not found;

ResourceKey = 'CancelButtonStyle'

提示:如果皮肤中(或皮肤的模板中)需要使用程序代码,就不能将其放在XAML中了,我们可以将其所在的ResouceDictionary编译到一个程序集中,并以相同的方式作为皮肤使用,这中间有一步关键操作是用Application.LoadComponent获得已编译的资源(可以位于相同或不同的程序集中),下面是代码示例:

 ResourceDictionary resource =(ResourceDictionary)Application.LoadComponent(new Uri("sample.xaml", UriKind.RelativeOrAbsolute));
Application.Current.Resources = resource;

提示:自定义皮肤(第三方皮肤)可能会带来不良的效果,如将界面某一元素通过样式隐藏了,将文本的颜色与背景色设为一致从而让文本难以读取等,这些只能由程序开发者设计方法来避免。这里的安全问题非一言一语可以给出方法,需在实际开发过程中谨慎对待。

 

主题

WPF中内建的控件都提供了多个独立的模板来对应不同的Windows主题,以使控件外观与Windows保持一致。

这一节我们的主要话题是怎样让用户自定义的模板适应用户所选的不同的Windows主题。有这种主要方法,前者简单但功能稍弱,后者灵活但相对复杂。

  1. 使用系统颜色,字体等参数

    当Windows系统的主题变化时,SystemColors,SystemFonts和SystemParameters类提供的成员会自动更新,在样式与模板中使用这些类是与系统主题保持一致的好方法。如:

     <Setter Property="Background"  Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>

    应用样式中含有这行代码的目标元素,背景色将随系统主题的变化而变。

  1. 为每种主题提供样式的模板

    这种方式中我们需要使用编程方式在主题改变时加载相应的主题。WPF没有内置监听主题更改的事件,需要使用Win32.WM_THEMECHANGE消息获得主题更改通知。下面我们详细介绍下这种方式的使用:

  • 第一步

    把对应不同主题的资源(样式或模板)放入不同的XAML文件中(资源字典),这些XAML命名规则为ThemeName.ThemeColor.xaml(非大小写敏感),我们把这些文件放在程序集根目录下的Themes文件夹中这样资源字典就被指派为主题字典,这样编译这个程序集就可以了。这样当应用程序启动或主题改变时,WPF会自动加载并应用主题字典(及其中的主题样式)。

    下面是Windows内置主题对应的WPF内置的主题字典的xaml文件,我们可以了解下它们的路径与命名:

    • Vista Aero主题:themes\Aero.NormalColor.xaml
    • Windows xp默认主题:themes\Luna.NormalColor.xaml
    • Windows Classic主题:themes\Classic.xaml

    另外,有一个最佳实践,添加一个通用字典,用于当前主题没有对应的主题字典的情况,这个字典的命名需要是themes\Generic.xaml。

  • 第二步

    有了主题字典和通用字典,我们需要使用程序集级的[ThemeInfo]特性启用主题机制,其构造函数接受两个类型为ResourceDictionaryLocation的参数,第一个参数告诉WPF主题字典的位置,第二个参数告诉WPF通用字典的位置。

ResourceDictionaryLocation类型参数接受如下几类值:

  • None:这是默认值,不查找资源字典
  • SourceAssembly:在当前程序集内寻找资源字典
  • ExternalAssembly:在其他程序集内查找,这些程序集的命名格式为AssemblyName.ThemeName.dll。WPF内部实现中也使用这种方式查找主题字典,如PresentationFramework.Aero.dll等。

介绍完了原理,我们给一个例子:假设我们把主题字典放在了当前程序集中。程序集级特性将类似如下这样:

 [assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly,ResourceDictionaryLocation.SourceAssembly)]

对于主题不得不说的一点是,其主要用于为控件(尤其是自定义的元素)提供默认样式。所以常常主题样式都是放在定义控件的程序集自身中或伙伴程序集中。在一个控件所在程序集的主题字典中不能为外部定义的元素定义类型化样式或重载它们的样式,下面的提示中有关于这个话题进一步的分析。

提示:一个使用外部元素主题样式的方法

我们来看这样一个场景,我们在一个程序中用到一个外部控件(可以是WPF内置控件或者某个自定义控件),为了得到引用的外部控件在不同主题下的主题字典,我们可能需要引用一些定义了这些主题字典的程序集。这时我们就需要借助ThemeDictionary这个标记扩展。

ThemeDictionaryExtension用于引用任何包含了主题字典的程序集(甚至包括当前程序集)。从而引用或重载任何元素的样式。实际使用中我们常把ThemeDictionary作为ResourceDictionary的一个源。将下面代码:

 <ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="{ThemeDictionary assemblyName}"/>
<ResourceDictionary .../>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
 

提示:给现有元素添加主题样式的一种方式

我们可以通过派生一个现有元素的子类来定义一个使用新主题样式的自定义控件。如我们由ProgressBar派生一个自定义控件,实现饼图外观,所需的代码很简单:

 static ProgressPie()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(ProgressPie),
new FrameworkPropertyMetadata(typeof(ProgressPie))
);
}

这样就可以引用为ProgressPie指定的类型化样式,实现饼状的ProgressBar。DefaultStyleKey是FrameworkElement和FrameworkContentFramework中定义的protected级的依赖属性,用于指定主题样式,这个样式定义同前文所述:

 <ResourceDictionary>
<Style TargetType="{x:Type local:ProgressPie}"></Style>
</ResourceDictionary>
 

本文完

参考:

《WPF揭秘》

WPF,Silverlight与XAML读书笔记第四十六 - 外观效果之三皮肤与主题的更多相关文章

  1. WPF,Silverlight与XAML读书笔记第四十五 - 外观效果之模板

    说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. 模板允许用任何东西完全替换一个元素的可视树, ...

  2. WPF,Silverlight与XAML读书笔记第三十九 - 可视化效果之3D图形

    原文:WPF,Silverlight与XAML读书笔记第三十九 - 可视化效果之3D图形 说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘> ...

  3. WPF,Silverlight与XAML读书笔记第四十八 - Silverlight网络与通讯

    说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. 这一部分我们重点讨论下Silverlight ...

  4. WPF,Silverlight与XAML读书笔记第四十四 - 外观效果之样式

    说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. 如果你有Web编程的经验,你会知道使用Sty ...

  5. WPF,Silverlight与XAML读书笔记第四十七 - Silverlight与浏览器

    说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. 这部分内容主要介绍Silverlight与浏 ...

  6. WPF,Silverlight与XAML读书笔记第四十三 - 多媒体支持之文本与文档

    说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. Glyphs对象(WPF,Silverlig ...

  7. WPF,Silverlight与XAML读书笔记(3) - 标记扩展

    hystar的.Net世界 博客园 首页 新闻 新随笔 联系 管理 订阅 随笔- 103  文章- 0  评论- 107  WPF,Silverlight与XAML读书笔记(3) - 标记扩展   说 ...

  8. Dynamic CRM 2013学习笔记(四十六)简单审批流的实现

    前面介绍过自定义审批流: Dynamic CRM 2013学习笔记(十九)自定义审批流1 - 效果演示 Dynamic CRM 2013学习笔记(二十一)自定义审批流2 - 配置按钮 Dynamic ...

  9. 【WPF学习】第四十六章 效果

    WPF提供了可应用于任何元素的可视化效果.效果的目标是提供一种简单的声明式方法,从而改进文本.图像.按钮以及其他控件的外观.不是编写自己的绘图代码,而是使用某个继承自Effect的类(位于System ...

随机推荐

  1. TCP/IP协议工作原理简述

    TCP/IP协议工作原理简述 // */ // ]]>   TCP/IP协议工作原理简述 Table of Contents 1 概要 2 应用层 3 传输层 4 网络层 5 链路层 1 概要 ...

  2. 使用phar上线你的代码包

    在我前一阵子写的一篇文章<新版 SegmentFault 重构之系统架构>中,很多人对其中提到的利用phar上线代码比较感兴趣,我就在这边跟大家分享下我目前的做法. 哪些项目适合phar打 ...

  3. WebView网页中使用到支付宝调不起来,提示ERR_UNKNOWN_URL_SCHEME

    转载自:http://blog.csdn.net/u014369799/article/details/51305788 在WebView中如果使用到支付宝,需要添加以下代码,否则操作系统会将支付宝的 ...

  4. 模拟n个人参加选举的过程,并输出选举结果:假设候选人有四人,分别用A,B,C,D表示,当选某候选人时,直接输入其编号(编号由计算机随机产生,若输入的不是A,B,C,D则视为无效票,选举结束后按得票数从高到底输出候选人编号和所得票数.

    模拟n个人参加选举的过程,并输出选举结果:假设候选人有四人,分别用A,B,C,D表示,当选某候选人时,直接输入其编号(编号由计算机随机产生,若输入的不是A,B,C,D则视为无效票,选举结束后按得票数从 ...

  5. 关于windows下QT以及QT creator的安装

    普及  之  windows下qt的安装及配置   qt介绍 : Qt,分为商业.开源两个版本,商业版需要花钱购买license,而开源版本则遵守GPL协议,提供了源码,用户需要自行编译,才能生产动态 ...

  6. map容器的使用

    1.map是STL容器中的一种,属于关联性容器.以key value的形式存储.key必须唯一.如果重复则插入失败.插入后按照key默认排序.必须要先声明命名空间:using namespace st ...

  7. HDU 1575

    Tr A Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  8. stm32软件模拟IIC读取PX4FLOW光流传感器数据

    这段时间在做全国光电设计大赛,用到了px4的px4flow光流传感器,用软件模拟iic读取数据不定期会导致px4flow死机,查了资料和光流的源码,发现这个光流用了stm32的硬件iic,所以对软件模 ...

  9. Centos 反向代理创建资料

    1. yum update 2. sh centos.sh 3. sh upgrade_nginx.sh nginx 1.7.0 4. cd /usr/local/nginx/conf/ upload ...

  10. 通过json数据进行传递调用

    最近在弄andriod的程序,需要调用web服务器上的数据,服务采用C#写的,并部署在iis服务器上.我们可以像.NET那样调用服务那,利用andriod库自带的HttpPost和HttpGet类来调 ...