Sprite and Image Optimization Framework & DotNetNuke

Tags: .NET, .NET C#, ASP.NET, ASPInsiders, C#, Cache, Caching, DNN, DotNetNuke, HTML

Introduction

The folks over at Microsoft recently pushed a open source release of their Sprite and Image Optimization Framework onto Codeplex last week.  And after a few tweaks I was able to get it running within DotNetNuke, here is how…

Download the Sprite and Image Optimization Framework.  Unzip it and open up the ImageOptimizationFramework.sln file using Visual Studio .NET 2010. 

Getting it 3.5 Compatible

If you are still running your DotNetNuke installation on 3.5, you will need to change the “Target Framework” for both the “Web Forms Control” and the “Image Optimization Framework” down to 3.5.  This can be achieved by right clicking the project and under the “Application” Tab / Target Framework choose “.NET 3.5 Framework”, save and close both property windows.

Next you will notice that when you attempt to build (now that we are on 3.5) you will get a compiler error.  In ImageOptimization.cs I changed the following method to look like:

private static void RebuildFromCacheHit(string key, object value, CacheItemRemovedReason reason) {

  var data = (object[])value;
  string path = (string)data[0];
  IEnumerable<string> cachedDirectoriesBelowCurrentFolder = (IEnumerable<string>)data[1];

That is, I replaced their use of the Tuple class with the generic object[]

The second method that needed to be changed is:

private static void InsertItemIntoCache(string path, IEnumerable<string> directoriesBelowCurrentFolder) {
  string key = Guid.NewGuid().ToString();
  //var value = Tuple.Create(path, directoriesBelowCurrentFolder);
  var value= new object[]{path, directoriesBelowCurrentFolder};
  HttpRuntime.Cache.Insert(key, value, new CacheDependency(path), Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, RebuildFromCacheHit);
}

Notice again, it was converting the Tuple to use an object[].  I’m sure there is a more optimal way available but this is good enough for this round of testing.

The solution should compile now. 

DotNetNuke

If you navigate to the folder”  %My Download Directory%\sprite-image-optimization-framework\WebFormsControlSample\Bin\   you will see the 2 compiled DLLs and PDB files. XCopy deploy these into your local (testing) DNN installation’s bin folder.

You will now need to crack open your web.config for your DNN installation and add the following line, under the “System.Web/httpModules” section:

<add type ="Microsoft.Samples.Web.ImageOptimizationModule" name ="Microsoft.Samples.Web.ImageOptimizationModule"/>

This HttpModule assumes that there is a folder at the root of your web site named App_Sprites.  You should create that folder now otherwise you will get a yellow screen of death at first load.

The HttpHandler wires up a cache dependency to the App_Sprites folder after “indexing” the content.  It will take all of the images in that folder and create the Sprite Image, and associated CSS files.  I also noticed that it will actually rebuild these files if they are deleted for any reason.

Capture

Notice the CSS files, a dat file and the new sprite0.png file; all of these were created based on the existing images in that folder by the HttpHandler.

Within your module folder structure, create a “App_Sprites” folder, copy your images in to that folder.  Like so:

image

Notice that I set references to both of the ImageOptimizationFramework.dll and the ImageSprite.dll in this project.

In your code-behind file for your module, we will follow the same pattern as the HttpHandler in order to add our Cache Dependency to the ImageOptimizer, like so:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.Samples.Web;

namespace DNN.Sprites.DesktopModules.SpriteSample
{
    public partial class Sprite : DotNetNuke.Entities.Modules.PortalModuleBase
    {
        private static readonly object _lockObj = new object();
        private static bool _hasAlreadyRun;

        static Sprite()
        {
            lock (_lockObj)
            {
                if (_hasAlreadyRun)
                    return;
                else
                    _hasAlreadyRun = true;
            }

            ImageOptimizations.AddCacheDependencies(System.Web.HttpContext.Current.Server.MapPath("~/DesktopModules/SpriteSample/App_Sprites/"), rebuildImages: true);
            return;
        }

        protected void Page_Load(object sender, EventArgs e)
        {

        }
    }
}

 

And finally in our ascx document we can now use the new ImageSprite control to refer to our images:

 

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Sprite.ascx.cs" Inherits="DNN.Sprites.DesktopModules.SpriteSample.Sprite" %>
<%@ Register TagPrefix="asp" Namespace="Microsoft.Samples.Web" Assembly="ImageSprite" %>
<asp:ImageSprite runat="server" ImageUrl="~/DesktopModules/SpriteSample/App_Sprites/windowsLogo.png" />
<asp:ImageSprite ID="ImageSprite1" runat="server" ImageUrl="~/DesktopModules/SpriteSample/App_Sprites/xbox.png" />
<asp:ImageSprite ID="ImageSprite2" runat="server" ImageUrl="~/DesktopModules/SpriteSample/App_Sprites/office.png" />

 

Which emits:

Capture1

With the HTML:

 

<div id="dnn_ctr6123_ModuleContent" class="SpriteContent">
    <img class="windowsLogo-png" src="data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" style="border-width:0px;" />
    <img id="dnn_ctr6123_Sprite_ImageSprite1" class="xbox-png" src="data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" style="border-width:0px;" />
    <img id="dnn_ctr6123_Sprite_ImageSprite2" class="office-png" src="data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" style="border-width:0px;" />
</div

Play close attention to the class attribute as well.  They are using a fairly simplistic naming convention to automatically emit the attribute for predictability.

Although the project seems to be in the early stages I can see it receiving very broad adoption in the near future.

No Comments