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

推荐订阅源

T
Tenable Blog
Last Week in AI
Last Week in AI
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
H
Help Net Security
F
Fortinet All Blogs
MyScale Blog
MyScale Blog
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 司徒正美
量子位
N
Netflix TechBlog - Medium
Apple Machine Learning Research
Apple Machine Learning Research
小众软件
小众软件
Recorded Future
Recorded Future
博客园 - 三生石上(FineUI控件)
Vercel News
Vercel News
aimingoo的专栏
aimingoo的专栏
I
InfoQ
Microsoft Security Blog
Microsoft Security Blog
Scott Helme
Scott Helme
The Last Watchdog
The Last Watchdog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
IT之家
IT之家
AI
AI
WordPress大学
WordPress大学
Security Archives - TechRepublic
Security Archives - TechRepublic
Google Online Security Blog
Google Online Security Blog
U
Unit 42
V2EX - 技术
V2EX - 技术
MongoDB | Blog
MongoDB | Blog
Schneier on Security
Schneier on Security
博客园 - Franky
H
Heimdal Security Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Jina AI
Jina AI
W
WeLiveSecurity
P
Privacy & Cybersecurity Law Blog
Cloudbric
Cloudbric
B
Blog RSS Feed
N
News | PayPal Newsroom
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
I
Intezer
Hacker News - Newest:
Hacker News - Newest: "LLM"
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园_首页
罗磊的独立博客
H
Hackread – Cybersecurity News, Data Breaches, AI and More
雷峰网
雷峰网

博客园 - 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章“样式、模板、皮肤和主题”,相信会对理解资源和高级换肤有很大帮助。