Silverlight 2: Demystifying URI references for app resources
In this post we will look at how application data files can be referenced using relative Uris from within XAML or code in your Silverlight 2 application.
Silverlight provides you with a luxury of choices for how you declare your application data files. These are the non-code artifacts of your app, such as images, videos, fonts etc. The build action for any file - i.e. how you declare it in your project file or within Visual Studio or Blend - is consequential to how it can be referenced in XAML or code via a Uri. While care was taken to ensure defaults that gel with the programmers intuitiveness while referencing them in Uris, there are always subtle issues that the intrepid Silverlight designer or developer must be aware of. If you come to Silverlight with previous WPF experience, the contents of this post should be old news to you. If not, fear not; Silverlight is designed to be easy to learn and easy to master.
Let's take Image files as an example as we walk through the various cases. Once you understand how this works, you can extrapolate behavior for MediaElement (audio/video) or MultiScaleImage (SeaDragon in Silverlight) or perhaps even fonts. While examples below show XAML syntax, the same applies to code.
Files marked as Resource:
- Get embedded in the app assembly within the XAP
- Are accessible using a relative Uri, relative to the XAML file from where they are being referenced
For e.g.<Image Source=”Foo.jpg” />
- When not found, there is no fallback. The ImageFailed event is raised.
Tip: Using assembly qualified Uris is most reliable both from within and outside that assembly. E.g. <Image Source=”/{assemblyShortName};component/Foo.jpg”/>
Tip: You can programmatically extract any application resource using Application.GetResourceStream(uri).Stream
.
Files marked as Content:
- Get added to the XAP at the conceptual application root
- Are accessible using a relative Uri, relative to the application root. This requires the use of a leading slash
For e.g.<Image Source=”/Foo.jpg” />
- When not found, the resource loading mechanism falls back to the application’s site of origin and looks for the image under the same (web root) dir as the XAP is located
- When not found, the ImageFailed event is raised.
Tip: this is a no-brainer when you have a resource that is commonly used from within 2 assemblies in the XAP
Files marked as None (with CopyToOutputDirectory set appropriately):
- Don’t get copied into the XAP, but the CopyToOutputDirectory metadata will ensure it gets copied alongside the XAP
- Are accessible using a relative Uri, relative to the application root. This requires the use of a leading slash
For e.g.<Image Source=”/Foo.jpg” />
- This is the fallback path of the above
Tip: in most cases you want to keep your video files out of the XAP. They are not encumbered by x-domain download restrictions. You do not get compression benefits from keeping them in the XAP. Rather you can use streaming or progressive download if they are outside.
Files marked as None, as well as image and audio/video files not marked at all in the project (i.e. external to the project), can always be referenced using absolute Uris.
Files marked as EmbeddedResource:
- Get embedded into the assembly in a manner that Silverlight cannot use for URI references either from XAML or code
- Should be changed to Resource, and used as detailed above.
Hopefully this demystifies Uri usage in Silverlight for you. If we can make improvements in the runtime or in tools to make this easier for you, please post a comment here. Feedback is always appreciated.
Related links:
- Resources in WPF - Part 1
- Resources in WPF - Part 2
- Silverlight resource files - documentation on MSDN
Labels: Content, EmbeddedResource, Resource, Silverlight, URI
12 Comments:
Ashish,
It would be great if there was some standard prefix like res: for resource, app: or xap: or whatever for the xap file, etc. Even after reading the exaplanations a few times I still can't get it right unless I refer back to one of these posts.
By duality, at March 12, 2008 at 7:54 PM
The problem is these are required to be URIs, and that would entail registering the app:, res: etc. as schemes. I want to stay away from that. I do hear the feedback loud and clear though. We have some work to do in making sure the default experience is intuitive. In other words, when you add an item to your project, we need to nail the build action right there, so any URI you use is intuitive. If a user decides to modify the build action, then one can assume they know what they're doing, and are aware of the nuance.
By Ashish Shetty, at March 12, 2008 at 8:43 PM
Ashish, your comment assumes the one providing the image is the same person writing the app. If the url is provided by a consumer of the app, it will still be very confusing!
By Anonymous, at March 12, 2008 at 9:50 PM
The explanation is excellent, in case you need a sample, I have one that shows most way to resolve references here.
jaimer
By Anonymous, at March 13, 2008 at 10:27 PM
Ashish;
Just as a side note, please don't forget to blog about those questions that I raise a couple of blogs ago about how an applications deals with many pages. You mentioned it going into the queue, but just wanted to touch base with you on that.
Thank you and looking forward to reading your blog!
..Ben
By Anonymous, at March 14, 2008 at 4:22 PM
Excellent post. Exactly what I needed. Found one issue though. In Visual studio when you link your web project with silverlight project it only copies the xap on building the solution. This is a bit of an issue if you have some files which you don't want to have in your xap but still want to copy it on the web server with build action none and copy always true. Like video as you mentioned. You end up manually copying the stuff..
By Anonymous, at June 25, 2008 at 11:13 AM
When referencing an XML file with Build Action set to Content, prepending a slash actually breaks things for me. Removing the leading slash works.
That would make it seem that the reference (at least for XDocument.Load) is relative to the application root *but* that the application root *isn't* the actual root (since relative URIs work, but absolute ones don't).
By Anonymous, at March 11, 2009 at 11:02 AM
A point of clarification: resources marked Content can be accessed when their URI has a leading slash, but only when they are in the application's main XAP. Content in secondary XAPs cannot get referenced. It has to be marked as a Resource and compiled into a secondary assembly. Isn't that correct?
By Anonymous, at March 11, 2009 at 4:57 PM
Jonathan, that's exactly what *didn't* work for me. My content in the main xap is not referenceable with the leading slash, only without it.
By Unknown, at March 27, 2009 at 3:35 PM
I 2nd Dave's comment: xml file marked a Content and accessed using GetResourceStream cannot have the leading / in the URI.
If you mark the xml file as Resource, then you can use the assembly-qualified URI syntax for access with GetResourceStream.
By MarkT, at April 2, 2009 at 1:26 PM
Are you certain this is correct:
"Files marked as Resource:...Are accessible using a relative Uri...When not found, there is no fallback."
In my testing, it seems like a relative URI with or without a leading slash does fall back to searching ClientBin. Thanks!
By Anonymous, at April 10, 2009 at 9:28 AM
When I do BuildAction=Content, all my controls on the page start erroring out with "not a member of...".
By Asit, at April 21, 2009 at 5:17 PM
Post a Comment | Home | Inference: my personal blog