Introduction
Using embedded resources with ASP.NET 2.0 has made deploying
your server controls easier than ever. First, we will address some issues that
need clarification when using embedded resources. Then, it will be time to
leverage this power and its inherent flexibility to easily customize the look
and feel of those controls for each of your sites.
Clarifications
1.
You can only embed resources in projects that have a dll as their
ultimate output. Thus, you cannot embed resources in your web project, unless
you are using the new Web Application
Project.
2.
Just because you have a rendered WebResourceUrl does not mean that the
embedded resource was actually found. Unfortunately, GetWebResourceUrl will
return a URL whether it finds the embedded resource in your assembly or not.
You will not really know that it is not finding the embedded item, unless you
are watching the HttpHeaders and see "404 Not Found" returned on the
WebResource.axd requests when the item is not found.
3.
You must include the default namespace in the name reference to the
embedded item. This means that if your default namespace for the assembly is
MyWebAppAssembly and your embedded item's name is MyImage.jpg, you must refer
to it in both the AssemblyInfo and the code as "MyWebAppAssembly.MyImage.jpg."
(They are case sensitive, so watch that also.)
Embedding Resources in Embedded Resources
If you want to embed a resource that references other
embedded resource (for example, images in a stylesheet), you need to add the
PerformSubstitution tag to your AssemblyInfo.cs, as shown in Listing 1. Then
you can reference other embedded resources, as shown in Listing 2.
Listing 1 - AssemblyInfo.cs entries
[assembly: WebResource("MyWebAppAssembly.MyStylesheet.css","text/css", PerformSubstitution = true)]
[assembly: WebResource("MyWebAppAssembly.MyImage.gif","img/gif")]
[assembly: WebResource("MyWebAppAssembly.MyComponent.htc","text/component")]
Listing 2 - MyStylesheet.css Snippet
.MyDiv { background-image: url('<% =WebResource("MyWebAppAssembly.MyImage.gif ") %>'); }
Aside: A colleague of mine was
embedding a behavior for his server control and discovered that to get the .htc
file to behave correctly he had to set the content type to text/component, as
shown at the bottom of Listing 1.
Overriding Embedded Resources
Scenario: You have developed a
really nice server control that provides a consistent look and feel for the
applications that you developed and your latest client loves the functionality,
but not the appearance.
Solution: Leverage the Type
parameter passed to GetWebResourceUrl to provide skinning. Listing 3 provides
one example of how you could do this. There is a lot going on in this listing,
but it breaks down as follows:
1.
Load the type of the current assembly, which will be the default source
for the embedded resources.
2.
Check for an overriding entry in the app's web.config.
3.
Parse out the base namespace of the assembly.
4.
Define the template for the stylesheet link and build the Web Resource
URL.
5.
Combine the two things from step 4 and add the entry to the Page's Head
tag area.
Listing 3 - Overriding Embedded Resources
private void LoadStylesheetLink()
{
Type uiResourceType = this.GetType();
string overrideType =ConfigurationManager.AppSettings[
"MyWebAppAssembly.EmbeddedResourceType"];
if (overrideType != null)
uiResourceType = Type.GetType(overrideType);
string assemblyName =uiResourceType.Assembly.FullName;
string assemblyBaseName =assemblyName.Substring(0, assemblyName.IndexOf(","));
string includeTemplate = "<linkrel='stylesheet' type='text/css' href='{0}' />";
string webResourceUrl =thePage.ClientScript.GetWebResourceUrl(uiResourceType,
assemblyBaseName +".MyStylesheet.css");
((System.Web.UI.HtmlControls.HtmlHead)thePage.Header).Controls.Add(new
LiteralControl(String.Format(includeTemplate, webResourceUrl)));
}
Embedded Resource Locations
All of the examples that I have supplied presume that the embedded
resource resides in the root of the assembly and would be compiled with the
name format, MyWebAppAssembly.MyEmbeddedResource. Your resources do not have
to be in the root and can exist in any folder in the project, but you must
remember that the name will change to
MyWebAppAssembly.MyFolderHeirarchy.MyEmbeddedResource and all references to
that resource will need to be changed as well, both in code and in the
AssemblyInfo.
Embedded Resource Recommendations
The "this feels right" part of me says that all
embedded resources should be located in the closest possible proximity to the
control that consumes them. The "minimum fuss" part of me says that
any embedded resource that you plan to override should be stored in the root of
the assembly. So, I guess that it comes down to whether or not you are going
to allow people who will be able to know the folder structure of your control
assembly to override your embedded resources. Also, I would consider long and
hard whether or not to allow anyone to override anything other than the
appearance elements of the control. I do not feel comfortable allowing someone
to override any embedded JavaScript that my control might reference.
Suggested Readings
Embedding Resources
in ASP.NET 2.0 Assemblies - Part 1
Conclusion
Now, with a few clarifications and some new tricks, we are
ready to develop skinnable, web server controls that should reduce our
development time and improve client satisfaction, which is what we are really
all about.