Even a chimp can write code

Saturday, November 05, 2005

Resources in Windows Presentation Foundation applications

I've been wanting to blog about the application files and resources story in WPF, but couldn't get to it thus far. Recently I responded to a newsgroup question on how WPF (Avalon) handles resources, and figured that would make a good blog post until the WinFX SDK content for this goes online.

Update [Apr 2009]: If you came here looking for resources and URIs in Silverlight, then you will find this post helpful. Note that Silverlight does not support pack URIs. If you're curious about resources in WPF or even as a historical perspective of what we enabled in the resource resolution and loading space in WPF before we created the subset in Silverlight, then read on.

Resources are non-executable data deployed with an application. They may take the form of images displayed in the UI or message strings for error messages. The .NET Framework provides built-in support for creating, localizing, packaging and deploying resources. You can create resources in a .resx or a .resources file. The .resx resource file format consists of XML entries to specify objects and strings. If your application has an image, the binary form of that image is serialized into the .resx file. But although a .resx file can be edited via a text editor, it cannot directly be added to an application executable or satellite assembly. You must first convert it to a .resources file using a tool like ResGen.exe and add the .resources file into the assembly. The GenerateResource task in MSBuild handles the conversion of .resx to .resources at build time. In your WinForms project, you'd specify the EmbeddedResource build action for .resx files.
For e.g.

<EmbeddedResource Include="myResources.resx"/>

How we do it in Avalon
In Windows Presentation Foundation, the resource definition and loading model is different from the WinForms model. First, we do not require you to create .resx files. You merely indicate your resources in the project file and the WPF build system will take care of the rest. Second, unlike with .NET resources which have resource ids, in WPF you need to reference a resource in XAML via a Uri. WPF also supports the multi-lingual user interface (MUI) assembly model where language-neutral resources go into the main assembly and language-specific resources go into corresponding language-specific satellite assemblies. For these purposes, a new set of resource build actions were created. These are the Resource and Content build actions. Both these build actions target application files that are known at compile time.

If a file is denoted 'Resource' in the project file, upon build, that file is embedded into the application assembly. Whether main or satellite assembly, depends on whether you've set the 'Localizable' meta-data. The default is main assembly.
For e.g.

<Resource Include="AmateurDictators/RoderickSpode.jpg"/>

The ResourcesGenerator task in the WPF MSBuild system (in Microsoft.WinFX.targets) creates a single .resources file for all Resources specified in the project. In a localized application, the application will load the resource from the appropriate satellite assembly based on the locale of the computer where it is running. In a future post I will talk about the resource fallback logic in WPF.

If a file is denoted as 'Content' with the appropriate 'CopyToOutputDirectory' meta-data set to 'Always' or 'PreserveNewest', the file is treated as loose application content and is copied to the build output directory, residing alongside the application assembly.
For e.g.

<Content Include="Drones/GussyFink-Nottle.png">

Regardless of whether you denote a file as 'Resource' or 'Content', you can access it in markup and code via a simple relative Uri. For e.g.

<Image Source="AmateurDictators/RoderickSpode.jpg" />
<Image Source="Drones/GussyFink-Nottle.png" />

During build, a mapping of all Content files in an application is created. At runtime, when confronted with a relative Uri, the loading mechanism knows if the object is a loose Content file or a Resource embedded in the application assembly.

Files at site of origin
If you do not want to be bound by the restrictions of having your application resources declared at compile time, there is another option for you. No, this doesn't involve using fully qualified Uris to reference resources over the internet. Although, that is indeed supported. WPF provides you with an abstraction for the application's conceptual site of origin i.e. the location from where the application was deployed. For instance, if your application was launched from http://nerddawg.blogspot.com, then your application's site of origin is http://nerddawg.blogspot.com. To access an image at images/AuntDahlia.gif at that location, you would specify in markup:

<Image Source="pack://siteoforigin:,,,/images/AuntDahlia.jpg" />

You can use the same Uri in code. For more on the Pack Uri syntax, see the Appendix in the XPS spec. I will provide more details in a future post.

Choosing one over the other
You would choose to mark a file as Resource if:

  • your file is localizable i.e. images with text in them, fonts or text files

  • you don't expect to replace the file once the application has been built

  • you don't want to deal with the task of managing and deploying the file separately from the application i.e. you want to be able to move the application around without having to worry about moving several associated files

And, you would choose to mark a file as Content if:

  • your file is not localizable

  • you want the flexibility of replacing the file after the application assemblies have been built

  • you want to be able to have the file download on demand (i.e. not with the application assembly)

  • WPF cannot load a file of this type when it is embedded into the application assembly e.g. HTML content rendered in a Frame, media files

Finally, you would choose the site of origin notation if:

  • your file is not localizable

  • you want the flexibility of replacing the file after the application assemblies have been built

  • you want to be able to have the file downloaded only when requested

  • you don't know the contents of the file (or you don't really have the file) when you build the application

  • your file is very large and you don't want that to affect the application download time e.g. large media files

  • WPF cannot load a file of this type when it is embedded into the application assembly e.g. HTML content rendered in a Frame, media files

There is currently no support for strongly typed 'Resource' and 'Content' files in the WPF project templates. 'EmbeddedResources' are supported, however, they cannot be referenced via Uris.


Email this | Bookmark this


Post a Comment | Home | Inference: my personal blog