说明:本系列基本上是《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. SQL存储过程删除数据库日志文件的方法

    --日志文件收缩至多少M DECLARE @DBLogSise AS INT SET @DBLogSise=0 --查询出数据库对应的日志文件名称 DECLARE @strDBName AS NVAR ...

  2. My安卓知识2--使用listview绑定sqlite中的数据

    我想在我的安卓项目中实现一个这样的功能,读取sqlite数据库中的数据并显示到某个页面的listview控件中. 首先,我建立了一个Service类,来实现对数据库的各种操作,然后在这个类中添加对数据 ...

  3. GPU高性能计算-CUDA

    前段时间有个同学的毕设是搞并行计算的,他基本不懂编程把我拉过去帮忙,我之前也没弄过,帮着搞了2天.先把代码贴上去,等有时间在把详细补充一些内容. CUDA编程主要是利用了显卡优越的并行计算能力,把一个 ...

  4. Oracle行转列(使用pivot函数)

    在日常使用中,经常遇到这样的情况,需要将数据库中行转化成列显示,如 转化为 这个时候,我们就需要使用pivot函数 百度后,参考网址http://www.2cto.com/database/20150 ...

  5. Python执行命令行

    背景 我们知道,虽然会破坏平台独立性,但是有的时候需要在代码里面调用命令行来获取一些信息,那么了解在 Python 中如何执行命令行至关重要 使用介绍 Python 中使用命令行可以通过 subpro ...

  6. C++链接两个cpp 文件

    我们在编程中,有没有想过,分别写代码,然后把两个cpp,文件合并,两个自身本不能运行的文件,在一起却可以运行(主要牵扯函数调用,一个有声明和调用,另一个定义).那么具体如何实现呢? 跟着我的步骤: 1 ...

  7. 一个sendMessage

    Message 1.判断是否同意协议.2.验证验证码是否正确.3.验证手机是否符合规则,符合规则就用message()发送短信,验证码的有效期以及使用的短信模板,在配置文件中进行管理.返回值下标为st ...

  8. eclipse中java项目的build path详解

    BuildPath中只支持加入jar文件,具体方法如下:在eclips里在工程名上右键->build path->contigure bud path->java build pat ...

  9. MS SQL查看效率语句 与PLSQL中F5功能相同

    使用方法:打开SQL SERVER 查询分析器,输入以下语句: SET STATISTICS PROFILE ON SET STATISTICS IO ON SET STATISTICS TIME O ...

  10. node3