惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
S
Securelist
MyScale Blog
MyScale Blog
Jina AI
Jina AI
罗磊的独立博客
The Cloudflare Blog
美团技术团队
博客园 - 叶小钗
阮一峰的网络日志
阮一峰的网络日志
博客园 - 三生石上(FineUI控件)
月光博客
月光博客
雷峰网
雷峰网
小众软件
小众软件
aimingoo的专栏
aimingoo的专栏
大猫的无限游戏
大猫的无限游戏
博客园 - Franky
博客园 - 聂微东
Y
Y Combinator Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
MongoDB | Blog
MongoDB | Blog
T
Tailwind CSS Blog
Attack and Defense Labs
Attack and Defense Labs
博客园_首页
Latest news
Latest news
Apple Machine Learning Research
Apple Machine Learning Research
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
The Hacker News
The Hacker News
G
GRAHAM CLULEY
Simon Willison's Weblog
Simon Willison's Weblog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Proofpoint News Feed
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
U
Unit 42
D
Docker
Webroot Blog
Webroot Blog
N
Netflix TechBlog - Medium
T
Tor Project blog
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LINUX DO - 最新话题
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The Last Watchdog
The Last Watchdog
B
Blog
Recent Announcements
Recent Announcements
GbyAI
GbyAI
Microsoft Azure Blog
Microsoft Azure Blog
Security Latest
Security Latest
V2EX - 技术
V2EX - 技术
N
News | PayPal Newsroom
Microsoft Security Blog
Microsoft Security Blog

博客园 - kings

项目团队成长日志--聆听客户声音,沟通无所不在 TOAD使用筆記 使电脑鼠标右键相应快的办法 非常好用的对日面试资料(转) IT技術者日本語面接(ぎじゅつしゃにほんごめんせつ)によく出(で)る100質問(しつもん) 获取应用程序路径 再议 构造方法(转自Q.yuhen) 基元类型、值类型和引用类型(转自Q.yuhen) C# 2.0 - 泛型(Generics)(转自Q.yuhen) new 和 override 的区别(转自Q.yuhen) “多态”一个需要注意的问题(转自Q.yuhen) C# 方法参数 ref 详述(转自Q.yuhen) 浅析Family Show 2.0的子窗体实现(转tonyqus) webservice 遇到的小问题 泛型集合类型,赋予集合业务意义,增强集合的抽象使用(转-lizhe1985) WPF_Markup的几种写法 When I tab into a toolbar in WPF I can't tab out again? What can I do to change this tab behaviour? Windows Presentation Foundation(WPF)中的数据绑定(使用XmlDataProvider作控件绑定之二:使用外部URL的XML文件)(转-大可山) Windows Presentation Foundation(WPF)中的数据绑定(使用XmlDataProvider作控件绑定)(转-大可山)
浅析Family Show 2.0的动态换肤实现(转tonyqus)
kings · 2007-09-20 · via 博客园 - kings

作者:Tony Qu 

前两天Family Show 2.0终于发布了(有关Family Show的项目信息大家可以看这里)。出于兴趣,这两天对这个项目中用到的一些技术作了一些研究。首先吸引我的就是这个动态换肤功能,请看下面两张图:

这张是Black Skin的效果


这张是Silver Skin的效果

我们会发现整个窗口风格都变了,很酷吧。。。

我先来介绍一下ResourceDictionary。ResourceDictionary中文译做资源字典,顾名思义,就是一个用来存放资源的集合。ResourceDictionary元素中可以直接嵌入资源,如下所示:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml">

  
<SolidColorBrush x:Key="MainBackgroundBrush" Color="#FF202020"/>

  
<LinearGradientBrush x:Key="PanelGradientBrush" EndPoint="1,0.5" StartPoint="0,0.5">
    
<GradientStop Color="#FF555555" Offset="0"/>
    
<GradientStop Color="#FF1C1C1C" Offset="1"/>
  
</LinearGradientBrush>

</ResourceDictionary>

也可以切入另一个ResourceDictionary,如下所示:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml">
  
<ResourceDictionary.MergedDictionaries>
    
<ResourceDictionary Source="Resources\Style1.xaml"/>
    <ResourceDictionary Source="Resources\Style2.xaml"/>
  
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

这里的MergedDicionaries是关键,它会把嵌入的资源文件合并起来,就像在一个资源中一样,但要注意一点——嵌入的资源文件必须把ResourceDictionary作为根元素。接下来的一个重点就是动态资源引用。在WPF中资源引用分为动态和静态两种,所谓动态资源引用就是一旦资源内容改变就会影响相应的内容,例如Window的背景动态引用了一个SolidColorBrush资源,一旦这个SolidColorBrush改变,那么背景同时也会改变;而静态资源引用则是仅在资源第一次使用时加载,所以如果使用的静态资源引用,即使之后资源的内容变化了,也不会对应用了资源的元素或属性产生任何影响。由于Family Show要实现的是动态换肤,静态资源引用恐怕是无能为力了,所以得用动态资源,例如:

<Border x:Name="Header" Background="{DynamicResource BackgroundBrush}" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1,1,1,0" CornerRadius="5,5,0,0">
      
<TextBlock Text="Add a family member" TextWrapping="Wrap" Margin="15,5,10,5" Foreground="{DynamicResource HeaderFontColor}" FontSize="18" VerticalAlignment="Center" FontWeight="Bold" x:Name="HeaderTextBlock"/>
</Border>

这里的Border.Background动态引用了BackgroundBrush资源Border.BorderBrush则引用了BorderBrush资源,TextBlock.Foreground则引用了HeaderFontColor资源。那么这些资源分别保存在哪里呢?原来它们都保存在每个Skin的BrushResources.xaml中,如下:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml">

  
<!-- The Background Brush is used as the background for the Headers and Footers -->
  
<SolidColorBrush x:Key="BackgroundBrush" Color="#FF202020"/>

  
<!-- The Border Brush is used as the color for most borders -->
  
<SolidColorBrush x:Key="BorderBrush" Color="#FF747474"/>

  
<SolidColorBrush x:Key="HeaderFontColor" Color="#FFE6E6E6"/>

</ResourceDictionary>

所以,如果大家为找不到这些资源而发愁,这就是答案。

另外,之所以可以在Family Show中直接用{DynamicResource XXX}这样的形式直接引用资源,还有一个原因,那就是这些资源都被作为了应用程序级资源,让我们看看App.xaml中的代码:

<Application
    
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class
="Microsoft.FamilyShow.App"
    StartupUri
="MainWindow.xaml"
    
>
  
<Application.Resources>
    
<ResourceDictionary>
      
<ResourceDictionary.MergedDictionaries>
        
<!-- Use the Black skin by default -->
        
<ResourceDictionary Source="Skins\Black\BlackResources.xaml"/>
      
</ResourceDictionary.MergedDictionaries>
    
</ResourceDictionary>
  
</Application.Resources>
</Application>

我们会发现原来默认情况下会把Skins\Black\BlackResources.xaml作为应用程序级资源引入,这也是为什么我们默认看到的是Black Skin,而不是Silver Skin。这里也顺便提一下,WPF的资源都是有作用范围的,有应用程序级的资源,也有Window级的资源,还有控件级的资源,所以在使用一个资源之前一定要先确认是否在它的作用范围之内。

但这里只解决了默认的Skin加载,如何在程序运行时实现资源替换呢?答案就是下面这段代码:

        /// <summary>
        
/// Command handler for ChangeSkinCommand
        
/// </summary>

        private void ChangeSkin(object sender, ExecutedRoutedEventArgs e)
        
{
            ResourceDictionary rd 
= new ResourceDictionary();
            rd.MergedDictionaries.Add(Application.LoadComponent(
new Uri(e.Parameter as string, UriKind.Relative)) as ResourceDictionary);
            Application.Current.Resources 
= rd;
          
        }


ChangeSkin其实是一个事件处理程序,对应于ChangeSkin路由事件。(关于路由事件的知识由于超出了这篇文章的范畴,就不做展开了,我可能会在另一篇文章中单独讲解,这里你只要把它当作一个普通事件来理解就可以了。)这个事件处理程序是在点击Skin菜单中的菜单项时触发的。其实代码还是比较简单的——首先创建一个ResourceDictionary,然后把新的资源字典加载进来放入MergedDictionary中,最后把这个资源字典作为当前应用程序的资源。

到此,我想你对Family Show 2.0动态换肤的实现已经理解得差不多了。当然这只是WPF中换肤的基础而已,其实WPF换肤不仅仅可以换某个控件的颜色,还可以换布局、样式等,这可以让你的应用程序产生夺人眼球的效果,但由于布局和样式在WPF中是两个很大的主题,在本文中无法一一道来,建议大家可以去看看WPF Unleashed的第8章“资源”和第10章“样式、模板、皮肤和主题”,相信会对理解资源和高级换肤有很大帮助。