说明:本系列基本上是《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. ShellExecuteA

    //第三个参数是指令,可以是一个可执行程序(后面不能加参数).有默认打开方式的文件.路径.网址.各种协议地址如迅雷ftp邮箱ed2k等 MessageBoxA

  2. oracle创建密码文件的语句

    orapwd file=$ORACLE_HOME/dbs/orapw$ORACLE_SID password=oracle entries=5;

  3. keepalived健康检查方式

    keepalived对后端realserver的健康检查方式主要有以下几种: TCP_CHECK:工作在第4层,keepalived向后端服务器发起一个tcp连接请求,如果后端服务器没有响应或超时,那 ...

  4. listbox 报错 Cannot have multiple items selected when the SelectionMode is Single.

    1.错误提示:Cannot have multiple items selected when the SelectionMode is Single. 刚刚在处理两个Listbox时,将其中一个li ...

  5. proc文件系统

    在shell终端里不带任何参数,直接运行mount命令可以显示正在挂载的文件系统.其中有这么一行 none on /proc type proc (rw) 这就是/proc文件系统.第一个域显示non ...

  6. Sql日期时间格式转换

    sql server2000中使用convert来取得datetime数据类型样式(全) 日期数据格式的处理,两个示例: CONVERT(varchar(16), 时间一, 20) 结果:2007-0 ...

  7. HttpWebRequest 请求数据

    string fullUrl = "http://vip.AAA.cn/PreviewInterfaceAction.action?code=vip0008&data_digest= ...

  8. webpack配置

    1.初始化webpack npm install webpack -g   (全局安装) npm init   (初始化packge.json) npm install webpack --save- ...

  9. Don't Block on Async Code【转】

    http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html This is a problem that is brough ...

  10. 对象序列化成Json字符串 及 反序列化成对象

    一. public static string JsonSerializer<T>(T t)        {            DataContractJsonSerializer ...