Using ASP.NET 2.0 Web Resources in WebParts

[wait…is this a (*gulp*) technical post? sheesh…when’s the last time I actually posted dev/technical content? I was shocked to find that it was March 2005, when I posted three fairly useful items — more than a year ago! Well, then…let’s get going, and let’s get something started, eh?]

Web Resources

Way back in the far-distant past, back when all developers had to work with was ASP.NET 1.1 (lo,many ages hence), those developers who wanted to include actual resources — static files such as images, CSS, etc. — with their Web Parts would have to package up the files into an installer package to be included with the Web Part library assembly. Lucky for us, ASP.NET 2.0 comes with a slick new feature called Web Resources. Web Resources allow resources to be embedded into the assembly and referenced using a special URL (a nice long convoluted querystring, as you’ll see, but easily referenced via some code magic) from within a web project. SharePoint developers working with ASP.NET 2.0 Web Parts — for example, those of you who are using pre-Beta 2 releases of Windows SharePoint Services 3.0 or Microsoft Office SharePoint Server 2007 (MOSS), and those of you who will be using the Beta 2 release when it shows up someday soon — can take advantage of the new Web Resources feature to wrap up static files into Web Part assemblies and clean up install processes quite a little bit. Web Resources include a number of other cool little tricks that make deploying Web Parts with embedded resources simpler and more robust, as well. Plus, you get to use a cool new thingy — and ain’t that all right, too?

In this post, I’m going to create a Web Part of negligible utility — well…okay, I’m going to create a totally pointless Web Part that will, nonetheless, demonstrate a couple of things that can be done with Web Resources, and hopefully serve as a starting point for those of you out there that have actual problems to solve. :-)

All that my Web Part will do is display an image, and have a bit of client-side script that does a rollover effect when you mouse over the image. I’m not even going to get so fancy as to have the image be interesting or functional. We’re just going to have two embedded image resources and one embedded script resource, and have a nice simple Web Part assembly at the end of the day. I’ll be writing a pure ASP.NET 2.0 Web Part — inheriting from the System.Web.UI.WebControls.WebParts.WebPart class — rather than doing anything SharePoint-specific. This will be the recommended approach with WSS 3.0 and MOSS 2007 as well — unless a handful of WSS 2.0/SPS 2003 Web Part relics (V2–style Web Part connections, for example…though I have no idea why you’d use those for a new Web Part in WSS 2.0 or MOSS 2007 when the much richer ASP.NET 2.0 connection framework is available to you) are absolutely necessary, ASP.NET 2.0 Web Parts will be the recommended approach, even for Web Part development that makes use of data in WSS 2.0 or MOSS 2007 via the various APIs.

The Resources

I’m going to use two very simple little button images for the purposes of this example. 

The default button image looks like this:

button - default state

The rollover-state button image looks like this:

button - hover state

The script for doing the rollover is very simple as well:

// Initial JScript source code
function RollOver(imageid)
{
document.all.item(imageid).src = 'button_hover.gif';
}
function RollOut(imageid)
{
document.all.item(imageid).src='button.gif';
}

This is saved in a .JS file. In a minute, I’m going to modify that script to include tags that ASP.NET 2.0 will interpret and substitute with URLs to my embedded image resources at runtime, but for now, it’s enough to see that there’s very little magic going on with the script itself.

Getting the Resources into Visual Studio 2005, and Writing the Code

Screenshot of Project Solution ExplorerIn Visual Studio 2005, I create a new Web Control Library project. In my case, I’m going to name it “My.BlogParts.” Next, I’m going to add a new Resource File to the project, which I can do by going the the menu and clicking Project -> Add New Item… and choosing “Resources File.” I’m going to name mine “testres.resx.” [I can also create a project default resources file, but for the purposes of this post, it’s just simpler to create a new one] Once I’ve created it, I can double-click on the testres.resx in Solution Explorer to open up the resources designer. From within the designer, I then choose Add Resource -> Add Existing File… and add my two .GIF images and my .JS script file to the Resources file. In the screenshot on the right, you can see what my project’s Solution Explorer looks like after this has been done. Disregard the additional files in there for now (e.g., RollOver.cs and mykeys.snk), I’ll be discussing them below. However, do note the added “Resources” folder, as it will become important later on.

For each of the embedded Resource files, I need to set the Build Action for the resource to “Embedded Resource” in the Properties pane for that item. To do so, I simply select the item from within the Resources folder, look at the Properties pane, and select “Embedded Resource” from the Build Action drop-down menu. I can leave “Copy to Output” set to its default, which is “Do not copy.” See the screenshot below:

Setting Build Action to Since these are Web Resources, I also need to reference them in my AssemblyInfo.cs file. In this case, here’s the few lines I add:

     [assembly: WebResource("My.BlogParts.Resources.button.gif", "image/gif")]
[assembly: WebResource("My.BlogParts.Resources.button_hover.gif", "image/gif")]
[assembly: WebResource("My.BlogParts.Resources.rollover.js", "text/javascript", PerformSubstitution = true)]

The format of these entries is pretty simple. First param = [Namespace].[Resources Folder].[Resource File Name]. Second param = content type. Third (optional) param = PerfomSubstitution (true/false). Because my project, namespace, and assembly will all be named “My.BlogParts,” and my resources folder is named “Resources,” this is pretty easy to figure out: My.BlogParts.Resources.[resource file name]. The third, optional, parameter lets me do some super-cool runtime substitution that I’ll explain more in a moment.

Now that I have my Resources imported into the Project, I’m going to add and write a simple Web Part that will use them. Because I hate default names, I’m going to simply delete the “WebCustomControl1.cs” that was created for me when I created the Project, and add a new Web Custom Control to the Project, named “RollOver.cs.” Visual Studio tries to be nice and adds a bunch of default code into the class file, and in the code snippets below, you might notice that I’ve erased it and started from scratch; for the purposes of this example, though, you could just as well leave all of the default code in the class file and edit the items that I’ve changed and leave the rest since they won’t really hurt anything.

By default, my web control inherits from the System.Web.UI.WebControls.WebControl class. Because I’m creating a Web Part, I need to change this. First, I’m going to add a using statement at the top of the .cs file:

using

System.Web.UI.WebControls.WebParts;

Next, I simply change my class to inherit from System.Web.UI.WebControls.WebParts.WebPart:

public

class RollOver : WebPart
{

Because my Web Part is going to be displaying an image, I’m going to declare a System.Web.UI.WebControls.Image object to the class, e.g.:

public

class RollOver : WebPart
{
protected Image imgRoll;

Next, I’m going to add an override of the CreateChildControls() method, so that I can add my Image object to the Web Part. This code will be the first to utilize some of the new Web Resource-related methods, and I’ll explain after showing the code:

protected override void CreateChildControls()
{
ClientScriptManager cs = Page.ClientScript;

imgRoll = new Image();
imgRoll.ImageUrl = cs.GetWebResourceUrl(this.GetType(),
"My.BlogParts.Resources.button.gif");
imgRoll.Attributes.Add("onMouseOver", "RollOver(this.id)");
imgRoll.Attributes.Add("onMouseOut", "RollOut(this.id)");
imgRoll.ID = "myimage2";

Controls.Add(imgRoll);
}

So, from the perspective of Web Part development, I first instantiate a new Image object, assign some attributes, and add it to the Web Part’s “Controls” collection. You’ll note that I’m adding some attributes to specify onMouseOut and onMouseOver script calls. The interesting stuff, though, is the ClientScriptManager object and GetWebResourceURL() method. I’m not going to go into detail about these, since you can read about each of them here and here, and I’m sure elsewhere as well. It’s enough to point out that once I’ve created a ClientScriptManager object, I can use its GetWebResourceURL() method to return the URL to an embedded resource. I pass it two parameters: the resource type (I can just use this.GetType() in C# to return the type) and the name of the resource. I need to specify the name of the web resource here like I specified it in AssemblyInfo.cs — [Namespace].[Resources folder].[Resource File Name].

OK, two more code steps: I need to get my script included in the target page, and I need to render the Image control. For the former, I override the OnPreRender event, because I want my script loaded before the page loads (so that it is available to my Web Part once it renders). I use the ClientScriptManager’s RegisterClientScriptInclude() method to add my script as an include, passing the type (again, using this.GetType() in C#), the resource name, and the URL of the resource (the last of which I obtain using GetWebResourceURL() again).

protected

override void OnPreRender(EventArgs e)
{
ClientScriptManager cs = Page.ClientScript;
cs.RegisterClientScriptInclude(this.GetType(), "rollover", cs.GetWebResourceUrl(this.GetType(),
"My.BlogParts.Resources.rollover.js"));
}

For the latter — rendering the Image control — I simply override RenderContents() (if you haven’t removed all of the code that Visual Studio created for you, one already exists, and you can just edit the contents), and render it like I would in WSS 2.0/SPS 2003:

protected

override void RenderContents(HtmlTextWriter output)
{
EnsureChildControls();
this.imgRoll.RenderControl(output);
}

Using PerformSubstition to Replace Placeholders with URLs at Runtime

One last thing. The script file that I outlined earlier included hard-coded values for URLs, e.g.:

document.all.item(imageid).src='button.gif';

This won’t work for an embedded Web Resource, of course (this would look for a static button.gif file in the current directory). Thankfully, ASP.NET 2.0 Web Resources includes a solution, via PerformSubstitution. When I referenced my Web Resources in AssemblyInfo.cs, I included “PerformSubstituion = true” for my embedded .JS script file. This tells ASP.NET 2.0 to look for “tags” (my phrase) in the embedded file, and replace them at runtime. I’m then able to add tags referencing additional embedded Web Resources. Because I’m embedding my image files, I’ll modify my script to include tags so that at runtime, the URL is replaced with the embedded Web Resource URL for the images, e.g.:

// JScript source code
function RollOver(imageid)
{
document.all.item(imageid).src = '<%=WebResource("My.BlogParts.Resources.button_hover.gif")%>';
}
function RollOut(imageid)
{
document.all.item(imageid).src='<%=WebResource("My.BlogParts.Resources.button.gif")%>';
}

The format should look pretty familiar by now — [Namespace].[Resources Folder].[Resource File Name]. At runtime, those tags will be replaced by the Web Resource URL for each of the embedded images.

Build and Deploy the Web Part

That’s it for code. Because I’m going to deploy my Web Part to the GAC for the purposes of this example, I strong-name the assembly when I build it (in this case, by going to the menu and selecting Project –> [Project Name] Properties…, selecting the “Signing” tab, checking “Sign the assembly,” and creating a new strong name key file using the option in the drop-down menu). The resulting assembly contains my Web Part and all of the resources it needs — I can deploy the assembly alone, and don’t have to package up the static files along with it for installation.

I’m not going to get into Web Part deployment in this post, but those are all of the steps needed to create a simple Web Part that uses embedded image and script resources to perform a simple little rollover/hover image effect. I’ve posted the full source code and image files in a ZIP archive if you’re interested.

Happy coding!