关闭

WPF中的资源

867人阅读 评论(0) 收藏 举报
分类:

我们把有用的东西称为资源。“兵马未动,粮草先行”——程序中的各种数据就是算法的原料和粮草。WPF不但支持程序级的传统资源,同时还推出了独具特色的对象级资源,每个界面元素都可以携带自己的资源并可被自己的子级元素共享。这样一来,WPF程序中的数据就分为四个等级存储:数据库里的数据相当于存放在仓库里,资源文件里的数据相当于放在旅行箱里,WPF对象资源里数据相当于放在随身携带的背包里,变量中的数据相当于拿在手里。

WPF对象级资源的定义与查找

每个WPF的界面元素都具有一个名为Resources的属性,这个属性继承自FrameworkElement类,其类型为ResourceDictionary。ResourceDictionary能够以“键——值”对的形式存储资源,当需要使用某个资源时,使用“键——值”对可以索引到资源对象。在保存资源时,ResourceDictionary视资源对象为Object类型,所以在使用资源时先要对资源对象进行类型转换,XAML编译器能够根据标签的Attribute自动识别资源类型,如果类型不对就会抛出异常,但在C#代码里检索到资源对象后,类型转换的事情就只能由我们自己来做了。

ResourceDictionary可以存储任意类型的对象。在XAML代码中向Resources添加资源时需要把正确的名称空间引入到XAML代码中。

<Window x:Class="Chapter10.Page193.ResourcesDemo.MainWindow"
        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"
        
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <sys:String x:Key="str">我是一个资源文件</sys:String>
        <sys:Double x:Key="dbl">3.1415926</sys:Double>
    </Window.Resources>
    <StackPanel>
        <TextBlock Text="{StaticResource ResourceKey=str}"/>
    </StackPanel>
</Window>

首先将System名称空间引入XAML代码并映射为sys名称空间,然后在Window.Resources属性里添加两个资源条目,一个是string类型实例,一个是double类型实例,最后我们用两个TextBlock来消费这些资源。

在检索资源时,先查找控件自己的Resources属性,如果没有这个资源程序会沿着逻辑树向上一级控件查找,如果连最顶层容器都没有这个资源,程序就会去Application.Resources(也就是程序的顶级资源),如果还没有找到,那就只好抛出异常了。这就好比每个界面元素都有自己的一个背包,里面可能装着各种各样的资源,使用的时候打开找一找,如果没有找打还可以去翻开上一层控件的背包,直到找到资源或报告没有这个资源为止。

如果想在C#代码中使用定义在XAML代码里的资源,大概是这样:

            string text = (string)this.FindResource("str");

或者明确的知道资源放在哪的资源词典里,就可以这样检索资源:

            string text1 = (string)this.Resources["str"];

WPF的资源可以像CSS或Javascript那样独立的放在文件中,使用时成套引用、重用。ResourceDictionary具有一个名为Source的属性,只要把包含资源定义的文件路径赋值给这个属性即可。例如:

    <Window.Resources>
        <ResourceDictionary Source="ShinyRed.xaml"/>
    </Window.Resources>

且“静”且“动”用资源

当资源被存储进资源词典后,我们可以通过两种方式来使用这些资源——静态方式和动态方式。Static和Dynamic两个词是我们的老朋友,Static指的是程序的非执行状态而Dynamic指的是程序运行状态。静态资源使用(StaticResource)指的是程序载入内存时对资源的一次性使用,之后就不再去访问这个资源了;动态资源使用(DynamicResource)指的是在程序运行过程中仍然会去访问资源。拿程序的主题来举例,如果程序皮肤的颜色在运行中始终不变,以StaticResource方式来使用资源就可以了;如果程序运行过程中允许用户更改皮肤的配色方案则必须以DynamicResource方式来使用资源。

请看下面的例子。我们在Window资源词典里放置了两个TextBlock类型资源并分别以StaticResource和DynamicResource方式来使用:

<Window x:Class="Chapter10.Page195.DynamicResouceDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <TextBlock x:Key="res1" Text="海上生明月"/>
        <TextBlock x:Key="res2" Text="海上生明月"/>
    </Window.Resources>
    
    <StackPanel>
        <Button Margin="5" Content="{StaticResource res1}"/>
        <Button Margin="5" Content="{DynamicResource res2}"/>
        <Button Margin="5" Content="Update" Click="Button_Click"/>
    </StackPanel>
</Window>

第三个按钮负责在程序运行过程中对资源词典里的两个资源进行改变:

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            this.Resources["res1"] = new TextBlock() { Text = "天涯共此时" };
            this.Resources["res2"] = new TextBlock() { Text = "天涯共此时" };

        }

实际上,因为第一个按钮是以静态方式使用资源,所以尽管资源已经被更新它也不会知道。第二个按钮是动态资源的方式,所以当后台代码更新资源时,界面上也会相应的改变。


向程序中添加二进制资源

Windows应用程序资源的道理与Winzip或WinRAR压缩包的道理差不多,实际上是把一些应用程序必须使用的资源与应用程序自身打包在一起,这样资源就不会意外丢失(副作用就是应用程序体积变大)。常见的应用程序资源有图标、图片、文本、视频、音频等,各种编程语言的编译器或资源编译器都有能力把这些文件编译进目标文件(最终的.exe或.dll文件),资源文件在目标文件里是以二进制数据的形式存在,行程目标文件的资源段(Resource Section),使用时数据会被提取出来。

为了不把资源词典里资源和应用程序内嵌的资源搞混,我们明确地称呼资源词典里的资源为“WPF资源”或“对象资源”,程序应用程序的内嵌资源为“程序集资源”或“二进制资源”。

下面我们看看如何向WPF程序添加二进制资源并使用他们。

如果要添加的资源是字符串而非文件,我们可以使用应用程序Properties名称空间中的Resources.resx资源文件。打开资源文件的方法是在项目管理器中展开Properties结点并双击Resources.resx:


Resources.resx文件内容的组织形式也是“键——值”对,编译后,Resources.resx会形成Properties名称空间中的Resources类,使用这个类的方法或属性就能获取资源。为了让XAML编译器能够访问这个类,一定要把Resources.resx的访问级别由Internal改为Public。利用资源文件编辑器,可以在资源文件的字符串组里添加两个条目,然后分别在XAML和C#代码中访问他们。


在XAML代码中使用Resources.resx中的资源,先要把程序的Properties名称空间映射为XAML名称空间,然后使用x:Static标签扩展来访问资源(x:Static在XAML界面里访问cs类里面的静态属性或字段):

<Window x:Class="Chapter10.Page196.ResoucesDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        
        xmlns:prop="clr-namespace:Chapter10.Page196.ResoucesDemo.Properties"
        
        Title="MainWindow" Height="350" Width="525">
    <Grid Margin="5">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="4"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="23"/>
            <RowDefinition Height="4"/>
            <RowDefinition Height="23"/>
        </Grid.RowDefinitions>
        <TextBlock Text="{x:Static prop:Resources.UserName}"/>
        <TextBlock x:Name="textBlockPassword" Grid.Row="2"/>
        <TextBox BorderBrush="Black" Grid.Column="2"/>
        <TextBox BorderBrush="Black" Grid.Column="2" Grid.Row="2"/>
    </Grid>
</Window>

C#中访问Resources.resx中的资源的使用:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Chapter10.Page196.ResoucesDemo
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.textBlockPassword.Text = Properties.Resources.Password;
        }
    }
}

使用Resources.resx最大的好处就是便于程序的国际化、本地化。如果我们想把界面改为英文版,只需要把资源的值改为相应的英文即可。


如果需要添加的资源不是字符串而是图标、图片、音频或视频,方法就不是使用Resources.resx了,WPF不支持这样做。在WPF中使用外部文件作为资源,仅需要简单地将其加入项目即可。方法是在项目是在项目管理器中右击项目名称,在弹出菜单里选择Add——>NewFolder,按需要新建基层文件夹来组织资源,然后在恰当的文件夹上右击,在弹出的菜单里选择Add——>Existing Item,在文件对话框里选择文件后单击Add按钮,文件就以资源的形式加入到项目中。

如果想让外部文件编译进目标成为二进制资源,必须在属性窗口中把文件的BuildAction属性值设为Resource。并不是每种文件都会自动设为Resource,比如图片文件会,MP3文件就不会。一般情况下如果BuildAction属性被设为Resource,则Copy to Output Directory属性就设为Do not copy;如果不希望以资源的形式使用外部文件,可以把Build Action设为None,而把Copy to output Directory设为Copy Always。另外,Build Action属性的下拉列表里有一个颇具迷惑性的值Embedded Resource,不要选择这个值。


使用Pack URI 路径访问二进制资源

前面例子中我们添加另一个名为Bomber.jpg的图片,它在项目里的路径是Image/Bomber.jpg,原封不动的使用这个路径就可以访问这个图片了。

<Image Source="Image/Bomber.jpg" x:Name="img"/>


等同于C#代码为:

            Uri imgUri = new Uri(@"Image/Bomber.jpg", UriKind.Relative);
            img.Source = new BitmapImage(imgUri);

UriKind必须为Relative,代表根目录的/可以省略。Absolute代表根目录的/不能省略。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:133360次
    • 积分:2268
    • 等级:
    • 排名:第17190名
    • 原创:86篇
    • 转载:86篇
    • 译文:0篇
    • 评论:2条
    文章分类
    最新评论