WPF资源系统是一种保管一些列有用对象(画刷,样式,模板)的简单方法,从而可以更容易地重用这些对象。
程序集资源是一块嵌入到编译过的程序集中的二进制数据。程序集资源可确保应用程序所需的图像或声音文件,另一方面对象资源是希望在某个位置定义并在其他几个位置重复使用的.NET对象。
资源基础
WPF允许在代码中以及在标记中的各个位置定义资源(和特定的控件,窗口一起定义,或整个应用程序中定义)。资源有许多优点:
- 高效 - 通过资源定义对象,并在标记中的多个地方使用。
- 可维护性 - 通过资源使用低级的格式化细节(如字号),并将它们移动到便于对其进行修改的中央位置。在XAML中创建资源相当于在代码中创建常量。
- 适应性 - 一旦特定信息与应用程序的其他部分分离开来,并放置到资源部分中,就可以动态地修改这些信息。
资源集合
每个元素都有 Resources 属性,存储了一个资源字典集合(ResourceDictionary类的实例),可包含任意类型的对象,根据字符串编写索引。
<Window.Resources>
<ImageBrush x:Key="TitleBrush" TileMode="Tile"
Viewport="0 0 32 32" ImageSource="happy.jpg">
</ImageBrush>
</Window.Resources>
Key特性会将该画刷放置到 XAML名称空间中,而不是WPF名称空间中。指定了在 Window.Resources.Collection 集合中编写画刷索引的名称。
为使用XAML标记中的资源,需要一种引用资源的方法,通过标记扩展完成,一个用于 动态资源 ,另一个用于 静态资源。
<!--静态资源在首次创建窗口时一次性设置完毕-->
<Button Background="{StaticResource TileBrush}">A Tiled Button</Button>
<!--动态资源,如果发生变化会重新应用资源(开销大些)-->
<Button Background="{DynamicResource TileBrush}">A Tiled Button<Button>
每个元素都可以由自己的资源集合,WPF在元素树中进行递归搜索,从自己的资源集合开始查找。静态资源只从资源集合中获取一次,根据对象的类型,任何变化都可能别立即注意到。动态资源在每次需要时都会重新从资源集合中查找对象。
在极少情况下,动态资源用于提高第一次加载窗口时的性能。然而,除非资源非常大切复杂,否则这样做没任何益处。
查找资源
通常在标记中定义和使用资源,但也可以在代码中使用资源:
private void cmdBtn_Click(object sender, RoutedEvnetArgs e)
{
Button cmd = (Button)sender;
ImageBrush brush = (ImageBrush)sender.FindResource("TitleBrush");
}
可使用 TryFindResource() 替代 FindResource() 方法,如果找不到资源会返回null引用,而不是抛出异常。
窗口不是查找资源的最后一站,WPF会继续查找应用程序定义的资源集合,即 App.xam 文件中定义的资源。
<Application>
<Application.Resource>
<ImageBrush x:Key="TitleBrush" ImageSource="happy.jpg"/>
</Application.Resource>
</Application>
应用程序资源也不是最后一站,WPF会继续查找系统资源。SystemColors,SystemFonts,SystemParameters.
资源字典
如果希望在多个项目之间共享资源,可创建资源字典。资源字典只是 XAML文档,除了存储资源不做其他任何事情。
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ImageBrush x:Key="TitleBrush" ImageSource="happy.jpg"/>
</ResourceDictionary>
当为应用程序添加字典时,将 Build Action 设置为 Page(与其他XAMl文件一样),可以保证获得最佳性能而将资源字典编译为 BAML。不过设置为 Resource 也是很完美的,这样会被嵌入到程序集中,但不会被编译。
使用资源字典
使用资源字典需要将其合并到应用程序某些位置的资源集合中。可以特定窗口中执行此操作,但通常将其合并到应用程序的资源集合中。
<Application>
<Application.Resources>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="AppBrushes.xaml"/>
<ResourceDictionary Source="WizardBrushes.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!--添加自己的资源并合并到资源字典中-->
<ImageBrush x:Key="TitleBrush" ImageSource="happy.jpg"/>
</Application.Resources>
</Application>
当在多个应用程序之间共享资源字典,可将资源字典编译到单独的类库程序集中,并分发组件。在使用时有两种方法提取资源。最直观的方法是用代码创建合适的 ResourceDictionary 对象。
var resourceDic = new resourceDictionary();
resourceDic.Source = new Uri("ResourceLibrary;component/ReusableDictionary.xaml",UriKind.Relative);
cmd.Background = (Brush)resourceDic["TitleBrush"];
如果不想写代码,可使用 ComponentResourceKey 标记扩展,必须将资源字典放置到 generic.xaml 文件中并放到 Themes 子文件夹中。
<ResourceDictionary>
<ImageBrush x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:CustomResources},ResourceId=SadTileBrush}"
TitleMode="Tile" ImageSource="ResourceLibrary;component/sadface.jpg"/>
</ResourceDictionary>
在另一个程序中使用它:
<Widnow
xmlns:res="clr-namespace:ResourceLibrary;assembly=ResourceLibrary">
<Button Background="{DynamicREsource{ComponentResourceKey TypeInTargetAssembly={x:Type res:CustomResources}, ResourceId=SadTileBrush}}"/>
</Widnow>
当使用 ComponentResourceKey时,必须使用动态资源,不能使用静态资源。