WPF Application Resource, Content, and Data Files
Microsoft Windows applications often depend on files that contain non-executable data, such as Extensible Application Markup Language (XAML), images, video, and audio. Windows Presentation Foundation (WPF) offers special support for configuring, identifying, and using these types of data files, which are called application data files. This support revolves around a specific set of application data file types, including:
Resource Files: Data files that are compiled into either an executable or library WPF assembly.
Content Files: Standalone data files that have an explicit association with an executable WPF assembly.
Site of Origin Files: Standalone data files that have no association with an executable WPF assembly.
One important distinction to make between these three types of files is that resource files and content files are known at build time; an assembly has explicit knowledge of them. For site of origin files, however, an assembly may have no knowledge of them at all, or implicit knowledge through a pack uniform resource identifier (URI) reference; the case of the latter, there is no guarantee that the referenced site of origin file actually exists.
To reference application data files, Windows Presentation Foundation (WPF) uses the Pack uniform resource identifier (URI) Scheme, which is described in detail in Pack URIs in WPF).
This topic describes how to configure and use application data files.
This topic contains the following sections.
- Resource Files
- Content Files
- Site of Origin Files
- Rebuilding After Changing Build Type
- Related Topics
Resource Files
If an application data file must always be available to an application, the only way to guarantee availability is to compile it into an application's main executable assembly or one of its referenced assemblies. This type of application data file is known as a resource file.
You should use resource files when:
You don't need to update the resource file's content after it is compiled into an assembly.
You want to simplify application distribution complexity by reducing the number of file dependencies.
Your application data file needs to be localizable (see WPF Globalization and Localization Overview).
Note
Resource dictionaries (XAML files with ResourceDictionary as their top-level element) are not WPF resource files; while WPF resource files can be resource dictionaries, a resource dictionary does not have to be a resource file (see ResourceDictionary).
Furthermore, WPF resource files are not the same as the embedded or linked type of resources that can be configured using the core .NET Framework support for assembly resources (see Managing Application Resources). While WPF resource files do leverage the core .NET Framework embedded resource support, the ability to access WPF resource files using pack URIs is easier than using namespaces.
Configuring Resource Files
In WPF, a resource file is a file that is included in an Microsoft build engine (MSBuild) project as a Resource item.
<Project "xmlns=https://schemas.microsoft.com/developer/msbuild/2003" ... >
...
<ItemGroup>
<Resource Include="ResourceFile.xaml" />
</ItemGroup>
...
</Project>
Note
In Microsoft Visual Studio, you create a resource file by adding a file to a project and setting its Build Action to Resource.
When the project is built, MSBuild compiles the resource into the assembly.
Using Resource Files
To load a resource file, you can call the GetResourceStream method of the Application class, passing a pack URI that identifies the desired resource file. GetResourceStream returns a StreamResourceInfo object, which exposes the resource file as a Stream and describes its content type.
As an example, the following code shows how to use GetResourceStream to load a Page resource file and set it as the content of a Frame (pageFrame):
' 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
// 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;
While calling GetResourceStream gives you access to the Stream, you need to perform the additional work of converting it to the type of the property that you'll be setting it with. Instead, you can let WPF take care of opening and converting the Stream by loading a resource file directly into the property of a type using code.
The following example shows how to load a Page directly into a Frame (pageFrame) using code.
Dim pageUri As New Uri("/PageResourceFile.xaml", UriKind.Relative)
Me.pageFrame.Source = pageUri
Uri pageUri = new Uri("/PageResourceFile.xaml", UriKind.Relative);
this.pageFrame.Source = pageUri;
The following example is the markup equivalent of the preceding example.
<Frame Name="pageFrame" Source="PageResourceFile.xaml" />
Application Code Files as Resource Files
A special set of WPF application code files can be referenced using pack URIs, including windows, pages, flow documents, and resource dictionaries. For example, you can set the Application.StartupUri property with a pack URI that references the window or page that you would like to load when an application starts.
<Application
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
StartupUri="SOOPage.xaml" />
You can do this when a XAML file is included in an Microsoft build engine (MSBuild) project as a Page item.
<Project xmlns="https://schemas.microsoft.com/developer/msbuild/2003" ... >
...
<ItemGroup>
<Page Include="MainWindow.xaml" />
</ItemGroup>
...
</Project>
Note
In Visual Studio, you add a new Window, NavigationWindow, Page, FlowDocument, or ResourceDictionary to a project, the Build Action for the markup file will default to Page.
When a project with Page items is compiled, the XAML items are converted to binary format and compiled into the associated assembly. Consequently, these files can be used in the same way as typical resource files.
Note
If a XAML file is configured as a Resource item, and does not have a code-behind file, the raw XAML is compiled into an assembly rather than a binary version of the raw XAML.
Content Files
A content file is distributed as a loose file alongside an executable assembly. Although they are not compiled into an assembly, assemblies are compiled with metadata that establishes an association with each content file.
You should use content files when your application requires a specific set of application data files that you want to be able to update without recompiling the assembly that consumes them.
Configuring Content Files
To add a content file to a project, an application data file must be included as a Content item. Furthermore, because a content file is not compiled directly into the assembly, you need to set the MSBuild CopyToOutputDirectory metadata element to specify that the content file is copied to a location that is relative to the built assembly. If you want the resource to be copied to the build output folder every time a project is built, you set the CopyToOutputDirectory metadata element with the Always value. Otherwise, you can ensure that only the newest version of the resource is copied to the build output folder by using the PreserveNewest value.
The following shows a file that is configured as a content file which is copied to the build output folder only when a new version of the resource is added to the project.
<Project xmlns="https://schemas.microsoft.com/developer/msbuild/2003" ... >
...
<ItemGroup>
<Content Include="ContentFile.xaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
...
</Project>
Note
In Visual Studio, you create a content file by adding a file to a project and setting its Build Action to Content, and set its Copy to Output Directory to Copy always (same as Always) and Copy if newer (same as PreserveNewest).
When the project is built, an AssemblyAssociatedContentFileAttribute attribute is compiled into the metadata of the assembly for each content file.
[assembly: AssemblyAssociatedContentFile("ContentFile.xaml")]
The value of the AssemblyAssociatedContentFileAttribute implies the path to the content file relative to its position in the project. For example, if a content file was located in a project subfolder, the additional path information would be incorporated into the AssemblyAssociatedContentFileAttribute value.
[assembly: AssemblyAssociatedContentFile("Resources/ContentFile.xaml")]
The AssemblyAssociatedContentFileAttribute value is also the value of the path to the content file in the build output folder.
Using Content Files
To load a content file, you can call the GetContentStream method of the Application class, passing a pack URI that identifies the desired content file. GetContentStream returns a StreamResourceInfo object, which exposes the content file as a Stream and describes its content type.
As an example, the following code shows how to use GetContentStream to load a Page content file and set it as the content of a Frame (pageFrame).
' 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
// 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;
While calling GetContentStream gives you access to the Stream, you need to perform the additional work of converting it to the type of the property that you'll be setting it with. Instead, you can let WPF take care of opening and converting the Stream by loading a resource file directly into the property of a type using code.
The following example shows how to load a Page directly into a Frame (pageFrame) using code.
Dim pageUri As New Uri("/PageContentFile.xaml", UriKind.Relative)
Me.pageFrame.Source = pageUri
Uri pageUri = new Uri("/PageContentFile.xaml", UriKind.Relative);
this.pageFrame.Source = pageUri;
The following example is the markup equivalent of the preceding example.
<Frame Name="pageFrame" Source="PageContentFile.xaml" />
Site of Origin Files
Resource files have an explicit relationship with the assemblies that they are distributed alongside, as defined by the AssemblyAssociatedContentFileAttribute. But, there are times when you may want to establish either an implicit or non-existent relationship between an assembly and an application data file, including when:
A file doesn't exist when at compile time.
You don't know what files your assembly will require until run time.
You want to be able to update files without recompiling the assembly that they are associated with.
Your application uses large data files, such as audio and video, and you only want users to download them if they choose to.
It is possible to load these types of files by using traditional URI schemes, such as the file:/// and http:// schemes.
<Image Source="file:///C:/DataFile.bmp" />
<Image Source="http://www.datafilewebsite.com/DataFile.bmp" />
However, the file:/// and http:// schemes require your application to have full trust. If your application is a XAML browser application (XBAP) that was launched from the Internet or intranet, and it requests only the set of permissions that are allowed for applications launched from those locations, loose files can only be loaded from the application's site of origin (launch location). Such files are known as site of origin files.
Site of origin files are the only option for partial trust applications, although are not limited to partial trust applications. Full trust applications may still need to load application data files that they do not know about at build time; while full trust applications could use file:///, it is likely that the application data files will be installed in the same folder as, or a subfolder of, the application assembly. In this case, using site of origin referencing is easier than using file:///, because using file:/// requires you to work out the full path the file.
Note
Site of origin files are not cached with an XAML browser application (XBAP) on a client machine, while content files are. Consequently, they are only downloaded when specifically requested. If an XAML browser application (XBAP) application has large media files, configuring them as site of origin files means the initial application launch is much faster, and the files are only downloaded on demand.
Configuring Site of Origin Files
If your site of origin files are non-existent or unknown at compile time, you need to use traditional deployment mechanisms for ensuring the required files are available at run time, including using either the XCopy command-line program or the Microsoft Windows Installer.
If you do know at compile time the files that you would like to be located at the site of origin, but still want to avoid an explicit dependency, you can add those files to an Microsoft build engine (MSBuild) project as None item. As with content files, you need to set the MSBuild CopyToOutputDirectory attribute to specify that the site of origin file is copied to a location that is relative to the built assembly, by specifying either the Always value or the PreserveNewest value.
<Project xmlns="https://schemas.microsoft.com/developer/msbuild/2003" ... >
...
<None Include="PageSiteOfOriginFile.xaml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
...
</Project>
Note
In Visual Studio, you create a site of origin file by adding a file to a project and setting its Build Action to None.
When the project is built, MSBuild copies the specified files to the build output folder.
Using Site of Origin Files
To load a site of origin file, you can call the GetRemoteStream method of the Application class, passing a pack URI that identifies the desired site of origin file. GetRemoteStream returns a StreamResourceInfo object, which exposes the site of origin file as a Stream and describes its content type.
As an example, the following code shows how to use GetRemoteStream to load a Page site of origin file and set it as the content of a Frame (pageFrame).
' 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
// 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;
While calling GetRemoteStream gives you access to the Stream, you need to perform the additional work of converting it to the type of the property that you'll be setting it with. Instead, you can let WPF take care of opening and converting the Stream by loading a resource file directly into the property of a type using code.
The following example shows how to load a Page directly into a Frame (pageFrame) using code.
Dim pageUri As New Uri("pack://siteoforigin:,,,/Subfolder/SiteOfOriginFile.xaml", UriKind.Absolute)
Me.pageFrame.Source = pageUri
Uri pageUri = new Uri("pack://siteoforigin:,,,/SiteOfOriginFile.xaml", UriKind.Absolute);
this.pageFrame.Source = pageUri;
The following example is the markup equivalent of the preceding example.
<Frame Name="pageFrame" Source="pack://siteoforigin:,,,/SiteOfOriginFile.xaml" />
Rebuilding After Changing Build Type
After you change the build type of an application data file, you need to rebuild the entire application to ensure those changes are applied. If you only build the application, the changes are not applied.