说明:本系列基本上是《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详解系列 --- 概念总结01

    UDP协议  .vs.  TCP协议:  原理上:(TCP报文段. vs . UDP用户数据报) TCP协议的特性: TCP是面向连接的运输层协议,应用程序在使用TCP协议之前,必须先建立TCP连接. ...

  2. EAA脚本语言0.2

    对上一版的改进.IL生成问题大部分解决了. 运行效果 脚本源码 #using "System"; #using "System.Drawing"; #using ...

  3. LeetCode(124) Binary Tree Maximum Path Sum

    题目 Given a binary tree, find the maximum path sum. For this problem, a path is defined as any sequen ...

  4. ORACLE 查询一个数据表后通过遍历再插入另一个表中的两种写法

    ORACLE 查询一个数据表后通过遍历再插入另一个表中的两种写法 语法 第一种: 通过使用Oracle语句块  --指定文档所有部门都能查看 declare cursor TABLE_DEPT and ...

  5. Linux 如何安装memcache?

    原有memcache所在机器损坏,需要在新的Linux机器上安装memcache,具体步骤如下: 1.使用Linux机器的root权限登陆 2.安装libevent 下载地址:http://libev ...

  6. JavaScript-BOM-history:保存当前窗口打开后成功访问过的url历史记录栈

    history:保存当前窗口打开后成功访问过的url历史记录栈history.go(n):前进n步前进一步:history.go(1);后退一步:history.go(-1);刷新:history.g ...

  7. flask_关注者,联系人和好友

    在这节我们实现的功能比较复杂,就是实现用户"关注"和"取消关注"的功能. 一个用户可以关注多个其他的用户,一个用户也可以被其他多个用户所关注,这样看的话,在数据 ...

  8. 工具fiddler学习

    1:Fiddler 是以代理web服务器的形式工作的,它使用代理地址:127.0.0.1, 端口:8888. 当Fiddler会自动设置代理.能支持HTTP代理的任意程序的数据包都能被Fiddler嗅 ...

  9. 代码规范、GitHub提交源码的标准 答题人-杨宇杰

    1.格式与命名规范1.1 缩进 使用Tab缩进,而不是空格键1.2 换行 每行120字符 if,for,while语句只有单句时,如果该句可能引起阅读混淆,需要用" {"和&quo ...

  10. sqlserver游标的使用方式

    ----臨時表 把數據抄寫到此表,然後做2個表的同步 SELECT [FA_NUMBER] ,[STATUS] ,[FA_REQUESTOR] ,[CALI_NUMBER] ,[AMT] FROM [ ...