在我们的程序中有时候需要去实现动态更换皮肤的效果,从而完成一些个性化的设置,那么我们究竟怎样去实现动态换皮肤的效果呢?那么我们经常用到的就是设置不同的Style,并且在主程序的xaml文件中通过DynamicResource引用这些Style,当我们点击更换皮肤时我们就动态去改变这些Style,从而实现动态去更换皮肤的操作。我们知道当我们使用Style="{DynamicResource styleBanner}"这种类型的代码时,我们的编译器首先会到App.xaml下面去寻找当前x:key为styleBanner的Style,如果能够找到,那么我们就可以将当前的Style设置为查找到的值,如果找不到会去MainWindow中查找相关的资源,并将查找到的资源赋值给该Style,如果还是找不到的话,会沿着MainWindow的子元素一级级去查找相关资源,直到最后找到相关的类型资源,如果找不到那么Style值为null;

在我们的程序中我们通过右键菜单来完成背景的切换,首先看一下我们的ContextMenu的代码:

 <Grid.ContextMenu>
<ContextMenu MenuItem.Click="OnMenuItemClick">
<MenuItem Tag=".\Resources\Skins\BlackSkin.xaml" IsChecked="True">
<MenuItem.Header>
<Rectangle Width="120" Height="40" Fill="Black" />
</MenuItem.Header>
</MenuItem>
<MenuItem Tag=".\Resources\Skins\GreenSkin.xaml">
<MenuItem.Header>
<Rectangle Width="120" Height="40" Fill="Green" />
</MenuItem.Header>
</MenuItem>
<MenuItem Tag=".\Resources\Skins\BlueSkin.xaml">
<MenuItem.Header>
<Rectangle Width="120" Height="40" Fill="Blue" />
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</Grid.ContextMenu>

  我们把每一个资源文件的相对路径作为一个Tag附加到每一个MenuItem上,这样当我们点击其中的某一个MenuItem的时候,我们就能够查找到相关的资源,从而加载该资源,另外这里有一个小技巧,当我们需要为每一个MenuItem执行相同的Click事件的时候,我们不必进行重复去写,我们可以在MenuItem的上一级ContextMenu 中添加附加属性MenuItem.Click="OnMenuItemClick",这样就可以不用重复去定义Click事件了。下面就是关键的OnMenuItemClick事件的代码了。

void OnMenuItemClick(object sender, RoutedEventArgs e)
{
MenuItem item = e.OriginalSource as MenuItem; // Update the checked state of the menu items.
Grid mainGrid = this.Content as Grid;
foreach (MenuItem mi in mainGrid.ContextMenu.Items)
mi.IsChecked = mi == item; // Load the selected skin.
this.ApplySkinFromMenuItem(item);
}

  这里通过e.OriginalSource来查找触发的事件源,这里每选择其中的一项时会将其他的项的IsChecked属性设置为false,这段代码里调用了一个核心的函数叫做ApplySkinFromMenuItem,那么我们来看看具体是怎么实现的。

void ApplySkinFromMenuItem(MenuItem item)
{
// Get a relative path to the ResourceDictionary which
// contains the selected skin.
string skinDictPath = item.Tag as string;
Uri skinDictUri = new Uri(skinDictPath, UriKind.Relative); // Tell the Application to load the skin resources.
DemoApp app = Application.Current as DemoApp;
app.ApplySkin(skinDictUri);
}

  我们把每个Tag中定义的不同皮肤的Xaml文件的路径传入DemoApp下面的ApplySkin函数中,记住使用Application.Current能够获取当前应用程序中唯一的APP,我们来看看我们的DemoAPP中定义的这个方法。

namespace SkinnableApp
{
public partial class DemoApp : Application
{
public void ApplySkin(Uri skinDictionaryUri)
{
// Load the ResourceDictionary into memory.
ResourceDictionary skinDict = Application.LoadComponent(skinDictionaryUri) as ResourceDictionary; Collection<ResourceDictionary> mergedDicts = base.Resources.MergedDictionaries; // Remove the existing skin dictionary, if one exists.
// NOTE: In a real application, this logic might need
// to be more complex, because there might be dictionaries
// which should not be removed.
if (mergedDicts.Count > 0)
mergedDicts.Clear(); // Apply the selected skin so that all elements in the
// application will honor the new look and feel.
mergedDicts.Add(skinDict);
}
}
}

  ResourceDictionary skinDict = Application.LoadComponent(skinDictionaryUri) as ResourceDictionary这行代码是用于加载位于指定统一资源标识符 (URI) 处的 XAML 文件,并将其转换为由该 XAML 文件的根元素指定的对象的实例,也就是将每一个MenuItem的Tag中标识的路径下的XAML文件的根元素指定的对象的实例获取到。我们获取到了该ResourceDictionary 接下来我们就需要将该ResourceDictionary 加载到Appliciton的MergedDictionaries(获取 System.Windows.ResourceDictionary 字典的集合,这些字典构成了合并的字典中的各种资源字典。)下面,通过Collection<ResourceDictionary> mergedDicts = base.Resources.MergedDictionaries;我们能够获取到当前Application级别下所有的资源字典,这里需要注意的就是在我们将新的ResourceDictionary 添加到mergedDicts 下面之前,我们需要先清除掉原来里面的内容,这是因为我们的

每一个xaml文件中使用的key都是相同的,为了避免混淆,所以首先清除掉原来的内容,其实每一次加载的时候即使有相同的key值编译器也只会默认加载最后面的一个,但是我们的操作会不断重复操作,所以mergedDicts 里面的内容会越加越多,所以这在程序中是一种不合理的方式。

在清除完毕之后,将我们的新的xaml文件作为ResourceDictionary 添加到里面去。这样我们的MainWindow.xaml就会查找到这些相关的内容。下面贴出相关的代码以及换肤后的图片内容。

MainWindow.xaml文件:

<Window
x:Class="SkinnableApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:local="clr-namespace:SkinnableApp"
SizeToContent="Height"
ResizeMode="NoResize"
Title="Skinnable App"
Width="680"
WindowStartupLocation="CenterScreen"
WindowStyle="ToolWindow">
<Grid x:Name="Root" Style="{DynamicResource styleBackground}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2.5*" />
</Grid.ColumnDefinitions>
<!-- CONTEXT MENU -->
<Grid.ContextMenu>
<ContextMenu MenuItem.Click="OnMenuItemClick">
<MenuItem Tag=".\Resources\Skins\BlackSkin.xaml" IsChecked="True">
<MenuItem.Header>
<Rectangle Width="120" Height="40" Fill="Black" />
</MenuItem.Header>
</MenuItem>
<MenuItem Tag=".\Resources\Skins\GreenSkin.xaml">
<MenuItem.Header>
<Rectangle Width="120" Height="40" Fill="Green" />
</MenuItem.Header>
</MenuItem>
<MenuItem Tag=".\Resources\Skins\BlueSkin.xaml">
<MenuItem.Header>
<Rectangle Width="120" Height="40" Fill="Blue" />
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</Grid.ContextMenu> <!-- AGENTS DATA SOURCE -->
<Grid.DataContext>
<CollectionViewSource>
<CollectionViewSource.Source>
<XmlDataProvider Source=".\Resources\Data\agents.xml" XPath="Agents/Agent" />
</CollectionViewSource.Source>
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Name/Last" />
<scm:SortDescription PropertyName="Name/First" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</Grid.DataContext> <!-- BANNER -->
<Grid Grid.ColumnSpan="2" Height="70" Style="{DynamicResource styleBanner}" >
<TextBlock FontSize="26" Padding="10,0,10,0" Text="Insurance Agent Management System" VerticalAlignment="Center"/>
</Grid> <!-- AGENT SELECTOR -->
<local:AgentSelectorControl Grid.Row="1" Grid.Column="0"/>
<!-- AGENT DETAILS -->
<local:AgentDetailsControl Grid.Row="1" Grid.Column="1"/>
<StackPanel Height="40" Orientation="Horizontal" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Content="移动到第一项" Click="MoveToFirstItem"></Button>
<Button Content="移动到上一项" Click="MoveToForwardItem" Margin="10,0,10,0"></Button>
<Button Content="移动到下一项" Click="MoveToNextItem" Margin="10,0,10,0"></Button>
<Button Content="移动到最后一项" Click="MoveToLastItem" Margin="10,0,10,0"></Button>
</StackPanel>
</Grid>
</Window>

  这里列举其中的一种皮肤的样式:BlueSkin.xaml

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source=".\BaseSkin.xaml" />
</ResourceDictionary.MergedDictionaries> <!-- Background Style -->
<Style x:Key="styleBackground">
<Setter Property="Control.Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5" Opacity="0.5">
<GradientStop Color="LightSkyBlue" Offset="0" />
<GradientStop Color="WhiteSmoke" Offset="0.5" />
<GradientStop Color="LightSkyBlue" Offset="1" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style> <!-- Banner Style -->
<Style x:Key="styleBanner">
<Setter Property="StackPanel.Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0.25" EndPoint="1,0.5">
<GradientStop Color="#CC0088DD" Offset="0.3" />
<GradientStop Color="#3300FFFF" Offset="0.85" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="TextBlock.Foreground" Value="Yellow" />
<Setter Property="TextBlock.FontFamily" Value="Comic Sans MS" />
</Style> <!-- Content Area Style -->
<Style x:Key="styleContentArea" BasedOn="{StaticResource styleContentArea_Base}">
<Setter Property="Border.Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#FFFFFFFF" Offset="0.5" />
<GradientStop Color="#CCFFFFDD" Offset="1" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Border.BorderBrush" Value="Gray" />
<Setter Property="TextBlock.FontFamily" Value="Comic Sans MS" />
</Style> <!-- Content Area Header Brush -->
<LinearGradientBrush x:Key="contentAreaHeaderBrush" StartPoint="0.5,0" EndPoint="1,0.5">
<GradientStop Color="#CC0088DD" Offset="0.3" />
<GradientStop Color="#CC00AADD" Offset="0.75" />
</LinearGradientBrush> <!-- Content Area Header Style -->
<Style x:Key="styleContentAreaHeader" BasedOn="{StaticResource styleContentAreaHeader_Base}">
<Setter Property="Border.Background" Value="{StaticResource contentAreaHeaderBrush}" />
<Setter Property="TextBlock.Foreground" Value="Yellow" />
<Setter Property="FrameworkElement.BitmapEffect">
<Setter.Value>
<DropShadowBitmapEffect ShadowDepth="12" />
</Setter.Value>
</Setter>
<Setter Property="FrameworkElement.LayoutTransform">
<Setter.Value>
<RotateTransform Angle="-2" />
</Setter.Value>
</Setter>
</Style> <!-- Agent Image Border Style -->
<Style x:Key="styleAgentImageBorder" TargetType="Border" BasedOn="{StaticResource styleAgentImageBorder_Base}">
<Setter Property="BitmapEffect">
<Setter.Value>
<DropShadowBitmapEffect />
</Setter.Value>
</Setter>
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="BorderThickness" Value="1" />
</Style> </ResourceDictionary>

  下面贴出几更换皮肤的图片。

  

WPF通过DynamicResource实现给界面动态更换皮肤的更多相关文章

  1. [ExtJS5学习笔记]第二十九节 sencha ext js 5.1.0中动态更换皮肤主题

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/42016107 本文作者:sushengmiyan ------------------ ...

  2. WPF下的视频录制界面设计

    原文:WPF下的视频录制界面设计 在去年12月份,我曾经写过三篇文章讨论C#下视频录制.播放界面的设计.这三篇文章是:利用C#画视频录制及播放的界面(一) 利用C#画视频录制及播放的界面(二)利用C# ...

  3. WPF中静态引用资源与动态引用资源的区别

    WPF中静态引用资源与动态引用资源的区别   WPF中引用资源分为静态引用与动态引用,两者的区别在哪里呢?我们通过一个小的例子来理解. 点击“Update”按钮,第2个按钮的文字会变成“更上一层楼”, ...

  4. 【转】【iOS】动态更换App图标

    原文网址:http://www.cocoachina.com/ios/20170619/19557.html 前言 动态更换App图标这件事,在用户里总是存在需求的:有些用户喜欢“美化”自己的手机.至 ...

  5. WPF 用代码调用dynamic resource动态更改背景 - CSDN博客

    原文:WPF 用代码调用dynamic resource动态更改背景 - CSDN博客 一般dynamic resoource通常在XAML里调用,如下范例: <Button Click=&qu ...

  6. 空闲时间研究一个小功能:winform桌面程序如何实现动态更换桌面图标

    今天休息在家,由于天气热再加上疫情原因,就在家里呆着,空闲时想着,在很早以前(约3年前),产品人员跟我提了一个需求,那就是winform桌面程序的图标能否根据节日动态更换,这种需求在移动APP上还是比 ...

  7. 动态更换应用Icon

    转:原理1--activity-alias 在AndroidMainifest中,有两个属性: // 决定应用程序最先启动的Activity android.intent.action.MAIN // ...

  8. WPF如何实现类似iPhone界面切换的效果(转载)

    WPF如何实现类似iPhone界面切换的效果 (version .1) 转自:http://blog.csdn.net/fallincloud/article/details/6968764 在论坛上 ...

  9. 界面动态加载时报NullPointException

    今天在做环境监测的模拟软件时,登陆页面报NullPointException 一般像我们初始化Button时,是Button btn=(Button)findViewById(R.id.button1 ...

随机推荐

  1. 【转】【fiddler】抓取https数据失败,全部显示“Tunnel to......443”

    这个问题是昨天下午就一直存在的,知道今天上午才解决,很感谢“韬光养晦”. 问题描述: 按照网络上的教程,设置fiddler开启解密https的选项,同时fiddler的证书也是安装到系统中,但是抓取h ...

  2. Python:Day21

    序列化 我们把对象(变量)从内存中变成可存储或可传输的过程称之为序列化 json模块

  3. 微信硬件平台(七) 设备控制控制面板-网页sokect-mqtt长连接

    给微信硬件设备添加我们自己的控制面板. 主要问题: 1 要保证长连接,这样面板可以实时交互阴间设备,http一次性的连接模式通信不行. 面板必须是网页化的,网页就可以操作交互.不用APP和小程序. 2 ...

  4. Spring Security(七):2.4 Getting Spring Security

    You can get hold of Spring Security in several ways. You can download a packaged distribution from t ...

  5. 【转】深入理解Java:注解(Annotation)--注解处理器

    https://www.cnblogs.com/peida/archive/2013/04/26/3038503.html 如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了.使用注解 ...

  6. hdu2121 Ice_cream's world II

    hdu2121 Ice_cream's world II 给一个有向图,求最小树形图,并输出根节点 \(n\leq10^3,\ m\leq10^4\) 最小树形图 对于求无根最小树形图,可以建一个虚拟 ...

  7. 2018-2019-2 20175310 实验二《Java面向对象程序设计》实验报告

    2018-2019-2 20175310 实验二<Java面向对象程序设计>实验报告 一.实验步骤及内容 (一).面向对象程序设计-1 参考 http://www.cnblogs.com/ ...

  8. face recognition[MobiFace]

    本文来自<MobiFace: A Lightweight Deep Learning Face Recognition on Mobile Devices>,时间线为2018年11月.是作 ...

  9. LDAP1-安装部署LDAP服务

    基于Linux部署openldap服务 参考文档: https://blog.csdn.net/computer1024/article/details/78172785 参考文档:  https:/ ...

  10. ASP.NET Core MVC四种枚举绑定方式

    前言 本节我们来讲讲在ASP.NET Core MVC又为我们提供了哪些方便,之前我们探讨过在ASP.NET MVC中下拉框绑定方式,这节我们来再来重点看看枚举绑定的方式,充分实现你所能想到的场景,满 ...