WPF(资源)
1.资源介绍
之前讲过直接设置元素的样式,但是直接设置对于样式的共享没有什么帮助。样式可以放在资源中。在资源中,可以把样式赋予指定的元素,把一个样式赋予某一类型的所有元素,或者为该样式使用一个键。要把样式赋予某一个类型的所有元素,可使用Style的TargetType属性,指定x:Type标记扩展{x:Type Button},从而将样式赋予一个按钮。要定义需要引用的样式,必须设置x:Key:
- 没有指定Key的Style就表示默认的样式:也可以这样写
- 若是在元素中设置了样式中同名称的属性,就会应用指定的设置,下例的Background显示为AliceBlue
2.Null样式
如果在资源中定义了默认样式,那如果想让一个Button什么样式都不使用怎么办? 将Style定义为{x:Null} 就不会应用任何样式了。
3.样式继承
样式提供了一种继承样式。一个样式可以基于另一个样式。继承之后,它使用该样式定义的所有设置,并且可以修改继承的设置和添加新的设置。设置样式的BasedOn属性定义继承的样式。
4.样式多用
按照上述定义的样式只能用于某一个控件,而不能将这个样式应用于多个控件,我们可以利用控件的继承关系使一个样式应用于多个控件!
WPF中的控件继承关系分两种:
- 控件 -> Control -> FrameworkElement
- 控件 -> FrameworkElement
对于继承了Control的控件可以设置同样的样式:
- 这里的Button、Label、TextBox都继承了Control,就可以对它们设置Control中含有的属性值。而TextBlock没有继承Control,对其设置样式就会报错。
- 必须先定义父类的样式才能应用BasedOn继承
5.动态资源
- 上述用到的资源都是静态资源,定义的资源必须写在第一个元素之前,否则运行后会提示未找到资源,设计期间没有问题。但是应用的是动态资源可以在所有元素之前或之后。
- 通过StaticResource标记扩展,在加载期间搜索资源。如果在运行程序的过程中改变了资源,就应使用DynamicResource标记扩展。
- DynamicResource需要的性能资源比StaticResource更多,因为其资源总数在需要时加载。只有需要在运行期间进行修改,才给资源使用DynamicResource。
- DynamicResource若没有找到,也不会报错,会自动应用默认的样式。
- BasedOn属性设置的资源只能是StaticResource。
通过代码更换资源:
private void Button_Click3(object sender, RoutedEventArgs e)
{
Control ctr = sender as Control;
ctr.Style = ctr.FindResource("BtnStyle3") as Style;
}
从XAML中可以看出,两个Button都应用了BtnStyle2样式,所以都有了Button_Click3事件。当点击按钮后将样式更改为BtnStyle3,这个时候按钮就会应用BtnStyle3样式。注意点就是由于这里的事件是在样式中定义的,若是样式改变后,样式中的事件也会取消订阅,若是新样式中也有同样的事件,就会重新订阅新样式中的事件。这里点击按钮就会取消Button_Click3事件,订阅Button_Click4事件。
通过代码更改样式设置:
添加一个按钮用于修改样式,事件Button_Click5。
private void Button_Click5(object sender, RoutedEventArgs e)
{
Style resource = this.canvas1.Resources["BtnStyle2"] as Style;
Setter setter = resource.Setters.OfType().
LastOrDefault(s => s.Property == Button.BackgroundProperty);
setter.Value = new SolidColorBrush(Colors.Blue);
}
若是以上图代码修改,你会发现提示异常:System.InvalidOperationException:“使用“SetterBase”之后(密封),无法对其进行修改。”。所以样式在XAML中定义好以后就不能修改了,但是我们可以先移除这个样式,再添加一个同名的样式以达到修改样式的结果。
private void Button_Click5(object sender, RoutedEventArgs e)
{
Style style = new Style(typeof(Button));
style.Setters.Add(new Setter(Button.BackgroundProperty, new SolidColorBrush(Colors.Blue)));
this.canvas1.Resources.Remove("BtnStyle2");
this.canvas1.Resources.Add("BtnStyle2", style);
}
用上述代码可以达到修改样式的效果,但是修改后你会发现只有是动态资源的Button的背景色改变了,而用静态资源的Button并没有改变,所以这就是静态资源和动态资源的区别。定义用静态资源样式的控件,运行后就不能通更改样式达到改变效果的目的,但是我们可以直接修改你要修改的属性值。
6.作用域
通过上述的例子,有没有发现资源都是定义的 Canvas.Resources。Resources是在FrameworkElement类里面,按道理是所有的控件都可以定义资源,并且资源只能定义一次。
//
// 摘要:
// 获取或设置本地定义的资源字典。
//
// 返回结果:
// 资源中当前本地定义的字典,其中的每个资源均可通过键进行访问。
[Ambient]
public ResourceDictionary Resources { get; set; }
上述事例中,定义了三个同名的样式(BtnStyle2),但是在不同的资源中,那各个Button会应用哪个样式呢???
- 若都是静态资源:btn1是Gold;btn2是Gold;btn3是Red; 静态资源是根据当前元素往上离它最近的资源为主。
- 若都是动态资源:btn1是Gold;btn2是Red;btn3是Red; 动态资源是根据所属关系最近的为主。
- 资源按层次结构搜索,各资源只能在自己所属范围内有效,不在该范围内的元素应用了该资源会提示错误。
如果需要将同一个样式应用于多个窗口,就可以用应用程序定义样式。App.xaml文件以定义应用程序的全局资源。应用程序样式对其中的每个窗口都有效;每个元素都可以访问该资源。在窗口中,如果该元素未在该窗口中找到资源,就可以通过Application继续搜索资源。
7.资源字典
如果相同的资源可用于不同的应用程序,把资源放在一个资源字典中就比较有效。使用资源字典,可以在多个应用程序之间共享文件,也可以把资源字典放在一个程序集中,供应用程序共享。
要共享程序集中的资源字典,应创建一个库。可以把资源字典文件添加到程序集中。这个文件的构建动作必须设置为Resource,从而把它作为资源添加到程序集中。
添加新建项《资源字典》,命名为 Dictionary1.xaml
对于目标窗体,需要引用这个库,并把资源字典添加到这个字典中。通过ResourceDictionary的MergedDictionaries属性,可以使用添加进来的多个资源字典文件。 使用ResourceDictionary的Source属性,可以引用字典。在引用时,使用包URI语法。包URI语法可以指定为绝对的,其中URI以"pack://"开头,也可以指定为相对的,如下例所示。使用相对语法,包含字典的引用程序集WpfAppLearn1跟在"/"的后面,其后是";component"。component表示,该字典作为程序集中的一个资源包含在内。之后添加字典文件的名称 Dictionary.xaml。如果把字典添加到子文件夹中,则还必须声明子文件夹名。