WPF 应用程序资源、内容和数据文件

Microsoft Windows 应用程序通常依赖于包含非可执行数据的文件,例如可扩展应用程序标记语言(XAML)、图像、视频和音频。 Windows Presentation Foundation (WPF)为配置、识别和使用这些类型数据文件(称为应用程序数据文件)提供了特殊支持。 此支持围绕一组特定的应用程序数据类型,包括:

  • 资源文件:编译到可执行文件或库程序集的 WPF 程序集中的数据文件。

  • 内容文件:与可执行 WPF 程序集显式关联的独立数据文件。

  • 源文件站点:与可执行 WPF 程序集没有关联的独立数据文件。

这三种类型的文件之间的一个重要区别是资源文件和内容文件在生成时已知:程序集具有对它们的明确了解。 但是,对于原始站点文件,程序集可能根本不了解它们,也可能只是通过包统一资源标识符(URI)获得隐式知识;在后一种情况下,无法保证引用的原始站点文件实际上存在。

为了引用应用程序数据文件,Windows Presentation Foundation(WPF)使用 Pack 统一资源标识符(URI)方案,该方案在 WPF的 Pack URI 中进行了详细说明。

本主题介绍如何配置和使用应用程序数据文件。

资源文件

如果应用程序数据文件必须始终可供应用程序使用,保证可用性的唯一方法是将其编译为应用程序的主可执行程序集或其引用的程序集之一。 这种类型的应用程序数据文件称为 资源文件

应在以下情况下使用资源文件:

  • 编译到程序集后,无需更新资源文件的内容。

  • 你希望通过减少文件依赖项的数量来简化应用程序分发复杂性。

  • 应用程序数据文件需要本地化(请参阅 WPF 全球化和本地化概述)。

注意

本节中所述的资源文件不同于 XAML 资源 中所述的资源文件,不同于 管理应用程序资源(.NET)中所述的嵌入资源或链接资源。

配置资源文件

在 WPF 中,资源文件是作为 Resource 项包含在Microsoft生成引擎 (MSBuild) 项目中的文件。

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ... >  
  ...  
  <ItemGroup>  
    <Resource Include="ResourceFile.xaml" />  
  </ItemGroup>  
  ...  
</Project>  

注意

在 Visual Studio 中,通过将文件添加到项目并将其 Build Action 设置为 Resource来创建资源文件。

生成项目后,MSBuild 会将资源编译到程序集中。

使用资源文件

若要加载资源文件,可以调用 Application 类的 GetResourceStream 方法,并传递标识所需资源文件的包 URI。 GetResourceStream 返回 StreamResourceInfo 对象,该对象将资源文件公开为 Stream 并描述其内容类型。

例如,以下代码演示了如何使用 GetResourceStream 来加载 Page 的资源文件,并将该文件设置为 FramepageFrame)的内容:

// Navigate to xaml page
Uri uri = new Uri("/PageResourceFile.xaml", UriKind.Relative);
StreamResourceInfo info = Application.GetResourceStream(uri);
System.Windows.Markup.XamlReader reader = new System.Windows.Markup.XamlReader();
Page page = (Page)reader.LoadAsync(info.Stream);
this.pageFrame.Content = page;
' Navigate to xaml page
Dim uri As New Uri("/PageResourceFile.xaml", UriKind.Relative)
Dim info As StreamResourceInfo = Application.GetResourceStream(uri)
Dim reader As New System.Windows.Markup.XamlReader()
Dim page As Page = CType(reader.LoadAsync(info.Stream), Page)
Me.pageFrame.Content = page

调用 GetResourceStream 可以访问 Stream,但你需要额外作业,将其转换为你要设置的属性类型。 相反,你可以让 WPF 负责打开和转换 Stream,方法是使用代码将资源文件直接加载到类型的属性中。

以下示例演示如何使用代码将 Page 直接加载到 FramepageFrame)。

Uri pageUri = new Uri("/PageResourceFile.xaml", UriKind.Relative);
this.pageFrame.Source = pageUri;
Dim pageUri As New Uri("/PageResourceFile.xaml", UriKind.Relative)
Me.pageFrame.Source = pageUri

以下示例是前面示例的等效标记。

<Frame Name="pageFrame" Source="PageResourceFile.xaml" />

应用程序代码文件作为资源文件

可以使用包 URI(包括窗口、页面、流文档和资源字典)引用一组特殊的 WPF 应用程序代码文件。 例如,可以通过使用包 URI 来引用应用程序启动时要加载的窗口或页面,从而设置 Application.StartupUri 属性。

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    StartupUri="SOOPage.xaml" />

将 XAML 文件作为 Page 项包含在 MSBuild 项目中时,可以执行此操作。

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ... >  
  ...  
  <ItemGroup>  
    <Page Include="MainWindow.xaml" />  
  </ItemGroup>  
  ...  
</Project>  

注意

在 Visual Studio 中,向项目添加新的 WindowNavigationWindowPageFlowDocumentResourceDictionary,标记文件的 Build Action 默认为 Page

编译具有 Page 项的项目时,XAML 项将转换为二进制格式并编译为关联的程序集。 因此,这些文件的使用方式与典型的资源文件相同。

注意

如果将 XAML 文件配置为 Resource 项,并且没有代码隐藏文件,则原始 XAML 将编译为程序集而不是原始 XAML 的二进制版本。

内容文件

内容文件 作为松散文件与可执行程序集一起分发。 虽然它们未被编译成程序集,但程序集在编译时使用元数据,该元数据与每个内容文件建立关联。

当应用程序需要一组特定的应用程序数据文件时,应使用内容文件,而你希望能够更新这些文件,而无需重新编译使用这些文件的程序集。

配置内容文件

若要将内容文件添加到项目,必须将应用程序数据文件作为 Content 项包含在内。 此外,由于内容文件未直接编译到程序集中,因此需要设置 MSBuild CopyToOutputDirectory 元数据元素,以指定将内容文件复制到相对于生成程序集的位置。 如果您希望每次构建项目时将资源复制到生成输出文件夹,请将 CopyToOutputDirectory 元数据元素设置为 Always 值。 否则,可以使用 PreserveNewest 值确保仅将最新版本的资源复制到生成输出文件夹。

下面显示了一个文件,该文件配置为内容文件,仅当将新版本的资源添加到项目时,才会复制到生成输出文件夹。

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ... >  
  ...  
  <ItemGroup>  
    <Content Include="ContentFile.xaml">  
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>  
    </Content>  
  </ItemGroup>  
  ...  
</Project>  

注意

在 Visual Studio 中,通过将文件添加到项目并将其 Build Action 设置为 Content,并将其 Copy to Output Directory 设置为 Copy always(与 Always相同)和 Copy if newer(与 PreserveNewest相同)来创建内容文件。

生成项目时,会将 AssemblyAssociatedContentFileAttribute 属性编译到每个内容文件的程序集的元数据中。

[assembly: AssemblyAssociatedContentFile("ContentFile.xaml")]

AssemblyAssociatedContentFileAttribute 的值表示内容文件的路径相对于其在项目中的位置。 例如,如果内容文件位于项目子文件夹中,则其他路径信息将合并到 AssemblyAssociatedContentFileAttribute 值中。

[assembly: AssemblyAssociatedContentFile("Resources/ContentFile.xaml")]

AssemblyAssociatedContentFileAttribute 值也是生成输出文件夹中内容文件的路径的值。

使用内容文件

若要加载内容文件,可以调用 Application 类的 GetContentStream 方法,传递标识所需内容的包 URI。 GetContentStream 返回一个 StreamResourceInfo 对象,该对象将内容文件公开为 Stream 并描述其内容类型。

例如,以下代码演示如何使用 GetContentStream 加载 Page 内容文件并将其设置为 FramepageFrame)的内容。

// Navigate to xaml page
Uri uri = new Uri("/PageContentFile.xaml", UriKind.Relative);
StreamResourceInfo info = Application.GetContentStream(uri);
System.Windows.Markup.XamlReader reader = new System.Windows.Markup.XamlReader();
Page page = (Page)reader.LoadAsync(info.Stream);
this.pageFrame.Content = page;
' Navigate to xaml page
Dim uri As New Uri("/PageContentFile.xaml", UriKind.Relative)
Dim info As StreamResourceInfo = Application.GetContentStream(uri)
Dim reader As New System.Windows.Markup.XamlReader()
Dim page As Page = CType(reader.LoadAsync(info.Stream), Page)
Me.pageFrame.Content = page

虽然调用 GetContentStream 可以访问 Stream,但你需要额外进行将其转换为要设置的属性类型的工作。 相反,你可以让 WPF 负责打开和转换 Stream,方法是使用代码将资源文件直接加载到类型的属性中。

以下示例演示如何使用代码将 Page 直接加载到 FramepageFrame)。

Uri pageUri = new Uri("/PageContentFile.xaml", UriKind.Relative);
this.pageFrame.Source = pageUri;
Dim pageUri As New Uri("/PageContentFile.xaml", UriKind.Relative)
Me.pageFrame.Source = pageUri

以下示例是前一个示例的等效标记。

<Frame Name="pageFrame" Source="PageContentFile.xaml" />

源文件站点

资源文件与它们一起分发的程序集具有明确的关系,这种关系是由 AssemblyAssociatedContentFileAttribute所定义的。 但是,有时你可能想要在程序集与应用程序数据文件之间建立隐式或不存在的关系,包括:

  • 编译时不存在文件。

  • 在运行时之前,你不知道程序集需要哪些文件。

  • 你希望能够更新文件,而无需重新编译与之关联的程序集。

  • 应用程序使用大型数据文件(如音频和视频),并且仅希望用户选择下载这些文件。

可以使用传统 URI 方案(如 file:///http:// 方案)加载这些类型的文件。

<Image Source="file:///C:/DataFile.bmp" />
<Image Source="http://www.datafilewebsite.com/DataFile.bmp" />

但是,file:///http:// 方案要求您的应用程序具备完全信任。 如果应用程序是从 Internet 或 Intranet 启动的 XAML 浏览器应用程序(XBAP),并且仅请求从这些位置启动的应用程序所允许的权限集,则松散文件只能从应用程序的源站点(启动位置)加载。 这些文件被称为 来源站点 文件。

原始文件站点是部分信任应用程序的唯一选项,虽然不限于部分信任应用程序。 完全信任应用程序可能需要加载在生成时不知道的应用程序数据文件;尽管完全信任应用程序可以使用 file:///,但应用程序数据文件可能安装在应用程序程序集所在的同一文件夹中或子文件夹中。 在这种情况下,使用原点引用比使用 file:/// 更容易,因为使用 file:/// 要求你找出文件的完整路径。

注意

源站文件不会在客户端计算机上使用 XAML 浏览器应用程序(XBAP)缓存,而内容文件则会缓存。 因此,它们仅在特别请求时下载。 如果 XAML 浏览器应用程序(XBAP)应用程序具有大型媒体文件,则将其配置为源文件站点意味着初始应用程序启动速度要快得多,并且文件仅按需下载。

配置源文件的站点

如果源文件站点在编译时不存在或未知,则需要使用传统的部署机制来确保在运行时提供所需的文件,包括使用 XCopy 命令行程序或 Microsoft Windows Installer。

如果在编译时知道要位于源站点的文件,但仍希望避免显式依赖项,则可以将这些文件作为 None 项添加到 MSBuild 项目。 与内容文件一样,需要设置 MSBuild CopyToOutputDirectory 属性,以通过指定 Always 值或 PreserveNewest 值,将源文件的站点复制到相对于生成程序集的位置。

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ... >  
  ...  
  <None Include="PageSiteOfOriginFile.xaml">  
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>  
  </None>  
  ...  
</Project>  

注意

在 Visual Studio 中,通过将文件添加到项目并将其 Build Action 设置为 None来创建原点文件。

生成项目后,MSBuild 会将指定的文件复制到生成输出文件夹。

使用站点来源文件

若要加载源文件的站点,可以调用 Application 类的 GetRemoteStream 方法,并传递标识源文件的所需站点的包 URI。 GetRemoteStream 返回一个 StreamResourceInfo 对象,该对象将源文件的位置显示为 Stream,并描述其内容类型。

例如,以下代码演示如何使用 GetRemoteStream 加载源文件的 Page 站点并将其设置为 FramepageFrame) 的内容。

// Navigate to xaml page
Uri uri = new Uri("/SiteOfOriginFile.xaml", UriKind.Relative);
StreamResourceInfo info = Application.GetRemoteStream(uri);
System.Windows.Markup.XamlReader reader = new System.Windows.Markup.XamlReader();
Page page = (Page)reader.LoadAsync(info.Stream);
this.pageFrame.Content = page;
' Navigate to xaml page
Dim uri As New Uri("/SiteOfOriginFile.xaml", UriKind.Relative)
Dim info As StreamResourceInfo = Application.GetRemoteStream(uri)
Dim reader As New System.Windows.Markup.XamlReader()
Dim page As Page = CType(reader.LoadAsync(info.Stream), Page)
Me.pageFrame.Content = page

尽管调用 GetRemoteStream 可使你访问 Stream,但你还需要执行进一步的工作,将其转换为要设置的属性类型。 相反,你可以让 WPF 负责打开和转换 Stream,方法是使用代码将资源文件直接加载到类型的属性中。

以下示例演示如何使用代码将 Page 直接加载到 FramepageFrame)。

Uri pageUri = new Uri("pack://siteoforigin:,,,/SiteOfOriginFile.xaml", UriKind.Absolute);
this.pageFrame.Source = pageUri;
Dim pageUri As New Uri("pack://siteoforigin:,,,/Subfolder/SiteOfOriginFile.xaml", UriKind.Absolute)
Me.pageFrame.Source = pageUri

以下示例是与前面例子具有相同标记的示例。

<Frame Name="pageFrame" Source="pack://siteoforigin:,,,/SiteOfOriginFile.xaml" />

修改构建类型后重新构建

更改应用程序数据文件的生成类型后,需要重新生成整个应用程序以确保应用这些更改。 如果仅仅构建应用程序,则不会应用更改。

另请参阅

  • WPF 中的 包 URI