About the author
Boaz I'm a software developer, working at a small company in the Netherlands. Currently I'm mostly using techniques like .NET, C#, SQL and jQuery, but I have experience with JAVA and PHP as well.

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Almost a year ago Scott Hanselman blogged about a NuGet package called AspNetSprites that automatically generates sprite images for you. When using a lot of small icon images, sprites can speedup your website quite a lot. This is due to minimizing the number of HTTP requests.

For AspNetSprites there are helper packages for both WebForms and ASP.NET MVC, but wouldn't it be nice to use this with MonoRail too? This is not to difficult to accomplish because there is a AspNetSprites-Core package containing all the logic so all we need to do is write a MonoRail View Helper class.

I downloaded the source from the ASP.NET codeplex page and modified the Razor view helper so it can be used with MonoRail. Here the result:

using System;
using System.Linq;
using System.Text;
using Microsoft.Web.Samples;
using System.IO;
using System.Web;
using System.Web.UI;
using System.Collections;

namespace MonoRail.Samples.SpriteHelper
{
    public class Sprite
    {
        private static Control helperControl = CreateHelperControl();

        public static string ImportStylesheet(string virtualPath)
        {
            ImageOptimizations.EnsureInitialized();

            if (Path.HasExtension(virtualPath))
            {
                virtualPath = Path.GetDirectoryName(virtualPath);
            }

            HttpContextBase httpContext = new HttpContextWrapper(HttpContext.Current);

            string cssFileName = ImageOptimizations.LinkCompatibleCssFile(httpContext.Request.Browser) ?? ImageOptimizations.LowCompatibilityCssFileName;

            virtualPath = Path.Combine(virtualPath, cssFileName);
            string physicalPath = HttpContext.Current.Server.MapPath(virtualPath);

            if (File.Exists(physicalPath))
            {
                StringWriter sw = new StringWriter();

                using (HtmlTextWriter html = new HtmlTextWriter(sw))
                {
                    html.AddAttribute(HtmlTextWriterAttribute.Href, ResolveUrl(virtualPath));
                    html.AddAttribute(HtmlTextWriterAttribute.Rel, "stylesheet");
                    html.AddAttribute(HtmlTextWriterAttribute.Type, "text/css");
                    html.AddAttribute("media", "all");
                    html.RenderBeginTag(HtmlTextWriterTag.Link);
                }

                return sw.ToString();
            }

            return String.Empty;
        }


        public static string MakeCssClassName(string pathToImage)
        {
            return ImageOptimizations.MakeCssClassName(pathToImage);
        }

        public static string Image(string virtualPath)
        {
            return Image(virtualPath, null);
        }

        public static string Image(string virtualPath, IDictionary htmlAttributes)
        {
            ImageOptimizations.EnsureInitialized();

            HttpContextBase httpContext = new HttpContextWrapper(HttpContext.Current);

            StringWriter sw = new StringWriter();

            using (HtmlTextWriter html = new HtmlTextWriter(sw))
            {
                if (htmlAttributes != null)
                {
                    foreach (DictionaryEntry entry in htmlAttributes)
                    {
                        html.AddAttribute((entry.Key ?? String.Empty).ToString(), (entry.Value ?? String.Empty).ToString());
                    }
                }

                if (ImageOptimizations.LinkCompatibleCssFile(httpContext.Request.Browser) == null)
                {
                    html.AddAttribute(HtmlTextWriterAttribute.Src, ResolveUrl(virtualPath));
                }
                else
                {
                    html.AddAttribute(HtmlTextWriterAttribute.Class, ImageOptimizations.MakeCssClassName(virtualPath));
                    html.AddAttribute(HtmlTextWriterAttribute.Src, ResolveUrl(ImageOptimizations.GetBlankImageSource(httpContext.Request.Browser)));
                }

                html.RenderBeginTag(HtmlTextWriterTag.Img);
            }

            return sw.ToString();
        }

        private static Control CreateHelperControl()
        {
            var control = new Control();
            control.AppRelativeTemplateSourceDirectory = "~/";
            return control;
        }

        private static string ResolveUrl(string path)
        {
            return helperControl.ResolveClientUrl(path);
        }

    }
}
This helper exposes three methods (and one overload) here are some usage examples:
$Sprite.ImportStylesheet("~/App_Sprites/categories/")

<li class="$Sprite.MakeCssClassName("~/App_Sprites/categories/dotNet.png")"><a href="#" class="categories">Programming</a></li>

<a href="#">$Sprite.Image("~/App_Sprites/popular/visualStudio.png", "%{alt='visualStudio'}")
</a>
Besides this code file a small change to the web.config file is required and you should off course add the [Helper(typeof(Sprite))] attribute to your controller.

To get you started here is the NuGet packages I created for personal usage:
AspNetSprites-MonoRailHelper.0.1.nupkg


January 1, 2012 - Comments [0] - Posted in ASP.NET | MonoRail
FireDotNet is now available as an official NuGet package on nuget.org:



FireDotNet is a library I created to show ASP.NET server-side log messages in the Firebug console on the client-side. A few months ago I updated the library to a NLog target so all NLog messages are send to Firebug.



I used this opportunity to make some changes too. First of all I automated the builds using TeamCity. I installed a NuGet plugin for TeamCity so dependencies are automatically resolved. This means that I no longer need to have the NLog.dll files under version control. Besides managing dependencies it can also automatically make a NuGet package and publish it to both my private NuGet server and the official NuGet server.

Another nice feature of TeamCity is that it can manage version numbers for you. There is a build feature named AssemblyInfo patcher and - as the name already implies - it modifies the version number of your AssemblyInfo.cs files. So from now on all FireDotNet builds will have a version number that consists of:
MajorVersion.MinorVersion.BuildNumber.SvnRevisionNumber eg. 0.2.30.34
Where I control the first two numbers and the last two are automatically set by TeamCity.

Last but not least I managed to add a new feature too! From now on FireDotNot by default only outputs to requests form localhost. This means that you, when deploying your project, no longer have to think of removing FireDotNet from it. Except of course if you changed the allowRemote property.

December 11, 2011 - Comments [0] - Posted in ASP.NET | FireDotNet | NLog | NuGet