WPF 中的打包 URI

在 Windows Presentation Foundation(WPF)中,统一资源标识符(URI)用于以多种方式标识和加载文件,其中包括:

  • 指定用户界面(UI)以在应用程序首次启动时显示。

  • 加载图像。

  • 导航至页面。

  • 加载非可执行数据文件。

此外,URI 还可用于识别和加载来自各种位置的文件,包括:

  • 当前程序集。

  • 被引用的程序集。

  • 相对于程序集的位置。

  • 应用程序的源站点。

为了提供一致的机制来识别和加载这些类型的文件,WPF 利用 包 URI 方案的扩展性。 本主题概述方案,介绍如何为各种方案构造包 URI,讨论绝对 URI 和相对 URI 解析,然后再演示如何使用标记和代码中的包 URI。

包 URI 协议

包 URI 方案由 开放打包约定 (OPC)规范使用,该规范描述了用于组织和标识内容的模型。 此模型的关键元素是包和部件,其中 是一个或多个逻辑 部件的逻辑容器。 下图说明了此概念。

包和部件图示

为了标识部件,OPC 规范利用 RFC 2396(统一资源标识符(URI):泛型语法)的扩展性来定义包 URI 方案。

由 URI 指定的方案由其前缀定义;http、ftp 和文件是众所周知的示例。 包 URI 方案使用“pack”作为其方案,并包含两个组件:权限和路径。 下面是包 URI 的格式。

pack://authority/路径

颁发机构指定部件包含的包类型,而路径指定部件在包中的位置。

下图说明了此概念:

包、权限和路径之间的关系

包和部件类似于应用程序和文件,其中应用程序(包)可以包含一个或多个文件(部件),包括:

  • 编译到本地程序集的资源文件。

  • 被编译成引用程序集的资源文件。

  • 编译为引用程序集的资源文件。

  • 内容文件。

  • 源文件的站点。

若要访问这些类型的文件,WPF 支持两个权限:application:/// 和 siteoforigin:///。 application:/// 机构标识编译时已知的应用程序数据文件,包括资源和内容文件。 siteoforigin:/// 机构标识源文件的站点。 下图显示了每个颁发机构的范围。

包 URI 示意图

注释

包 URI 的权限组件是一个指向包的嵌入式 URI,必须符合 RFC 2396。 此外,“/”字符必须替换为“,”字符,而保留字符如“%”和“?”必须转义。 有关详细信息,请参阅 OPC。

以下部分介绍如何结合使用这两个颁发机构构建包 URI,以及用于标识源文件的资源、内容和站点的适当路径。

资源文件集合 URI

资源文件被配置为 MSBuild Resource 项,并编译为程序集。 WPF 支持构建包 URI,这些 URI 可以用于标识已编译到本地程序集中的资源文件,或编译到从本地程序集引用的程序集中的资源文件。

本地程序集资源文件

编译到本地程序集的资源文件的包 URI 使用以下权限和路径:

  • 颁发机构:application:///。

  • 路径:资源文件的名称(包括其路径)相对于本地程序集项目文件夹根目录。

以下示例显示了位于本地程序集项目文件夹根目录中的 XAML 资源文件的包 URI。

pack://application:,,,/ResourceFile.xaml

以下示例显示了位于本地程序集项目文件夹的子文件夹中的 XAML 资源文件的包 URI。

pack://application:,,,/Subfolder/ResourceFile.xaml

引用的程序集资源文件

编译到引用程序集的资源文件的包 URI 使用如下权限和路径:

  • 权限:application:///.

  • 路径:编译为引用程序集的资源文件的名称。 路径必须符合以下格式:

    AssemblyShortName{;版本]{;公钥];component/路径

    • AssemblyShortName:所引用程序集的短名称。

    • ;版本 [可选]:包含资源文件的引用程序集的版本。 在加载两个或多个具有相同短名称的引用程序集时,会使用此方法。

    • ;PublicKey [可选]:用于对引用的程序集进行签名的公钥。 当加载具有相同短名称的两个或多个引用程序集时,将使用此工具。

    • ;组件:指定所引用的程序集是从本地程序集引用的。

    • /Path:资源文件的名称,包括其路径,相对于所引用程序集的项目文件夹的根目录。

以下示例显示了位于所引用程序集项目文件夹根目录中的 XAML 资源文件的包 URI。

pack://application:,,,/ReferencedAssembly;component/ResourceFile.xaml

以下示例显示了位于所引用程序集项目文件夹的子文件夹中的 XAML 资源文件的包 URI。

pack://application:,,,/ReferencedAssembly;component/Subfolder/ResourceFile.xaml

以下示例显示了一个包 URI,该 URI 指向在引用的特定版本程序集项目文件夹根目录中的 XAML 资源文件。

pack://application:,,,/ReferencedAssembly;v1.0.0.1;component/ResourceFile.xaml

请注意,引用的程序集资源文件的包 URI 语法只能与 application:/// 权限部分一起使用。 例如,WPF 不支持以下各项。

pack://siteoforigin:,,,/SomeAssembly;component/ResourceFile.xaml

内容文件包 URI

内容文件的包 URI 使用以下颁发机构和路径:

  • 颁发机构:application:///。

  • 路径:内容文件的名称,包括其相对于应用程序主可执行程序集的文件系统位置的路径。

以下示例显示了 XAML 内容文件的包 URI,该文件位于与可执行程序集位于同一文件夹中。

pack://application:,,,/ContentFile.xaml

以下示例显示了 XAML 内容文件的包 URI,该文件位于相对于应用程序的可执行程序集的子文件夹中。

pack://application:,,,/Subfolder/ContentFile.xaml

注释

HTML 内容文件无法访问。 URI 方案仅支持导航到位于源站点的 HTML 文件。

源包 URI 站点

原网站文件的包 URI 使用以下权限和路径:

  • 权限:siteoforigin:///.

  • 路径:源文件站点的名称,包括其相对于启动可执行文件程序集的位置的路径。

以下示例显示了原始站点文件的 XAML 包 URI,该文件存储在启动可执行程序集的位置。

pack://siteoforigin:,,,/SiteOfOriginFile.xaml

以下示例显示了 XAML 原始文件的包 URI,该文件存储在一个子文件夹中,该子文件夹相对于启动应用程序的可执行程序集的位置。

pack://siteoforigin:,,,/Subfolder/SiteOfOriginFile.xaml

页面文件

配置为 MSBuild Page 项的 XAML 文件会像资源文件一样被编译到程序集。 因此,可以使用资源文件的包的 URI 来标识 MSBuild Page 项。

通常配置为 MSBuildPage 项的 XAML 文件类型具有以下根元素之一:

绝对 URI 与相对包 URI

完全限定的包 URI 包括方案、颁发机构和路径,并被视为绝对包 URI。 作为对开发人员的简化,XAML 元素通常允许您使用相对 pack URI 设置适当的属性,其中仅包括路径。

例如,请考虑本地程序集中资源文件的以下绝对包 URI。

pack://application:,,,/ResourceFile.xaml

引用此资源文件的相对包 URI 如下所示。

/ResourceFile.xaml

注释

由于源文件的站点不与程序集关联,因此只能使用绝对包 URI 来引用它们。

默认情况下,相对包 URI 被认为是相对于包含引用的标记或代码的位置。 但是,如果使用前导反斜杠,则相对包 URI 引用将被视为相对于应用程序的根目录。 例如,请考虑以下项目结构。

App.xaml

Page2.xaml

\SubFolder

+ Page1.xaml

+ Page2.xaml

如果 Page1.xaml 包含引用 Root\SubFolder\Page2.xaml 的 URI,则引用可以使用以下相对包 URI。

Page2.xaml

如果 Page1.xaml 包含引用 Root\Page2.xaml 的 URI,则引用可以使用以下相对包 URI。

/Page2.xaml

包 URI 解析

包 URI 的格式使不同类型文件的包 URI 看起来可以相同。 例如,请考虑以下绝对包 URI。

pack://application:,,,/ResourceOrContentFile.xaml

此绝对包 URI 可以引用本地程序集或内容文件中的资源文件。 对于以下的相对 URI,情况也是一样。

/ResourceOrContentFile.xaml

为了确定包 URI 引用的文件类型,WPF 使用以下启发式解析本地程序集和内容文件中资源文件的 URI:

  1. 探测程序集元数据中的AssemblyAssociatedContentFileAttribute属性,以匹配包 URI。

  2. 如果找到该 AssemblyAssociatedContentFileAttribute 属性,则包 URI 的路径引用内容文件。

  3. 如果未找到该 AssemblyAssociatedContentFileAttribute 属性,请查看编译到本地程序集中的资源文件集。

  4. 如果找到与包 URI 路径匹配的资源文件,则包 URI 的路径引用资源文件。

  5. 如果未找到资源,则内部创建 Uri 的资源无效。

URI 解析不适用于引用以下内容的 URI:

  • 被引用的程序集中的内容文件:WPF 不支持这些文件类型。

  • 引用程序集中的嵌入文件:标识它们的 URI 是唯一的,因为它们包括所引用程序集的名称和 ;component 后缀。

  • 源文件站点:标识它们的 URI 是唯一的,因为它们是唯一可由包含 siteoforigin:/// 机构的包 URI 标识的文件。

包 URI 解析允许的一种简化是使代码在资源和内容文件的位置上更具独立性。 例如,如果本地程序集中有一个资源文件,该文件重新配置为内容文件,则资源的包 URI 保持不变,就像使用包 URI 的代码一样。

使用包 URI 编程

许多 WPF 类实现可以使用包 URI 设置的属性,包括:

可以从标记和代码设置这些属性。 本部分演示这两者的基本构造,然后演示常见方案的示例。

在标记中使用包 URI

通过在标记中设置具有包 URI 的属性来指定包 URI。 例如:

<element attribute="pack://application:,,,/File.xaml" />

表 1 说明了可以在标记中指定的各种绝对包 URI。

表 1:标记中的绝对包 URI

文件 绝对包 URI
资源文件 - 本地程序集 "pack://application:,,,/ResourceFile.xaml"
子文件夹中的资源文件 - 本地程序集 "pack://application:,,,/Subfolder/ResourceFile.xaml"
资源文件 - 引用的程序集 "pack://application:,,,/ReferencedAssembly;component/ResourceFile.xaml"
引用程序集的子文件夹中的资源文件 "pack://application:,,,/ReferencedAssembly;component/Subfolder/ResourceFile.xaml"
版本化引用的程序集中的资源文件 "pack://application:,,,/ReferencedAssembly;v1.0.0.0;component/ResourceFile.xaml"
内容文件 "pack://application:,,,/ContentFile.xaml"
子文件夹中的内容文件 "pack://application:,,,/Subfolder/ContentFile.xaml"
源文件站点 "pack://siteoforigin:,,,/SOOFile.xaml"
子文件夹中的源文件站点 "pack://siteoforigin:,,,/Subfolder/SOOFile.xaml"

表 2 展示了在标记中可以指定的各种不同的相对包 URI。

表 2:标记中的相对包 URI

文件 相对包 URI
本地程序集中的资源文件 "/ResourceFile.xaml"
本地程序集的子文件夹中的一个资源文件 "/Subfolder/ResourceFile.xaml"
引用程序集中的资源文件 "/ReferencedAssembly;component/ResourceFile.xaml"
引用程序集的子文件夹中的资源文件 "/ReferencedAssembly;component/Subfolder/ResourceFile.xaml"
内容文件 "/ContentFile.xaml"
子文件夹内的内容文件 "/Subfolder/ContentFile.xaml"

在代码中使用打包 URI

通过在代码中实例化 Uri 类并将包 URI 作为参数传递给构造函数来指定包 URI。 以下示例对此进行了演示。

Uri uri = new Uri("pack://application:,,,/File.xaml");

默认情况下,该 Uri 类将包 URI 视为绝对 URI。 因此,当使用相对包 URI 创建 Uri 类的实例时,会抛出异常。

Uri uri = new Uri("/File.xaml");

幸运的是, Uri(String, UriKind) 类构造函数的 Uri 重载接受类型 UriKind 参数,以便指定包 URI 是绝对 URI 还是相对 URI。

// Absolute URI (default)
Uri absoluteUri = new Uri("pack://application:,,,/File.xaml", UriKind.Absolute);
// Relative URI
Uri relativeUri = new Uri("/File.xaml",
                        UriKind.Relative);

当您确定提供的包 URI 是其中之一或另一个时,您应仅指定 AbsoluteRelative。 如果不知道使用的包 URI 类型,例如当用户在运行时输入包 URI 时,请改用 RelativeOrAbsolute

// Relative or Absolute URI provided by user via a text box
TextBox userProvidedUriTextBox = new TextBox();
Uri uri = new Uri(userProvidedUriTextBox.Text, UriKind.RelativeOrAbsolute);

表 3 说明了您可以在代码中使用 System.Uri 指定的各种相对包 URI。

表 3:代码中的绝对包 URI

文件 绝对包 URI
资源文件 - 本地程序集 Uri uri = new Uri("pack://application:,,,/ResourceFile.xaml", UriKind.Absolute);
子文件夹中的资源文件 - 本地程序集 Uri uri = new Uri("pack://application:,,,/Subfolder/ResourceFile.xaml", UriKind.Absolute);
资源文件 - 引用的程序集 Uri uri = new Uri("pack://application:,,,/ReferencedAssembly;component/ResourceFile.xaml", UriKind.Absolute);
引用程序集的子文件夹中的资源文件 Uri uri = new Uri("pack://application:,,,/ReferencedAssembly;component/Subfolder/ResourceFile.xaml", UriKind.Absolute);
版本化引用的程序集中的资源文件 Uri uri = new Uri("pack://application:,,,/ReferencedAssembly;v1.0.0.0;component/ResourceFile.xaml", UriKind.Absolute);
内容文件 Uri uri = new Uri("pack://application:,,,/ContentFile.xaml", UriKind.Absolute);
子文件夹内的内容文件 Uri uri = new Uri("pack://application:,,,/Subfolder/ContentFile.xaml", UriKind.Absolute);
源文件站点 Uri uri = new Uri("pack://siteoforigin:,,,/SOOFile.xaml", UriKind.Absolute);
子文件夹中的源文件站点 Uri uri = new Uri("pack://siteoforigin:,,,/Subfolder/SOOFile.xaml", UriKind.Absolute);

表 4 说明了可以在代码 System.Uri中指定的各种相对包 URI。

表 4:代码中的相对包 URI

文件 相对包 URI
资源文件 - 本地程序集 Uri uri = new Uri("/ResourceFile.xaml", UriKind.Relative);
子文件夹中的资源文件 - 本地程序集 Uri uri = new Uri("/Subfolder/ResourceFile.xaml", UriKind.Relative);
资源文件 - 引用的程序集 Uri uri = new Uri("/ReferencedAssembly;component/ResourceFile.xaml", UriKind.Relative);
子文件夹中的资源文件 - 被引用的程序集 Uri uri = new Uri("/ReferencedAssembly;component/Subfolder/ResourceFile.xaml", UriKind.Relative);
内容文件 Uri uri = new Uri("/ContentFile.xaml", UriKind.Relative);
子文件夹内的内容文件 Uri uri = new Uri("/Subfolder/ContentFile.xaml", UriKind.Relative);

常见包 URI 使用场景

前面的部分讨论了如何构造包 URI 来标识源文件的资源、内容和站点。 在 WPF 中,这些构造以多种方式使用,以下部分介绍了几种常见用法。

指定要在应用程序启动时显示的 UI

StartupUri 指定要在启动 WPF 应用程序时显示的第一个 UI。 对于独立应用程序,UI 可以是窗口,如以下示例所示。

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

独立应用程序和 XAML 浏览器应用程序(XBAP)还可以将页面指定为初始 UI,如以下示例所示。

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

如果应用程序是独立的应用程序,并且指定了 StartupUri一个页面,则 WPF 将打开一个 NavigationWindow 用于托管页面的页。 对于 XBAP,页面将显示在主机浏览器中。

以下示例演示如何导航到页面。

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  WindowTitle="Page With Hyperlink"
  WindowWidth="250"
  WindowHeight="250">
<Hyperlink NavigateUri="UriOfPageToNavigateTo.xaml">
  Navigate to Another Page
</Hyperlink>
</Page>

有关在 WPF 中导航的各种方法的详细信息,请参阅 导航概述

指定窗口图标

以下示例演示如何使用 URI 指定窗口的图标。

<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.MainWindow"
    Icon="WPFIcon1.ico">
</Window>

有关详细信息,请参阅 Icon

加载图像、音频和视频文件

WPF 允许应用程序使用各种媒体类型,所有这些类型都可以使用包 URI 进行标识和加载,如以下示例所示。

<MediaElement Stretch="Fill" LoadedBehavior="Play" Source="pack://siteoforigin:,,,/Media/bee.wmv" />
<MediaElement Stretch="Fill" LoadedBehavior="Play" Source="pack://siteoforigin:,,,/Media/ringin.wav" />
<Image Source="Images/Watermark.png" />

有关使用媒体内容的详细信息,请参阅 图形和多媒体

从源站点加载资源字典

资源字典(ResourceDictionary)可用于支持应用程序主题。 创建和管理主题的一种方法是创建多个主题作为位于应用程序原点的资源字典。 这样就可以添加和更新主题,而无需重新编译和重新部署应用程序。 可以使用包 URI 识别和加载这些资源字典,如以下示例所示。

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    StartupUri="HomePage.xaml">
  <Application.Resources>
    <ResourceDictionary Source="pack://siteoforigin:,,,/PageTheme.xaml" />
  </Application.Resources>
</Application>

有关 WPF 中主题的概述,请参阅 样式设置和模板化

另请参阅