背景:前段时间做项目的时候,遇到这样一个问题,当我们在从非mango sdk升级到mango sdk时,把系统的默认主题切换到“Light”,应用程序自定义的主题不生效,通过查阅大量的资料,最终找到了solution,由于这段时间需要用这方面的知识,所以将其整理出来。如果有不对的地方,请大家多多指教,谢谢!
wp7中系统提供给我们两种默认的系统主题:黑色和白色,但在某些情况下,我们需要实现当系统主题改变时,不会影响到我们的应用程序。所以,就需要使用系统自定义的主题。这里需要说明一点,在开发项目过程中,我们遇到这样的一个问题,在非mango中使用自定义的主题时(即修改Theme Resource中的key值到自己想要的),应用程序运行正常,而在mango中我们使用自定义主题时,应用程序没有生效,还是使用系统默认的主题。这个主要原因是,在非mango中使用Windows Phone v7.0SDK,自定义主题不会覆盖系统的主题,在mango中Windows Phone v7.1SDK微软将其认为是一个bug而修复,所以,在mango设备中我们使用key值来自定义主题行不通。
样式在资源中有两种声明方式,一种是带Key(键值)的一种是不带key(键值)的(是一种隐式转换,仅在mango中有生效)。带key的样式,不会自动应用的控件上,除非使用Style="{StaticResource PhoneTextTitle1Style}"引用该资源的键值。而不带key的样式,会自动应用到所有同类型的元素上。
以下以Windows Phone v7.0和Windows Phone v7.1为例简单说明自定义主题的使用:
一下主要以一个简单的例子做对比,首先看在Windows Phone v7.0中,在安装完sdk后,C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.1\Design目录下有两个文件System.Windows.xaml和ThemeResources.xaml文件,一个是定义wp使用的控件样式文件,一个是资源文件(颜色,字体,画笔,字号和字体粗细等等)。如果我们要使用自定义的主题,我们需要将ThemeResources.xaml中的内容全部copy到app.xaml <Application.Resources> </Application.Resources>中,或者新建一个customtheme文件夹将自定义的主题都放入customtheme文件夹下面的customtheme.xaml中。在这里推荐使用后者,这样看起来比较清楚。本文是以第二种为例,我是将ThemeResources.xaml中的内容全部copy到我新建的文件夹下面customtheme.xaml中,在app.xaml<Application.Resources> </Application.Resources>中加入下面的代码(这样应用程序才会将我们自定义的主题加载进去):
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionar Source="UseCustomTheme\ThemeResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
在ThemeResources.xaml中,我做如下修改:
<!--<Color x:Key="PhoneForegroundColor">#FFFFFFFF</Color>-->
<Color x:Key="PhoneForegroundColor">#FF84F4FF</Color>
而TextBlock的定义如下:
<Style x:Key="PhoneTextBlockBase" TargetType="TextBlock">
<Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilyNormal}"/>
<Setter Property="FontSize" Value="{StaticResource PhoneFontSizeNormal}"/>
<Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
<Setter Property="Margin" Value="{StaticResource PhoneHorizontalMargin}"/>
</Style>
在MainPage.xaml中加入代码:
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneBackgroundBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock Height="71" HorizontalAlignment="Left" Margin="128,159,0,0" Name="textBlock1" Text="自定义主题测试" VerticalAlignment="Top" Width="179" />
</Grid>
</Grid>
运行程序结果如下:
在测试程序中,我仅仅改变了程序的前景色。
同样的程序代码,我们升级sdk到mango,运行程序界面如下:
程序的并没有使用我们自定义的主题,原因上面已经提到,是因为微软把他当做一个bug在7.1的sdk中已经修复。那么,在7.1中我们如何实现自定义主题呢?
7.1 的silverlight for windows phone是基于Siverlight4的,因此加入了隐式样式,即声明样式的时候不指定key,只有一个targetType。所以,我们可以在ThemeResources.xaml对TextBlock作如下修改:
<Style TargetType="TextBlock" BasedOn="{StaticResource PhoneTextBlockBase}" >
<Setter Property="Foreground" Value="#FF84F4FF"/>
</Style>
那么,相应地,在应用该资源的时候也不需要使用Style="{StaticResource PhoneTextTitle1Style}引用资源的键值。
在MainPage.xaml中作如下修改:
<Grid x:Name="LayoutRoot" Background="{StaticResource CustomBackgroundBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" /> <!--Style="{StaticResource PhoneTextNormalStyle}"/>-->
<TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0"/> <!--Style="{StaticResource PhoneTextTitle1Style}" />-->
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock Height="71" HorizontalAlignment="Left" Margin="128,159,0,0" Name="textBlock1" Text="自定义主题测试" VerticalAlignment="Top" Width="179" />
</Grid>
</Grid>
运行结果如下图所示(分别是在系统的主题是dark和light运行效果):
但是在windows phone7.0sdk中运行时正常的。
结论:windows phone7.0sdk中支持自定义的主题覆盖默认主题,但在windows phone7.1sdk中MS将其认为是一个bug进行了修复。
解决办法1:
我们可以在应用程序初始化的时候动态的读取我们定义的ResourceDictionary,并替换系统内置资源。首先,注释掉App.xaml中下面的代码:<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionar Source="UseCustomTheme\ThemeResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
其次,在App.xaml.cs中加入如下代码:
var dictionaries = new ResourceDictionary();
string source = String.Format("/CustomTheme;component/UseCustomTheme/ThemeResources.xaml");
var themeStyles = new ResourceDictionary { Source = new Uri(source, UriKind.Relative) };
dictionaries.MergedDictionaries.Add(themeStyles);
ResourceDictionary appResources = App.Current.Resources;
foreach (DictionaryEntry entry in dictionaries.MergedDictionaries[0])
{
SolidColorBrush colorBrush = entry.Value as SolidColorBrush;
SolidColorBrush existingBrush = appResources[entry.Key] as SolidColorBrush;
if (existingBrush != null && colorBrush != null)
{
existingBrush.Color = colorBrush.Color;
}
}
切记:这段代码要加在InitializeComponent()初始化之前,负责不起效果。
解决办法2:
在App.xaml.cs中加入如下代码:
(App.Current.Resources["PhoneForegroundBrush"] as SolidColorBrush).Color = Color.FromArgb(255, 0, 92, 200);(这个颜色根据需要调整)
(App.Current.Resources["PhoneBackgroundBrush"] as SolidColorBrush).Color = Colors.Black;
解决办法3:
上面已经提到在7.1SDK中不能通过修改key值来修改应用程序主题,所以我们做如下修改:
首先,新add一个CustomTheme.xaml进入工程中,引入C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.1\Design下面的
System.Windows.xaml和ThemeResources.xaml两个文件。
其次,在CustomTheme.xaml中加入如下代码:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="System.Windows.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
注意:这里有个奇怪的现象CustomTheme.xaml不能放在新建的文件夹下,否则运行时抛出异常所以,需要将CustomTheme.xaml放在当前根目录下面。
同时在System.Windows.xaml中引入ThemeResources.xaml。由于App.xaml引用了CustomTheme.xaml,CustomTheme.xaml引用了 System.Windows.xaml,System.Windows.xaml引用了ThemeResources.xaml。因此,我们的应用中已经包含了Windows Phone的几乎所有资源和样式。
最后把System.Windows.xaml和ThemeResources.xaml中系统的内置资源和样式做修改,因为Windows Phone内置的资源和样式的名称都以“Phone”开头,所以将x:Key=”Phone替换为x:Key=”Custom,{StaticResource Phone替换为{StaticResource Custom,完成以上修改后别忘了修改需要引用资源的地方Style="{StaticResource CustomTextNormalStyle}"而非Style="{StaticResource PhoneTextNormalStyle}"。
运行程序结果如下: