WPF中的资源

本文详细介绍了WPF中的对象级资源,包括如何定义和查找,以及如何通过静态和动态方式使用资源。此外,还讲解了如何向程序中添加二进制资源,如图标、图片等,并通过Pack URI路径访问这些资源。资源的层级结构从数据库、资源文件到对象资源和变量,使得数据管理更加灵活。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们把有用的东西称为资源。“兵马未动,粮草先行”——程序中的各种数据就是算法的原料和粮草。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代表根目录的/不能省略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值