Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

An ASP.NET Color Picker Control based on YUI Color picker

, 3 Jun 2009 Ms-PL
An ASP.NET Color Picker Control based on YUI Color picker
using System;
using System.Configuration;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Text;

namespace YUI.Controls
{
	public enum ColorPickerMode
	{
		/// <summary>
		/// The color picker widget will be rendered into a movable virtual window within the document
		/// </summary>
		FloatingDialog,

		/// <summary>
		/// The color picker will be statically rendered into the control specified by the ContainerControl property
		/// </summary>
		Inline
	}

	public enum ColorPickerUpdateMode
	{
		/// <summary>
		/// Whenever the user clicks on a color, the callback and client code will be called.
		/// </summary>
		Continuous,

		/// <summary>
		/// Used on dialogs only (FloatingDialog)
		/// </summary>
		OnConfirm
	}

	[AspNetHostingPermission(System.Security.Permissions.SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
	[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
	//[DefaultProperty("ContainerControl")]
	[ToolboxData("<{0}:ColorPicker run=\"server\"> </{0}:ColorPicker>")]
	//[ParseChildren(true, "Text")]
	public class ColorPicker : WebControl, ICallbackEventHandler
	{
		private string ContainerClientID
		{
			get
			{
				return ClientID + "_container";
			}
		}
		private string HiddenFieldClientID
		{
			get
			{
				return ClientID + "_value";
			}
		}
		private string DialogContainerClientID
		{
			get
			{
				return ClientID + "_dialog";
			}
		}

		#region Behavior properties
		[Bindable(false)]
		[Category("Behavior")]
		[Description("Color picker mode. One of: FloatingDialog, Inline.")]
		[DefaultValue("")]
		[Localizable(false)]
		public ColorPickerMode ColorPickerMode { get; set; }

		[Bindable(true)]
		[Category("Behavior")]
		[Description("The update mode of the Color Picker. Either OnConfirm or Continuous.")]
		[DefaultValue("")]
		[Localizable(false)]
		public ColorPickerUpdateMode UpdateMode { get; set; }

		[Bindable(false)]
		[Category("Behavior")]
		[Description("Enables animation of the color picker thumb")]
		[DefaultValue(false)]
		[Localizable(false)]
		public bool EnableAnimation { get; set; }

		[Bindable(false)]
		[Category("Behavior")]
		[Description("Color picker modal behavior.")]
		[DefaultValue("")]
		[Localizable(false)]
		public bool ModalBehavior { get; set; }

		[Bindable(false)]
		[Category("Behavior")]
		[Description("Color picker modal behavior.")]
		[DefaultValue("")]
		[Localizable(false)]
		public bool UseCallback { get; set; }
		#endregion

		#region Color Picker Properties

		[Bindable(true)]
		[Category("Color Picker")]
		[Description("The Color picker RGB Value")]
		[DefaultValue("")]
		[Localizable(false)]
		public string Color
		{
			get
			{
				return (string)ViewState["Color"];

			}
			set
			{
				if (Color != value)
				{
					ViewState["Color"] = value;
					if (ColorChanged != null)
						ColorChanged(this, EventArgs.Empty);
				}
			}
		}

		[Bindable(true)]
		[Category("Color Picker")]
		[Description("Controls whether the color picker should be rendered as visible or hidden")]
		[DefaultValue(true)]
		[Localizable(false)]
		public bool IsHidden
		{
			get
			{
				var vsp = ViewState["IsHidden"];
				if (vsp == null)
					return false;
				return (bool)vsp;
			}
			set
			{
				ViewState["IsHidden"] = value;
			}
		}

		[Bindable(true)]
		[Category("Color Picker")]
		[Description("Hide/show the entire set of controls. Default: true.")]
		[DefaultValue(true)]
		[Localizable(false)]
		public bool? ShowControls
		{
			get
			{
				var vsp = ViewState["ShowControls"];
				if (vsp == null)
					return null;
				return (bool)vsp;
			}
			set
			{
				ViewState["ShowControls"] = value;
			}
		}

		[Bindable(true)]
		[Category("Color Picker")]
		[Description("Hide/show the hex controls. Default: true.")]
		[DefaultValue(true)]
		[Localizable(false)]
		public bool? ShowHexControls
		{
			get
			{
				var vsp = ViewState["ShowHexControls"];
				if (vsp == null)
					return null;
				return (bool)vsp;
			}
			set
			{
				ViewState["ShowHexControls"] = value;
			}
		}

		[Bindable(true)]
		[Category("Color Picker")]
		[Description("Hide/show the hex summary. Default: true.")]
		[DefaultValue(true)]
		[Localizable(false)]
		public bool? ShowHexSummary
		{
			get
			{
				var vsp = ViewState["ShowHexSummary"];
				if (vsp == null)
					return null;
				return (bool)vsp;
			}
			set
			{
				ViewState["ShowHexControls"] = value;
			}
		}

		[Bindable(true)]
		[Category("Color Picker")]
		[Description("Hide/show the HSV controls. Default: false.")]
		[DefaultValue(false)]
		[Localizable(false)]
		public bool? ShowHSVControls
		{
			get
			{
				var vsp = ViewState["ShowHSVControls"];
				if (vsp == null)
					return null;
				return (bool)vsp;
			}
			set
			{
				ViewState["ShowHSVControls"] = value;
			}
		}

		[Bindable(true)]
		[Category("Color Picker")]
		[Description("Hide/show the RGB controls. Default: true.")]
		[DefaultValue(true)]
		[Localizable(false)]
		public bool? ShowRGBControls
		{
			get
			{
				var vsp = ViewState["ShowRGBControls"];
				if (vsp == null)
					return null;
				return (bool)vsp;
			}
			set
			{
				ViewState["ShowRGBControls"] = value;
			}
		}

		[Bindable(true)]
		[Category("Color Picker")]
		[Description("Hide/show the websafe-color swatch. Default: true.")]
		[DefaultValue(true)]
		[Localizable(false)]
		public bool? ShowWebSafe
		{
			get
			{
				var vsp = ViewState["ShowWebSafe"];
				if (vsp == null)
					return null;
				return (bool)vsp;
			}
			set
			{
				ViewState["ShowWebSafe"] = value;
			}
		}

		#endregion

		[DefaultValue(true)]
		public bool LoadYui
		{
			get
			{
				var vsp = ViewState["LoadYui"];
				if (vsp == null)
					return true;
				return (bool)vsp;
			}
			set
			{
				ViewState["LoadYui"] = value;
			}
		}

		#region Color Picker events

		[Category("Events")]
		[Description("The Color picker server-side ColorChanged handler")]
		[DefaultValue("")]
		[Localizable(false)]
		public event EventHandler ColorChanged;

		[Bindable(true)]
		[Category("Events")]
		[Description("The Color picker client-side ColorChanged handler code")]
		[DefaultValue("")]
		[Localizable(false)]
		public string OnClientColorChanged { get; set; }

		#endregion

		#region ASP.NET stuff
		/// <summary>
		/// Registers YUI and the initializer. On postbacks, loads the update value (which, in turn, will fire events)
		/// </summary>
		/// <param name="e"></param>
		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e); 
			
			NextControlInitializerFunctionId = ContainerClientID + "_loadyui";
			NextControlUpdateFunctionId = ContainerClientID + "_updateyui";

			if (ModalBehavior && ColorPickerMode == ColorPickerMode.Inline)
				throw new Exception("ModalBehavior cannot be set on ColorPickerMode Fixed");

			if (LoadYui)
			{
				Page.ClientScript.RegisterClientScriptInclude(GetType(), "YuiLoader", "http://yui.yahooapis.com/2.7.0/build/yuiloader/yuiloader-min.js");
			}
			if (!Page.IsPostBack)
			{

			}
			else if (Page.IsPostBack && !Page.IsCallback)
			{
				var val = Page.Request[HiddenFieldClientID];
				if (val.Length == 6)
					Color = "#" + val;			}
		}

		/// <summary>
		/// Initializes the script that will create the color picker on the next postback
		/// </summary>
		private string GetNextControlUpdateFunction()
		{
			var sb = new StringBuilder();
			sb.AppendLine("var picker = c.context;");
			//Not implemented
			return sb.ToString();
		}

		/// <summary>
		/// Returns the YUI loader code that will load the color picker and its dependencies, and then call this color picker instance's initializer
		/// </summary>
		/// <returns></returns>
		private string GetYuiLoaderCode()
		{
			string modules = "'colorpicker'";
			if (EnableAnimation)
				modules += ",'animation'";
			if (ColorPickerMode == ColorPickerMode.FloatingDialog)
				modules += ",'container','event'";

			return @"
(function() { 
    var loader = new YAHOO.util.YUILoader({ 
        base: '', 
        require: [" + modules + @"], 
        loadOptional: false, 
        combine: true, 
        filter: 'MIN', 
        allowRollup: true, 
        onSuccess: function() { " + NextControlInitializerFunctionId + @"();}
    }); 
loader.insert(); 
})();
";
		}

		/// <summary>
		/// The name of the function that will initialize this color picker instance
		/// </summary>
		string NextControlInitializerFunctionId;

		/// <summary>
		/// The name of the function that will update this color picker instance after the callback
		/// </summary>
		string NextControlUpdateFunctionId;

		/// <summary>
		/// Callback handler
		/// </summary>
		/// <param name="eventArg"></param>
		public void RaiseCallbackEvent(string eventArg)
		{
			Color = "#" + eventArg;
		}

		/// <summary>
		/// Callback handler
		/// </summary>
		/// <returns></returns>
		public string GetCallbackResult()
		{
			return "";
		}

		protected override HtmlTextWriterTag TagKey
		{
			get
			{
				return HtmlTextWriterTag.Div;
			}
		}

		/// <summary>
		/// Writes the color picker instance's initialization script
		/// </summary>
		/// <param name="e"></param>
		protected override void OnPreRender(EventArgs e)
		{
			base.OnPreRender(e);

			if (UseCallback)
				Page.ClientScript.RegisterStartupScript(GetType(), NextControlUpdateFunctionId, "function " + NextControlUpdateFunctionId + "(c) {" + GetNextControlUpdateFunction() + "}", true);

			Page.ClientScript.RegisterClientScriptBlock(GetType(), "YuiColorPickerJs", Code, true);

			int[] colors = ParseColor(Color);
			var script = new StringBuilder();
			script.Append("ASPYUI.CreateColorPicker(");
			script.AppendFormat("'{0}',", HiddenFieldClientID);
			script.AppendFormat("'{0}',", ContainerClientID);
			script.AppendFormat("function(){{{0}}},", (OnClientColorChanged ?? ""));
			script.AppendFormat("\"{0}\",", UseCallback ? Page.ClientScript.GetCallbackEventReference(this, "hex", NextControlUpdateFunctionId, "picker") : "");
			script.AppendFormat("'{0}',", ColorPickerMode.ToString());
			script.AppendFormat("'{0}',", DialogContainerClientID);
			script.AppendFormat("{0},", GetJsBool(ModalBehavior));
			script.AppendFormat("{0},", GetJsBool(IsHidden));
			script.AppendFormat("{0},", GetJsBool(ShowControls, true));
			script.AppendFormat("{0},", GetJsBool(ShowHexControls, true));
			script.AppendFormat("{0},", GetJsBool(ShowHexSummary, true));
			script.AppendFormat("{0},", GetJsBool(ShowHSVControls, false));
			script.AppendFormat("{0},", GetJsBool(ShowRGBControls, true));
			script.AppendFormat("{0},", GetJsBool(ShowWebSafe, true));
			script.AppendFormat("[{0},{1},{2}],", colors[0], colors[1], colors[2]);
			script.AppendFormat("'{0}');", UpdateMode.ToString());

			if (LoadYui)
			{
				script.Insert(0, "ASPYUI.Callbacks.push(function(){");
				script.Append("});");
				script.Append("ASPYUI.Modules.colorpicker = true;");
				if (EnableAnimation)
					script.Append("ASPYUI.Modules.animation = true;");
				if (ColorPickerMode == ColorPickerMode.FloatingDialog)
				{
					script.Append("ASPYUI.Modules.container = true;");
					script.Append("ASPYUI.Modules.event = true;");
				}
			}
			else
			{
				script.Insert(0, "YAHOO.util.Event.onDOMReady(function(){");
				script.Append("});");
			}

			Page.ClientScript.RegisterStartupScript(GetType(), NextControlInitializerFunctionId, script.ToString(), true);

			if (LoadYui)
				Page.ClientScript.RegisterStartupScript(GetType(), "LoadYUI", "ASPYUI.LoadYuiAsync();", true);

			Page.ClientScript.RegisterHiddenField(HiddenFieldClientID, Color);
		}



		/// <summary>
		/// Returns a javascript statement that hides the color picker
		/// </summary>
		/// <returns></returns>
		public string GetClientHidePickerScript()
		{
			switch (ColorPickerMode)
			{
				case ColorPickerMode.Inline:
					return "document.getElementById('" + ContainerClientID + "').style.display='none';";
				case ColorPickerMode.FloatingDialog:
					return "document.getElementById('" + DialogContainerClientID + "').dialog.cancel();";
				default:
					return "";
			}
		}

		/// <summary>
		/// Returns a javascript statement that shows the color picker
		/// </summary>
		/// <returns></returns>
		public string GetClientShowPickerScript()
		{
			switch (ColorPickerMode)
			{
				case ColorPickerMode.Inline:
					return "document.getElementById('" + ContainerClientID + "').style.display='';";
				case ColorPickerMode.FloatingDialog:
					return "document.getElementById('" + DialogContainerClientID + "').dialog.show();";
				default:
					return "";
			}
		}

		/// <summary>
		/// Return a javascript expression that evaluates to "#rrggbb"
		/// </summary>
		/// <returns></returns>
		public string GetClientGetPickerColorScript()
		{
			return "document.getElementById('" + HiddenFieldClientID + "').value";
		}
		#endregion

		#region Utils
		/// <summary>
		/// Used to parse an RGB color from value into the Color Picker widget
		/// </summary>
		/// <param name="RGB"></param>
		/// <returns></returns>
		private int[] ParseColor(string RGB)
		{
			int[] ret = new int[3];
			if (String.IsNullOrEmpty(RGB))
				RGB = "ffffff";
			if (RGB.StartsWith("#") || RGB.Length == 6)
			{
				if (RGB.StartsWith("#"))
					RGB = RGB.Substring(1);
				if (RGB.Length == 3)
				{
					var r = RGB.Substring(0, 1);
					ret[0] = int.Parse(r, System.Globalization.NumberStyles.HexNumber);
					var g = RGB.Substring(1, 1);
					ret[1] = int.Parse(g, System.Globalization.NumberStyles.HexNumber);
					var b = RGB.Substring(2, 1);
					ret[2] = int.Parse(b, System.Globalization.NumberStyles.HexNumber);
				}
				else if (RGB.Length == 6)
				{
					var r = RGB.Substring(0, 2);
					ret[0] = int.Parse(r, System.Globalization.NumberStyles.HexNumber);
					var g = RGB.Substring(2, 2);
					ret[1] = int.Parse(g, System.Globalization.NumberStyles.HexNumber);
					var b = RGB.Substring(4, 2);
					ret[2] = int.Parse(b, System.Globalization.NumberStyles.HexNumber);
				}
				else throw new Exception("Invalid hex RGB value");
			}
			else if (RGB.Split(',').Length == 3)
			{
				var parts = RGB.Split(',');
				var r = parts[0];
				ret[0] = Int32.Parse(r);
				var g = parts[0];
				ret[1] = Int32.Parse(r);
				var b = parts[0];
				ret[2] = Int32.Parse(r);
			}
			else throw new Exception("Invalid color value");

			if (ret[0] < 0 || ret[0] > 255)
				throw new Exception("Invalid Red component value");
			if (ret[1] < 0 || ret[1] > 255)
				throw new Exception("Invalid Green component value");
			if (ret[2] < 0 || ret[2] > 255)
				throw new Exception("Invalid Blue component value");
			return ret;
		}

		/// <summary>
		/// Returns either true or false, with a default value.
		/// </summary>
		/// <param name="value"></param>
		/// <param name="defaultValue"></param>
		/// <returns></returns>
		private string GetJsBool(bool? value, bool defaultValue)
		{
			return GetJsBool(value.GetValueOrDefault(defaultValue));
		}

		private string GetJsBool(bool v)
		{
			return v ? "true" : "false";
		}

		#endregion

		protected override void RenderContents(HtmlTextWriter writer)
		{
			writer.WriteBeginTag("div");
			writer.WriteAttribute("id", DialogContainerClientID);

			if (ColorPickerMode == ColorPickerMode.FloatingDialog)
			{
				writer.WriteAttribute("class", "yui-picker-panel");
				writer.WriteAttribute("visibility", "hidden");
				writer.Write(HtmlTextWriter.TagRightChar);

				writer.WriteBeginTag("div");
				writer.WriteAttribute("class", "hd");
				writer.Write(HtmlTextWriter.TagRightChar);
				writer.WriteEncodedText("Please choose a color:");
				writer.WriteEndTag("div");

				writer.WriteBeginTag("div");
				writer.WriteAttribute("class", "bd");
				writer.Write(HtmlTextWriter.TagRightChar);
			}
			else
				writer.Write(HtmlTextWriter.TagRightChar);


			writer.WriteBeginTag("div");
			writer.WriteAttribute("id", ContainerClientID);
			writer.WriteAttribute("style", "position:relative;height:220px");//width:420px;
			writer.Write(HtmlTextWriter.SelfClosingTagEnd);

			if (ColorPickerMode == ColorPickerMode.FloatingDialog)
			{
				writer.WriteEndTag("div"); //close the body div

				writer.WriteBeginTag("div");
				writer.WriteAttribute("class", "ft");
				writer.Write(HtmlTextWriter.TagRightChar);
				//writer.WriteEncodedText("Footer text");        
				writer.WriteEndTag("div");
			}

			writer.WriteEndTag("div");
		}


		const string Code = @"
if (typeof(ASPYUI)=='undefined') 
  ASPYUI = {};
if (typeof(ASPYUI.Callbacks)=='undefined')   
  ASPYUI.Callbacks=[];
if (typeof(ASPYUI.Modules)=='undefined')   
  ASPYUI.Modules={};
if (typeof(ASPYUI.LoadYuiAsync)=='undefined') 
  ASPYUI.LoadYuiAsync = function()
  {
    window.setTimeout(ASPYUI.LoadYui,0);
  }
if (typeof(ASPYUI.LoadYui)=='undefined') 
  ASPYUI.LoadYui = function()
  {
    var modules = [];
    for (var i in ASPYUI.Modules)
      modules.push(i);
    var loader = new YAHOO.util.YUILoader(
    { 
      base: '', 
      require: modules, 
      loadOptional: false, 
      combine: true, 
      filter: 'MIN', 
      allowRollup: true, 
      onSuccess: function() 
      { 
        for (var j=0;j<ASPYUI.Callbacks.length;j++)
        try
        {
          ASPYUI.Callbacks[j]();
        }
        catch(e1)
        {
        }
      }
    }); 
    loader.insert();
  }

ASPYUI.CreateColorPicker = function(  HiddenFieldClientID, 
      ContainerClientID, 
      OnClientValueChanged, 
      CallbackRef, 
      ColorPickerMode, 
      DialogContainerClientID, 
      ModalBehavior, 
      IsHidden,
      ShowControls,
      ShowHexControls,
      ShowHexSummary,
      ShowHSVControls,
      ShowRGBControls,
      ShowWebSafe,
      RGB, //[int,int,int]
      UpdateMode
      )
{
  var picker, dialog, container, hiddenField, valueChangeHandler, hex, revertValueHandler;
  hiddenField = document.getElementById(HiddenFieldClientID);
  container = new YAHOO.util.Element(ContainerClientID);
  new YAHOO.util.Element(document.body).addClass('yui-skin-sam');
  //Create value changed handler
  function valueChangeHandler() 
  {
    hex = picker.get('hex');
    if (HiddenFieldClientID != null)
      hiddenField.value = hex;
    if (OnClientValueChanged != null)
    {
      try
      {
        OnClientValueChanged();
      } 
      catch(e1)
      {
      }
    }
    if (CallbackRef!=null)
    {
      try 
      { 
        eval(CallbackRef+';');
      } 
      catch(e2)
      {
      }
    }  
    }
  
  //Create revert handler
  revertValueHandler = function() {picker.set('hex',dialog._initialPickerValue);}

  function setupControl()
  {
    picker = new YAHOO.widget.ColorPicker(container, 
    {
      showcontrols: ShowControls
      ,showhexcontrols: ShowHexControls
      ,showhexsummary: ShowHexSummary
      ,showhsvcontrols: ShowHSVControls
      ,showrgbcontrols: ShowRGBControls
      ,showwebsafe: ShowWebSafe 
      ,images: 
      {
        PICKER_THUMB: 'http://yui.yahooapis.com/2.7.0/build/colorpicker/assets/picker_thumb.png', 
        HUE_THUMB: 'http://yui.yahooapis.com/2.7.0/build/colorpicker/assets/hue_thumb.png'     
      }
      ,container: ((ColorPickerMode == ColorPickerMode.FloatingDialog) ? dialog : undefined)
    });
    
    container.get('element')._picker = picker;
    if (RGB)
    {
      picker.skipAnim = true;
      picker.setValue(RGB, true);
      picker.skipAnim = false;
    }
    
    if (ColorPickerMode == 'FloatingDialog')
    {
      if (UpdateMode=='Continuous')
      {
        picker.on('rgbChange', valueChangeHandler);
      }
      
    } 
    else if (ColorPickerMode == 'Inline')
    {
      if (UpdateMode == 'Continuous')
        picker.on('rgbChange', valueChangeHandler);        
    }
    hiddenField.value = picker.get('hex');    
    
  }

  if (ColorPickerMode == 'FloatingDialog')
    {
        
        dialog = new YAHOO.widget.Dialog(DialogContainerClientID, 
    { 
        width : '500px',
        close: false,
        fixedcenter : true,
        visible : false,
        modal : ModalBehavior,
        zindex: 4,
        constraintoviewport : true,
        buttons : [ 
          { text:'OK',handler: function(){dialog.cancel(); valueChangeHandler(); }, isDefault:true },
          { text:'Cancel', handler: function(){revertValueHandler(); dialog.cancel(); } } ]
        });
        dialog.showEvent.subscribe(function() { dialog._initialPickerValue=picker.get('hex'); });
        dialog.renderEvent.subscribe(function() {setupControl();});
    dialog.render();
    document.getElementById(DialogContainerClientID).dialog = dialog;    
    if (!IsHidden)
        {
          dialog.show();
        }
      }
      else
      {
        container.addClass('yui-skin-sam');
        if (IsHidden)
          container.setStyle('display','none');
    setupControl();
      }
};";
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Alon.Weiss
Product Manager CodeRun
United States United States
I'm a founder and product manager at CodeRun. I'm interested in C#, Code technology, Javascript, and everything in between.
Aside from these professional interests, I enjoy playing and listening to music, movies, diving, hiking, biking, wine, food, and everything life has to offer.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150414.1 | Last Updated 3 Jun 2009
Article Copyright 2009 by Alon.Weiss
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid