Benjamin Ouellet
Left Edge Right Edge
PNGImage Source Code Listing
Go Back
/*
PNGImage .NET Web Server Control Version 1.2 - November 2006
Copyright 2006 by Benjamin Ouellet
This web control can be freely used for any purpose, whether personal or commercial.
You can make any changes you want to this code. If you do make changes to improve this
control, you can submit the changes back to me and I'll consider adding the changes to my
source code.  Please review license.txt for more details.
Contact me at wowitsben at yahoo.com or visit my website at http://bigbeno.dyndns.org:8000/
*/

using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Web.UI.Design;

namespace Beno.WebControls 
{
	/// <summary>
	/// Enhanced Image Web Control with PNG support.
	/// </summary>
	[DefaultProperty("Image"), 
	ToolboxData("<{0}:PNGImage runat=server></{0}:PNGImage>"),
	Designer(typeof(Beno.WebControls.PNGImageDesigner)) ]
	public class PNGImage : System.Web.UI.WebControls.Image 
    {
        // PNG support level
        public enum PngSupport { Supported, NotSupported, RequiresFilter };
        private PngSupport pngSupport;

        private System.Text.StringBuilder styles;
	
		public PNGImage()
        {
			styles = new System.Text.StringBuilder();
		}

		protected override void OnLoad(EventArgs e) 
        {
			pngSupport = getBrowserType();
			base.OnLoad(e);
		}

		/// <summary>
		/// Alternate image to use if browser doesn't support PNG transparency
		/// </summary>
		[Bindable(true), Category("Appearance"), Description("Replaces standard PNG image with an alternate image (Transparent GIF Recommended) if browser doesn't support PNG format.")]
		public string AlternateImageURL {
			get {
				return (string)ViewState["AlternateImageURL"];
			}
			set {
				ViewState["AlternateImageURL"] = value;
			}
		}

		/// <summary>
		/// Specifies URL to dummy image when displaying Transparent PNG image in IE.
		/// </summary>
		[Bindable(true), Category("Misc"), Description("For IE PNG transparency on an image to work, a dummy image is required in the image tag.  Defaults to spacer.GIF.")]
		public string ImageSpacerURL {
			get {
				return (string)ViewState["ImageSpacerURL"];
			}
			set {
				ViewState["ImageSpacerURL"] = value;
			}
		}

		/// <summary> 
		/// Render this control to the output parameter specified.
		/// </summary>
		/// <param name="output"> The HTML writer to write out to </param>
		protected override void Render(HtmlTextWriter output) 
        {
			output.WriteBeginTag("img");
			RenderBasicAttributes(output);

			switch (pngSupport) 
            {
					// PNG Transparency capable browser.  Image control acts like a standard control,
					// except style information is manually added.
				case PngSupport.Supported:
					OutputStandardPNG(output);
					break;

					// IE 5.5 or higher browser.  PNG transparency capable, but
					// requires proprietary HTML code
                case PngSupport.RequiresFilter:
                    if (this.ImageUrl.EndsWith(".png") == true)
                    {
                        OutputIEPNG(output);
                    }
                    else
                    {
                        OutputStandardPNG(output);
                    }
					break;

					// Not capable of PNG transparency.  Substitute with alternate image.
                case PngSupport.NotSupported:
					OutputDownlevelHTML(output);
					break;
			}
			// Output styles
			if (styles.Length > 0)
				output.WriteAttribute("style", styles.ToString());
			output.Write(" />");
		}

		// Case Supported - Web standard compliant browser.
		private void OutputStandardPNG(HtmlTextWriter output) 
        {
			RenderBorderBackground(output);
			RenderAdditionalStyles(output);
			output.WriteAttribute("src", this.ImageUrl);
			OutputDimensions(output);
		}

		// Case RequiresFilter - Output img tag with AlphaImageLoader inline style and dummy image.
		private void OutputIEPNG(HtmlTextWriter output) 
        {
			RenderBorderBackground(output);
			RenderAdditionalStyles(output);
			// Check to see if width and height are not defined.
			if ((this.Width == Unit.Empty) & (this.Height == Unit.Empty)) 
            {
				// No width and height defined.
				// Set sizing method to "image" and put in a dummy width and height.
				// Not adding a width and height strangely causes IE not to display the
				// image at all.
				AddStyle("FILTER", "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.ImageUrl + "', sizingMethod='image')");
				output.WriteAttribute("width", "1");
				output.WriteAttribute("height", "1");
			}
			else 
            {
				// We have a width and / or height.  Set the image to automatically
				// scale to the size specified.
				AddStyle("FILTER", "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.ImageUrl + "', sizingMethod='scale')");
				output.WriteAttribute("width", outputAttribute(this.Width));
				output.WriteAttribute("height", outputAttribute(this.Height));
			}

			// Output a spacer image, used as a placeholder so the AlphaImageLoader
			// displays PNG image properly.
			if ((string)ViewState["ImageSpacerURL"] == null)
				ViewState["ImageSpacerURL"] = "spacer.gif";
			output.WriteAttribute("src", (string)ViewState["ImageSpacerURL"]);
		}

		// Output HTML for case 2 - A browser not capable of transparent PNG images.
		private void OutputDownlevelHTML(HtmlTextWriter output) {
			// If it's a PNG image, substitute with alternate downlevel image.
			if (this.ImageUrl.EndsWith(".png") == true) {
				if (this.AlternateImageURL == null) {
					// No alternate image defined.
					output.WriteAttribute("src", this.ImageUrl);
				}
				else
					// Output alternate image file name
					output.WriteAttribute("src", this.AlternateImageURL);
			}
			else {
				// Output standard image.
				output.WriteAttribute("src", this.ImageUrl);
			}
			if (this.BorderWidth != Unit.Empty)
				output.WriteAttribute("border", this.BorderWidth.ToString());
			OutputDimensions(output);
			RenderAdditionalStyles(output);
		}

		// Outputs border and background color style attributes for case Supported and
		// RequiresFilter
		private void RenderBorderBackground(HtmlTextWriter output) {
			if (this.BorderStyle != BorderStyle.NotSet)
				AddStyle("border-style", this.BorderStyle.ToString());
			if (this.BorderColor.IsEmpty == false)
				AddStyle("border-color", System.Drawing.ColorTranslator.ToHtml(this.BorderColor));
			if (this.BorderWidth.IsEmpty == false)
				AddStyle("border-width", this.BorderWidth.ToString());
			if (this.BackColor.IsEmpty == false)
				AddStyle("background-color", System.Drawing.ColorTranslator.ToHtml(this.BackColor));
		}

		// Output additional styles if defined.
		private void RenderAdditionalStyles(HtmlTextWriter output) {
			System.Collections.IEnumerator keys = Style.Keys.GetEnumerator();
			while (keys.MoveNext()) {
				AddStyle((string)keys.Current, Style[(string)keys.Current]);
			}
		}

		// Outputs width and height.
		private void OutputDimensions(HtmlTextWriter output) {
			// Set width and height
			if (this.Width != Unit.Empty)
				output.WriteAttribute("width", outputAttribute(this.Width));
			if (this.Height != Unit.Empty)
				output.WriteAttribute("height", outputAttribute(this.Height));
		}

		// Outputs a size value - a number or a percentage value
		private string outputAttribute(System.Web.UI.WebControls.Unit Size) {
			string size;

			if (Size.Type == UnitType.Percentage)
				size = Size.ToString();
			else
				size = Size.Value.ToString();
			return size;
		}

		private void RenderBasicAttributes(HtmlTextWriter output) {
			// Add ID
			output.WriteAttribute("id", this.ID);
			// Add css class
			if (this.CssClass != "")
				output.WriteAttribute("class", this.CssClass);
			// Alternate Text
				output.WriteAttribute("alt", this.AlternateText);
			// Image Alignment
			if (this.ImageAlign != ImageAlign.NotSet) {
				output.WriteAttribute("align", Enum.Format(typeof(ImageAlign), this.ImageAlign, "g"));
			}

			// Long Description URL
			if (this.DescriptionUrl != "") {
				output.WriteAttribute("longdesc", this.DescriptionUrl);
			}
			// Tooltip Text.
			if (this.ToolTip != "")
				output.WriteAttribute("title", this.ToolTip);
			
			// Render other attributes
			System.Collections.IEnumerator attribs = Attributes.Keys.GetEnumerator();
			while (attribs.MoveNext()) {
				if ((string)attribs.Current != "style")
					output.WriteAttribute((string)attribs.Current, this.Attributes[(string)attribs.Current]);
			}
		}

		// Browser detection code here.
        private PngSupport getBrowserType() 
        {
			System.Web.HttpBrowserCapabilities cap = this.Context.Request.Browser;
            PngSupport browserType = PngSupport.Supported;
            try
            {
                // Parse version numbers
				string[] version = cap.Version.Split('.');
				int versionMajor = 0, versionMinor = 0;
				
				if (version.Length > 0)
                {
                    versionMajor = int.Parse(version[0]);
                }
                if (version.Length > 1)
                {
                    versionMinor = int.Parse(version[1]);
				}

				if (cap.Browser.ToLower() == "ie")
                {
					// IE7
                    if (versionMajor >= 7) {
						browserType = PngSupport.Supported;
                    }
					// IE 5.5 or 6
                    else if ((versionMajor == 5 & versionMinor >= 5) | versionMajor == 6) {
						browserType = PngSupport.RequiresFilter;
					}
					// IE 5.0 or lower
                    else {
                        browserType = PngSupport.NotSupported;
					}
				}
                if ((cap.Browser.ToLower() == "netscape") & (cap.MajorVersion <= 4)) {
					browserType = PngSupport.NotSupported;
				}
            }
            catch(Exception e)
            {
                AlternateText = e.Message;
            }
			return browserType;
		}

		/// <summary>
		/// Allows the detected browser type to be overridden.
		/// </summary>
		/// <param name="typeCode">The browser type (0 - Modern, 1 - IE, 2 Old)</param>
		public void setBrowserType(PngSupport typeCode) 
        {
			pngSupport = typeCode;
		}

		// adds a style to the stringbuilder, to later be rendered on the control.
		private void AddStyle(string name, string val) 
        {
			styles.Append(name).Append(":").Append(val).Append(";");
		}
	}

	public class PNGImageDesigner:ControlDesigner 
    {
		/// <summary>
		/// Class for design time support in Visual Studio.
		/// </summary>
		/// <returns></returns>
		public override string GetDesignTimeHtml( ) 
        {
            Beno.WebControls.PNGImage control = (Beno.WebControls.PNGImage)this.Component;
            if (control != null)
            {
				// Force control to display standard PNG image in Visual Studio.
                control.setBrowserType(Beno.WebControls.PNGImage.PngSupport.Supported);
            }
			return base.GetDesignTimeHtml();
		}
	}
}