前段时间国外的大牛为Silverlight2定义了四套皮肤,效果让人称奇,美中不足的是四个皮肤不能动态切换,
所以我借花献佛将这四套皮肤放在一起实现了动态切换也就是换肤的效果。
开发平台:VS2008 + Silverlight2
Live Demo
效果图:
选择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的属性:
1
namespace 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中做成画面上面的菜单
1
internal List<SkinDefinition> AppSkins { get; private set; }
2
3
public 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://files.cnblogs.com/ithurricane/Skinnable.zip
到这里动态换肤的程序就告一段落了,但现在有一个问题,那就是假如有两个皮肤文件A和B,A定义了Button的皮肤,B中没有定义,
那么A切换到B,Button如何回到原始的样子?如果不同的样式文件定义的控件不一致有什么好方法调整吗?
posted @ 2008-04-01 13:04
ithurricane 阅读(3251)
评论(14) 编辑 收藏 网摘 所属分类:
Fun with Silverlight2.0