前变也已经说过可以引用别的程序集的资源,那么到底如何引用呢?另外,我们都是在说XAML中引用资源,那么代码中又该如何去做呢?我们又通常会在什么地方定义资源呢?这里就来说一下这几个问题,以及某些特殊情况下的定义。
· 共享资源
默认情况下,当有一个资源被引用到多个地方是,使用的都是同一个对象实例,这通常是理想的行为。但你同样也可以把x:Shared=”False”来让每个引用资源的地方都生成一个不同的对象实例,这样可以独立进行修改。这通常用于多逻辑资源的声明。
· 程序代码中定义和应用资源
在代码中定义一个新的Resource,你需要首先得到一个ResourceDictionary的实例,然后再创建一个新的资源并将这个资源加入到ResourceDictionary的实例中。而在访问资源时,你需要用到myWindow.Resources[“key”]或者object.FindResource(key)函数。注意myWindows是你当前window的实例,而在用FindResource时,前边的object代表的是这个资源所在的ResourceDictionary的父对象。
private void Window_Loaded(object sender, RoutedEventArgs e) { Window3 window = new Window3(); window.Resources.Add("buttonBackground", newSolidColorBrush(Color.FromRgb(0,255,0))); window.Resources.Add("borderBrush", new SolidColorBrush(Color.FromRgb(255, 0, 0)));
btnContent.Background = (Brush)window.FindResource("buttonBackground"); btnContent.BorderBrush = (Brush)window.FindResource("borderBrush"); } |
注意在找不到资源时会抛出一个ResourceReferenceKeyNotFoundException异常,所以尽量调用TryFindResource方法更好些,如果失败将会返回null.
上边的例子是针对StaticResource来说的,它就相当于这段代码:
<Button x:Name="btnContent" Canvas.Left="50" Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Content="Content" /> |
|
但对于DynamicResource来说,需要调用这个元素的SetResourceReference方法来更新依赖属性的绑定。下边的两端代码是相等的:
<Button x:Name="btnContent" Canvas.Left="50" Background="{DynamicResource buttonBackground}" BorderBrush="{ DynamicResource borderBrush}" Content="Content" /> |
btnContent.SetResourceReference(Button.BackgroundProperty, "buttonBackground"); btnContent.SetResourceReference(Button.BorderBrushProperty, "borderBrush"); |
SetResourceReference是可以在资源被加载到某个Resource Dictionary之前调用的,即便是FindResource会失败,但引用的建立仍然有效。
· 从另一个程序集中访问嵌入式资源
除了可以用特定的URI来访问别的程序中的二进制资源外,WPF可以从另外一个程序集中获取逻辑资源,这得用到ComponentResourceKey标记。要使用ComponentResourceKey,每个资源都必须有一个键名。然后你可以通过这样的方式访问:
<Button Background=”{DynamicResource {x:Static otherAssembly: MyClass.MyClassBrushKey }}” /> |
· Styles 和 Implicit Keys
样式是最常见的一种资源,而且它总是被定义在Resource Dictionary中,为了来重用。Style其实就是一系列分组的Setter的集合,用来设定逻辑资源的属性值,它有一种比较特殊的情形就是Implicit Keys,可以不声明一个x:Key的名字,而只设置x:TargetType的值,这样面对的就是对于所有这个类型的控件都使用这个样式。下边的示例中x:Key的值其实就是type-Button。
<Style TargetType="Button"> <Setter Property="Background"> <Setter.Value> <LinearGradientBrush> <GradientStop Offset="0.0" Color="AliceBlue"/> <GradientStop Offset="1.0" Color="Salmon"/> </LinearGradientBrush> </Setter.Value> </Setter> <Setter Property="FontSize" Value="18"/> </Style> |
Style也是资源的一种---从某种意义上来说,它很类似于我们给普通HTML中的元素建立CSS. 对于Designer来说可能轻松一些哦。
6. Resource Dictionary –资源字典
所有的资源项在最终都会被整合到Resource Dictionary中的,也就是说无论是FrameworkElement的Resources,还是Window的Resources,还是Application的Resources,还是特定的ResourceDictionary中定义的resources在整个应用编译执行的时候实际上他们都在一起的作为可遍历集合共同存在于一个相对会话空间内的。
我们也提到过Resource的key是可以被允许有相同的,这样在遍历不同相对地址的Resource Dictionary时会根据StaticResource或者DynamicResource的lookup behavior来确定哪个有效。通常为了维护和灵活性的考虑,我们通常会将Resource Dictionary文件分成好几个,但在某些场合下我们只需要用其中某些资源,那么我么可以将资源从几个独立的文件中提取并合并,那么可以这么做:
<Window.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Diction1.xaml"></ResourceDictionary> <ResourceDictionary Source="Diction2.xaml"></ResourceDictionary> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Window.Resources> |
注意,在资源合并后,仍然会出现重复值的情况,那么最后取出的资源获胜。
7. Localization – 本地化
本地化和换肤其实都是在用ResourceDictionary来做文章的。说白了,Localization就是用不同语言下取不同事先设定好的资源来显示而已。要做到这些很容易,4步就可以轻松实现:
· 定义Resource Dictionary来包含不同语言下要显示的资源项。
创建单独的Resource Dictionary文件,并以语言本身名字来命名,并把en-US来作为默认语言环境(这里顺便就命名为default.xaml了)
Default.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String x:Key="Title_PM">Project Manager</sys:String> <sys:String x:Key="Title_PL">Project Lead</sys:String> <sys:String x:Key="Title_SD">Senior Developer</sys:String> <sys:String x:Key="Title_SA">System Architecture</sys:String>
</ResourceDictionary> |
zh-CN.xaml (注意对.NET命名空间的引用)
<ResourceDictionaryxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String x:Key="Title_PM">项目经理</sys:String> <sys:String x:Key="Title_PL">项目主管</sys:String> <sys:String x:Key="Title_SD">资深开发工程师</sys:String> <sys:String x:Key="Title_SA">系统架构师</sys:String>
</ResourceDictionary> |
· 给应用程序添加默认资源:其实就是将默认的Resource Dictionary加入到Application的全局Resource里边。
<Application x:Class="WpfApplication1.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="Window1.xaml"> <Application.Resources>
<ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Language\default.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary>
</Application.Resources> </Application> |
· 在Application启动时根据不同语言来加载以语言命名的XAML文件(Resource Dictionary)。因为对于重名的资源,后来加载的资源将会胜出,所以以当前语言名加载的XAML文件中的资源项将会被引用。这就是多语言的本质!
public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e);
LoadLanguage(); }
private void LoadLanguage() { CultureInfo currentCultureInfo = CultureInfo.CurrentCulture; ResourceDictionary langRd = null; try { langRd = Application.LoadComponent( new Uri(@"Language\" + currentCultureInfo.Name + ".xaml", UriKind.Relative)) asResourceDictionary; } catch { }
if (langRd != null) { if (this.Resources.MergedDictionaries.Count > 0) { this.Resources.MergedDictionaries.Clear(); } this.Resources.MergedDictionaries.Add(langRd); } } } |
· 在XAML中引用资源。
<TextBlock Canvas.Top="50" Width="100" Height="24" Text="{StaticResource Title_PM}" /> |
· 大功告成,运行程序你会看到默认的语言的显示:Project Manager.当然如果你的默认文化是英语的话。用程序换成中文试试结果?没问题,在LoadLanguage()之前更改语言即可:
base.OnStartup(e); CultureInfo info = new CultureInfo("zh-CN"); Thread.CurrentThread.CurrentCulture = info; Thread.CurrentThread.CurrentUICulture = info; LoadLanguage(); |
简单吧?呵呵。有关Resource的东西基本上就这么多了,换肤我们再开辟另一个话题来谈吧。这可是WPF够炫的Feature之一哦。。。。