阅读文章

Fun with Silverlight系列之三 -- Skinnable动态换肤效果

[日期:2008-04-03] 来源:  作者: [字体: ]

     前段时间国外的大牛为Silverlight2定义了四套皮肤,效果让人称奇,美中不足的是四个皮肤不能动态切换,
  所以我借花献佛将这四套皮肤放在一起实现了动态切换也就是换肤的效果。
  开发平台:VS2008 + Silverlight2
  
  效果图:
  选择Bubbly皮肤
  
  选择Flat皮肤
  
  选择Red皮肤
  
  选择Rough皮肤
  
  实现技术主要是利用两个小技巧,
  首先是ResourceDictionary,ResourceDictionary中文译做资源字典,顾名思义,就是一个用来存放资源的集合。
  ResourceDictionary元素中可以直接嵌入资源,在资源文件里定义ControlTemplate,因为所有的皮肤其实呈现的都是同样的控件,
  所以将所有的控件写在里面便于实现元素的重用,然后所有控件的Style属性都和相应的Style动态绑定。
  
  资源文件
   1<ResourceDictionary
   2 XMLns="http://schemas.microsoft.com/client/2007"
   3 XMLns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4 XMLns:controls="clr-namespace:Skinnable;assembly=Skinnable"
   5 xmlns:d="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data">
   6
   7 <Style TargetType="controls:BasicSkin">
   8 <Setter Property="Width" Value="1000" />
   9 <Setter Property="Height" Value="800" />
   10 <Setter Property="Foreground">
   11 <Setter.Value>
   12 <SolidColorBrush Color="#FFAF17" />
   13 </Setter.Value>
   14 </Setter>
   15 <Setter Property="ButtonStyle" Value="{StaticResource BubblyButtonStyle}" />
   16 <Setter Property="ToggleStyle" Value="{StaticResource BubblyToggleStyle}" />
   17 <Setter Property="ToolTipStyle" Value="{StaticResource BubblyToolTipStyle}" />
   18 <Setter Property="CheckBoxStyle" Value="{StaticResource BubblyCheckBoxStyle}" />
   19 <Setter Property="RadioStyle" Value="{StaticResource BubblyRadioStyle}" />
   20 <Setter Property="TextBoxStyle" Value="{StaticResource BubblyTextBoxStyle}" />
   21 <Setter Property="CalendarStyle" Value="{StaticResource BubblyCalendarStyle}" />
   22 <Setter Property="DayButtonStyle" Value="{StaticResource BubblyDayButtonStyle}" />
   23 <Setter Property="MonthButtonStyle" Value="{StaticResource BubblyMonthButtonStyle}" />
   24 <Setter Property="DatePickerStyle" Value="{StaticResource BubblyDatePickerStyle}" />
   25 <Setter Property="LinkStyle" Value="{StaticResource BubblyLinkStyle}" />
   26 <Setter Property="DataGridStyle" Value="{StaticResource BubblyDataGridStyle}" />
   27 <Setter Property="ColumnHeaderStyle" Value="{StaticResource BubblyColumnHeaderStyle}" />
   28 <Setter Property="RowHeaderStyle" Value="{StaticResource BubblyRowHeaderStyle}" />
   29 <Setter Property="RowStyle" Value="{StaticResource BubblyRowStyle}" />
   30
   31 <Setter Property="Template">
   32 <Setter.Value>
   33 <ControlTemplate TargetType="controls:BasicSkin">
   34 <Grid>
   35 <Grid Background="#FFE0E4C1">
   36 <Grid.ColumnDefinitions>
   37 <ColumnDefinition Width="5*"/>
   38 <ColumnDefinition Width="20*"/>
   39 <ColumnDefinition Width="5*"/>
   40 <ColumnDefinition Width="30*"/>
   41 <ColumnDefinition Width="30*"/>
   42 <ColumnDefinition Width="5*"/>
   43 <ColumnDefinition Width="5*"/>
   44 </Grid.ColumnDefinitions>
   45 <Grid.RowDefinitions>
   46 <RowDefinition Height="5*"/>
   47 <RowDefinition Height="10*"/>
   48 <RowDefinition Height="10*"/>
   49 <RowDefinition Height="14*"/>
   50 <RowDefinition Height="14*"/>
   51 <RowDefinition Height="10*"/>
   52 <RowDefinition Height="10*"/>
   53 <RowDefinition Height="22*"/>
   54 <RowDefinition Height="5*"/>
   55 </Grid.RowDefinitions>
   56
   57 <Button Grid.Column="1" Grid.Row="1" Margin="2" Height="27" Width="79" Content="Button" Style="{TemplateBinding ButtonStyle}">
   58 <Button.ToolTip>
   59 <ToolTip Content="Button tooltip" Style="{TemplateBinding ToolTipStyle}"/>
   60 </Button.ToolTip>
   61 </Button>
   62
   63 <ToggleButton Grid.Column="1" Grid.Row="2" Margin="5" Height="26" Width="65" Content="toggle" Style="{TemplateBinding ToggleStyle}">
   64 <ToggleButton.ToolTip>
   65 <ToolTip Content="toggle tooltip" Style="{TemplateBinding ToolTipStyle}"/>
   66 </ToggleButton.ToolTip>
   67 </ToggleButton>
   68
   69 <Grid Grid.Column="1" Grid.Row="3">
   70 <Grid.RowDefinitions>
   71 <RowDefinition Height="10*"/>
   72 <RowDefinition Height="20*"/>
   73 <RowDefinition Height="20*"/>
   74 <RowDefinition Height="20*"/>
   75 <RowDefinition Height="20*"/>
   76 <RowDefinition Height="10*"/>
   77 </Grid.RowDefinitions>
   78
   79 <CheckBox Content="Checkbox One" Grid.Row="1" IsThreeState="True" Style="{TemplateBinding CheckBoxStyle}"/>
   80 <CheckBox Content="Checkbox Two" Grid.Row="2" IsThreeState="True" Style="{TemplateBinding CheckBoxStyle}"/>
   81 <CheckBox Content="Checkbox Three" Grid.Row="3" IsThreeState="True" Style="{TemplateBinding CheckBoxStyle}"/>
   82 </Grid>
   83
   84 <Grid Grid.Column="1" Grid.Row="4">
   85 <Grid.RowDefinitions>
   86 <RowDefinition Height="10*"/>
   87 <RowDefinition Height="20*"/>
   88 <RowDefinition Height="20*"/>
   89 <RowDefinition Height="20*"/>
   90 <RowDefinition Height="20*"/>
   91 <RowDefinition Height="10*"/>
   92 </Grid.RowDefinitions>
   93
   94 <RadioButton Content="Radio One" Grid.Row="1" Style="{TemplateBinding RadioStyle}" GroupName="a"/>
   95 <RadioButton Content="Radio Two" Grid.Row="2" Style="{TemplateBinding RadioStyle}" GroupName="a"/>
   96 </Grid>
   97
   98 <TextBox Grid.Column="1" Grid.Row="5" Width="150" Style="{TemplateBinding TextBoxStyle}"/>
   99
  100 <Calendar Grid.Column="3" Grid.Row="1" Grid.RowSpan="3" Style="{TemplateBinding CalendarStyle}" DayStyle="{TemplateBinding DayButtonStyle}"
  101 MonthStyle="{TemplateBinding MonthButtonStyle}" />
  102
  103 <DatePicker Grid.Column="3" Grid.Row="4" Grid.RowSpan="3" Margin="10" Style="{TemplateBinding DatePickerStyle}" CalendarStyle="{TemplateBinding CalendarStyle}"/>
  104
  105 <Grid Grid.Column="1" Grid.Row="6">
  106 <Grid.ColumnDefinitions>
  107 <ColumnDefinition Width="Auto"/>
  108 <ColumnDefinition Width="Auto"/>
  109 <ColumnDefinition Width="*"/>
  110 </Grid.ColumnDefinitions>
  111 <TextBlock Grid.Column="0" Text="This is a " Foreground="#FF1E2B33" FontFamily="Trebuchet MS" FontSize="10.5"/>
  112 <HyperlinkButton Grid.Column="1" Content="hyperlink" FontFamily="Trebuchet MS" Style="{TemplateBinding LinkStyle}"/>
  113 </Grid>
  114
  115 <Grid Grid.Column="4" Grid.Row="1" Grid.RowSpan="6" Grid.ColumnSpan="3" Margin="30">
  116 <d:DataGrid x:Name="dataGridInstance"
  117 Style="{TemplateBinding DataGridStyle}"
  118 ColumnHeaderStyle="{TemplateBinding ColumnHeaderStyle}"
  119 RowHeaderStyle="{TemplateBinding RowHeaderStyle}"
  120 RowStyle="{TemplateBinding RowStyle}"
  121 AutoGenerateColumns="True"
  122 GridlinesVisibility="Horizontal"
  123 HeadersVisibility="All"
  124 ColumnHeadersHeight="30">
  125 <d:DataGrid.ItemsSource>
  126 <controls:CustomerList />
  127 </d:DataGrid.ItemsSource>
  128 </d:DataGrid>
  129 </Grid>
  130
  131 </Grid>
  132 </Grid>
  133 </ControlTemplate>
  134 </Setter.Value>
  135 </Setter>
  136 </Style>
  137
  138</ResourceDictionary>
  139
  保存这个资源文件为generic.xaml并设置为嵌入式资源。通过删除Custom Tool属性值并设置Build Action为Resource。
  这样就可以动态的定义样式和资源文件里的控件模板绑定了。
  
  下面说一说动态定义样式的小技巧,那就是依赖属性(DependencyProperty),它其实是WPF的核心技术点之一,和DependencyObject配合,
  提供了WPF中基本的数据存储、访问和通知的机制,具体可参见MSDN上的解说DependencyProperty ,
  这篇博客也说的不错WPF中如何实现数据与表示分离。
  
  
  
  属性绑定
   1 public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register("Foreground", typeof(Brush), typeof(BasicSkin), null);
   2 public static readonly DependencyProperty ButtonStyleProperty = DependencyProperty.Register("ButtonStyle", typeof(Style), typeof(BasicSkin), null);
   3 public static readonly DependencyProperty ToggleStyleProperty = DependencyProperty.Register("ToggleStyle", typeof(Style), typeof(BasicSkin), null);
   4 public static readonly DependencyProperty ToolTipStyleProperty = DependencyProperty.Register("ToolTipStyle", typeof(Style), typeof(BasicSkin), null);
   5 public static readonly DependencyProperty CheckBoxStyleProperty = DependencyProperty.Register("CheckBoxStyle", typeof(Style), typeof(BasicSkin), null);
   6 public static readonly DependencyProperty RadioStyleProperty = DependencyProperty.Register("RadioStyle", typeof(Style), typeof(BasicSkin), null);
   7 public static readonly DependencyProperty TextBoxStyleProperty = DependencyProperty.Register("TextBoxStyle", typeof(Style), typeof(BasicSkin), null);
   8 public static readonly DependencyProperty CalendarStyleProperty = DependencyProperty.Register("CalendarStyle", typeof(Style), typeof(BasicSkin), null);
   9 public static readonly DependencyProperty DayButtonStyleProperty = DependencyProperty.Register("DayButtonStyle", typeof(Style), typeof(BasicSkin), null);
   10 public static readonly DependencyProperty MonthButtonStyleProperty = DependencyProperty.Register("MonthButtonStyle", typeof(Style), typeof(BasicSkin), null);
   11 public static readonly DependencyProperty DatePickerStyleProperty = DependencyProperty.Register("DatePickerStyle", typeof(Style), typeof(BasicSkin), null);
   12 public static readonly DependencyProperty LinkStyleProperty = DependencyProperty.Register("LinkStyle", typeof(Style), typeof(BasicSkin), null);
   13 public static readonly DependencyProperty DataGridStyleProperty = DependencyProperty.Register("DataGridStyle", typeof(Style), typeof(BasicSkin), null);
   14 public static readonly DependencyProperty ColumnHeaderStyleProperty = DependencyProperty.Register("ColumnHeaderStyle", typeof(Style), typeof(BasicSkin), null);
   15 public static readonly DependencyProperty RowHeaderStyleProperty = DependencyProperty.Register("RowHeaderStyle", typeof(Style), typeof(BasicSkin), null);
   16 public static readonly DependencyProperty RowStyleProperty = DependencyProperty.Register("RowStyle", typeof(Style), typeof(BasicSkin), null);
   17
   18
   19 /**//// <summary>
   20 /// Expose our background
   21 /// </summary>
   22 public Brush Foreground
   23 {
   24 get { return (Brush)GetValue(ForegroundProperty); }
   25 set { SetValue(ForegroundProperty, value); }
   26 }
   27
   28 /**//// <summary>
   29 /// Expose our button style
   30 /// </summary>
   31 public Style ButtonStyle
   32 {
   33 get { return (Style)GetValue(ButtonStyleProperty); }
   34 set { SetValue(ButtonStyleProperty, value); }
   35 }
   36
   37 /**//// <summary>
   38 /// Expose our toggle button style
   39 /// </summary>
   40 public Style ToggleStyle
   41 {
   42 get { return (Style)GetValue(ToggleStyleProperty); }
   43 set { SetValue(ToggleStyleProperty, value); }
   44 }
   45
   46 /**//// <summary>
   47 /// Expose our ToolTip Style
   48 /// </summary>
   49 public Style ToolTipStyle
   50 {
   51 get { return (Style)GetValue(ToolTipStyleProperty); }
   52 set { SetValue(ToolTipStyleProperty, value); }
   53 }
   54
   55 /**//// <summary>
   56 /// Expose our button style
   57 /// </summary>
   58 public Style CheckBoxStyle
   59 {
   60 get { return (Style)GetValue(CheckBoxStyleProperty); }
   61 set { SetValue(CheckBoxStyleProperty, value); }
   62 }
   63
   64 /**//// <summary>
   65 /// Expose our toggle button style
   66 /// </summary>
   67 public Style RadioStyle
   68 {
   69 get { return (Style)GetValue(RadioStyleProperty); }
   70 set { SetValue(RadioStyleProperty, value); }
   71 }
   72
   73 /**//// <summary>
   74 /// Expose our ToolTip Style
   75 /// </summary>
   76 public Style TextBoxStyle
   77 {
   78 get { return (Style)GetValue(TextBoxStyleProperty); }
   79 set { SetValue(TextBoxStyleProperty, value); }
   80 }
   81
   82 public Style CalendarStyle
   83 {
   84 get { return (Style)GetValue(CalendarStyleProperty); }
   85 set { SetValue(CalendarStyleProperty, value); }
   86 }
   87 public Style DayButtonStyle
   88 {
   89 get { return (Style)GetValue(DayButtonStyleProperty); }
   90 set { SetValue(DayButtonStyleProperty, value); }
   91 }
   92 public Style MonthButtonStyle
   93 {
   94 get { return (Style)GetValue(MonthButtonStyleProperty); }
   95 set { SetValue(MonthButtonStyleProperty, value); }
   96 }
   97 public Style DatePickerStyle
   98 {
   99 get { return (Style)GetValue(DatePickerStyleProperty); }
  100 set { SetValue(DatePickerStyleProperty, value); }
  101 }
  102 public Style LinkStyle
  103 {
  104 get { return (Style)GetValue(LinkStyleProperty); }
  105 set { SetValue(LinkStyleProperty, value); }
  106 }
  107
  108 public Style DataGridStyle
  109 {
  110 get { return (Style)GetValue(DataGridStyleProperty); }
  111 set { SetValue(DataGridStyleProperty, value); }
  112 }
  113 public Style ColumnHeaderStyle
  114 {
  115 get { return (Style)GetValue(ColumnHeaderStyleProperty); }
  116 set { SetValue(ColumnHeaderStyleProperty, value); }
  117 }
  118 public Style RowHeaderStyle
  119 {
  120 get { return (Style)GetValue(RowHeaderStyleProperty); }
  121 set { SetValue(RowHeaderStyleProperty, value); }
  122 }
  123 public Style RowStyle
  124 {
  125 get { return (Style)GetValue(RowStyleProperty); }
  126 set { SetValue(RowStyleProperty, value); }
  127 }
  完成了这两点程序已经可以动态切换皮肤了,自动查找皮肤文件的功能其实可有可无,但是这样做可以省却维护配置文件的麻烦,
  首先定义一个Skin的属性:
  
  
  1namespace Skinnable
  2{
  3 [AttributeUsage(AttributeTargets.Class)]
  4 public class SkinAttribute : Attribute
  5 {
  6 public string DisplayName { get; set; }
  7 }
  8}然后所有的皮肤样式都实现这个属性,比如:
  1 [Skin(DisplayName = "Red")]
  2 public partial class RedSkin : UserControl, ISkin
  3 {
  最后在Assembly里找到所有实现Skin属性的皮肤,保存到List中做成画面上面的菜单
   1internal List<SkinDefinition> AppSkins { get; private set; }
   2
   3public App()
   4 {
   5 this.AppSkins = new List<SkinDefinition>();
   6 ScanAssemblyForSkins(Assembly.GetExecutingAssembly());
   7 }
   8
   9 /// <summary>
  10 /// Scans an assembly for skins
  11 /// </summary>
  12 /// <param name="asm"></param>
  13 private void ScanAssemblyForSkins(Assembly asm)
  14 {
  15 if (asm != null)
  16 {
  17 LoadSkins(asm);
  18 }
  19 }
  20
  21
  22 /// <summary>
  23 /// Loads all the calculator skins that can be found inside the assembly
  24 /// </summary>
  25 /// <param name="asm"></param>
  26 private void LoadSkins(Assembly asm)
  27 {
  28 // Look for all internal static classes in the assembly
  29 var classTypes = from type in asm.GetTypes()
  30 let attr = GetSkinAttribute(type)
  31 where type.IsAbstract == false && type.IsClass == true && attr != null
  32 select new { ClassType = type, SkinAttribute = attr };
  33
  34 foreach (var ct in classTypes)
  35 {
  36 string name = ct.SkinAttribute.DisplayName;
  37 if (string.IsNullOrEmpty(name))
  38 name = ct.ClassType.Name;
  39
  40 this.AppSkins.Add(new SkinDefinition(name, ct.ClassType));
  41 }
  42 }
  43
  44 /// <summary>
  45 /// Finds the SkinAttribute if it is on the type
  46 /// </summary>
  47 /// <param name="member"></param>
  48 /// <returns></returns>
  49 static private SkinAttribute GetSkinAttribute(Type type)
  50 {
  51 return (from a in type.GetCustomAttributes(typeof(SkinAttribute), true)
  52 select a as SkinAttribute).FirstOrDefault();
  53 }
  代码下载:http://www.cnblogs.com/Files/ithurricane/Skinnable.zip
  
  到这里动态换肤的程序就告一段落了,但现在有一个问题,那就是假如有两个皮肤文件A和B,A定义了Button的皮肤,B中没有定义,
  那么A切换到B,Button如何回到原始的样子?如果不同的样式文件定义的控件不一致有什么好方法调整吗?
  
  
  
  
  作者:ithurricane
  出处:http://ithurricane.cnblogs.com
  联系:ithurricane@126.com
  MSN:ithurricane@hotmail.com
  QQ:20158686
  
    


阅读:
录入:blue1000

评论 】 【 推荐 】 【 打印
上一篇:微软收购SpringSource公司
下一篇:在.Net中使用异步(二)
相关文章      
本文评论
发表评论


点评: 字数
姓名:

  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款