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="http://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
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.
Uri pageUri = new Uri("pack://siteoforigin:,,,/Subfolder/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" />