Click here to Skip to main content
15,909,939 members
Articles / Web Development / ASP.NET

ASP.NET Color Picker Web Server Control

Rate me:
Please Sign up or sign in to vote.
4.85/5 (35 votes)
6 Apr 2010CPOL3 min read 266.4K   3K   75   113
An ASP.NET color picker web server control
ColorPickerDemo.png

Introduction

It is difficult to find a decent color picker control for ASP.NET. However, there are plenty of pure JavaScript color picker controls. I decided to take one of them and convert it into an ASP.NET web server control. As a base, I took the dhtmlgoodies advanced color picker.

Project Setup

First, let's do New Project ->ASP.NET Server Control. By default, the name of the project would be the same as the name of the default namespace. I called my project WebControls, and renamed ServerControl1.cs to ColorPicker.cs. I changed default namespace to Karpach.WebControls, as well as assembly name. Then, I added to the project the images, JavaScript, and styles supplied by dhtmlgoodies.

CustomControlSolutionFiles.gif

Then, I clicked on each file's properties and changed the Build Action from Content to Embedded Resource. I also renamed some of the files.

AssemblyInfo.cs

I registered all the resources like this:

C#
[assembly: System.Web.UI.WebResource("Karpach.WebControls.Images.SliderHandle.gif",
    "img/gif")]
[assembly: System.Web.UI.WebResource("Karpach.WebControls.Images.TabCenterActive.gif",
    "img/gif")]
[assembly: System.Web.UI.WebResource("Karpach.WebControls.Images.TabLeftActive.gif",
    "img/gif")]
[assembly: System.Web.UI.WebResource("Karpach.WebControls.Images.TabLeftInactive.gif",
    "img/gif")]
[assembly: System.Web.UI.WebResource("Karpach.WebControls.Images.TabRightActive.gif",
    "img/gif")]
[assembly: System.Web.UI.WebResource("Karpach.WebControls.Images.TabRightInactive.gif",
    "img/gif")]
[assembly: System.Web.UI.WebResource("Karpach.WebControls.Images.ColorPickerIcon.jpg",
    "img/jpeg")]


[assembly: System.Web.UI.WebResource("Karpach.WebControls.Styles.ColorPicker.css",
    "text/css")]

[assembly: System.Web.UI.WebResource("Karpach.WebControls.Javascript.ColorPicker.js",
    "text/js")]

As you might have noticed, System.Web.UI.WebResource, the first parameter has the following signature:

[Assembly Name].[Folder].[File Name]

This is very important, since it is not documented even in MSDN.

ColorPicker.cs

In order to enable Validators for your control, you need to specify ValidationProperty attribute of your server control class.

C#
[DefaultProperty("Color"),ValidationProperty("Color")]

I modified ToolboxData to look like this:

C#
[ToolboxData("<{0}:ColorPicker runat="server">")]

Next, I wanted a custom icon in the Visual Studio Toolbox. After ToolboxData, I added the following line:

C#
[System.Drawing.ToolboxBitmap(typeof(ColorPicker),"Images.color_picker_icon.jpg")]

where the first parameter is the type of the control and the second parameter is the icon file name used in AssemblyInfo.cs.

Originally color picker had two JavaScript files: color_functions.js and js_color_picker_v2.js. I combined them in one file, ColorPicker.js. Those files had a bunch of functions. I combined everything in one JavaScript class, exposed some public properties and one public function ShowColorPicker.

C#
function ColorPicker(options)
{
    // Public properties
    this.FormWidgetAmountSliderHandleImage = options.FormWidgetAmountSliderHandleImage;
    this.TabRightActiveImage = options.TabRightActiveImage;
    this.TabRightInactiveImage = options.TabRightInactiveImage;
    this.TabLeftActiveImage = options.TabLeftActiveImage;
    this.TabLeftInactiveImage = options.TabLeftInactiveImage;
    this.AutoPostBack = options.AutoPostBack;
    this.AutoPostBackReference = options.AutoPostBackReference;
    this.PopupPosition = options.PopupPosition;

    // Public methods

    this.ShowColorPicker = function(inputObj, formField)
    {
	  //.....
	}
	// Private variables
	// ....
	
	// Private functions
    // ....
}

After some feedback, I had to fix JavaScript in order to support ASP.NET AJAX UpdatePanel and ModalPopup extender.

Then, I needed to load the stored resources from the DLL into JavaScript class. The best event for this is OnPreRender:

C#
// Javascript
string colorFunctions = Page.ClientScript.GetWebResourceUrl(typeof(ColorPicker),
			"Karpach.WebControls.Javascript.ColorPicker.js");
Page.ClientScript.RegisterClientScriptInclude("ColorPicker.js", colorFunctions);

// Create ColorPicker object
string script = string.Format(@"
var colorPicker_{0} = new ColorPicker({{
FormWidgetAmountSliderHandleImage : '{1}',
TabRightActiveImage : '{2}',
TabRightInactiveImage : '{3}',
TabLeftActiveImage : '{4}',
TabLeftInactiveImage : '{5}',
AutoPostBack : {6},
AutoPostBackReference : ""{7}"",
PopupPosition : {8}
}});            
", ClientID
 , Page.ClientScript.GetWebResourceUrl(typeof(ColorPicker), 
	"Karpach.WebControls.Images.SliderHandle.gif")
 , Page.ClientScript.GetWebResourceUrl(typeof(ColorPicker), 
	"Karpach.WebControls.Images.TabRightActive.gif")
 , Page.ClientScript.GetWebResourceUrl(typeof(ColorPicker), 
	"Karpach.WebControls.Images.TabRightInactive.gif")
 , Page.ClientScript.GetWebResourceUrl(typeof(ColorPicker), 
	"Karpach.WebControls.Images.TabLeftActive.gif")
 , Page.ClientScript.GetWebResourceUrl(typeof(ColorPicker), 
	"Karpach.WebControls.Images.TabLeftInactive.gif") 
 , AutoPostBack?"true":"false"
 , Page.ClientScript.GetPostBackEventReference(this,"")
 , (int)PopupPosition
 );

Page.ClientScript.RegisterStartupScript(Page.GetType(), 
	String.Format("InitColorPicker_{0}", ClientID), script, true);
if (!DesignMode && Page.Header != null)
{
    RegisterCSSInclude(Page.Header);
}

Where RegisterCSSInclude is the following helper method:

C#
private void RegisterCSSInclude(Control target)
{
    // CSS                   
    bool linkIncluded = false;
    foreach (Control c in target.Controls)
    {
        if (c.ID == "ControlPickerStyle")
        {
            linkIncluded = true;
        }
    }
    if (!linkIncluded)
    {
        HtmlGenericControl csslink = new HtmlGenericControl("link");
        csslink.ID = "ControlPickerStyle";
        csslink.Attributes.Add("href", Page.ClientScript.GetWebResourceUrl
	(typeof(ColorPicker), "Karpach.WebControls.Styles.ColorPicker.css"));
        csslink.Attributes.Add("type", "text/css");
        csslink.Attributes.Add("rel", "stylesheet");
        csslink.EnableViewState = false;
        target.Controls.Add(csslink);
    }
}

Then I overrode the Render event of WebControl class in order to render the control HTML.

C#
using (PlaceHolder plh = new PlaceHolder())
{
    if (DesignMode || Page.Header == null)
        RegisterCSSInclude(plh);
    Table table = new Table();
    table.CellPadding = 0;
    table.CellSpacing = 0;
    table.Rows.Add(new TableRow());
    table.Rows[0].Cells.Add(new TableCell());
    table.Rows[0].Cells.Add(new TableCell());
    table.Rows[0].Cells.Add(new TableCell());
    table.Rows[0].Cells[1].Style.Add(HtmlTextWriterStyle.PaddingRight, "5px");
    HtmlGenericControl txt = new HtmlGenericControl("input");
    txt.EnableViewState = false;
    txt.Attributes.Add("maxlength", "15");
    txt.Attributes.Add("size", "15");
    txt.Attributes.Add("value", Color);
    txt.Attributes.Add("id", ClientID);
    txt.Attributes.Add("name", UniqueID);
    txt.Attributes.CssStyle.Value = "height:17px;padding:2px;";
    table.Rows[0].Cells[0].Controls.Add(txt);
    HtmlGenericControl colorBar = new HtmlGenericControl("div");
    colorBar.EnableViewState = false;
    colorBar.Attributes.CssStyle.Add(HtmlTextWriterStyle.Height, "21px");
    colorBar.Attributes.CssStyle.Add(HtmlTextWriterStyle.Width, "5px");
    colorBar.Attributes.CssStyle.Add("border", "solid 1px #7f9db9");
    colorBar.Attributes.CssStyle.Add(HtmlTextWriterStyle.BackgroundColor, Color);
    table.Rows[0].Cells[1].Controls.Add(colorBar);
    HtmlInputImage btn = new HtmlInputImage();
    btn.Src = Page.ClientScript.GetWebResourceUrl(typeof(ColorPicker), 
		"Karpach.WebControls.Images.ColorPickerIcon.jpg");
    btn.Attributes.Add("onclick", string.Format("colorPicker_{0}.ShowColorPicker
	(this,document.getElementById('{1}'));return false;", ClientID, ClientID));
    btn.Attributes.CssStyle.Add(HtmlTextWriterStyle.ZIndex, "1");
    HtmlGenericControl container = new HtmlGenericControl("div");
    container.EnableViewState = false;
    container.Controls.Add(btn);
    container.Attributes.CssStyle.Add(HtmlTextWriterStyle.Position, "static");
    container.Attributes.CssStyle.Add(HtmlTextWriterStyle.Display, "block");
    table.Rows[0].Cells[2].Controls.Add(container);
    plh.Controls.Add(table);
    plh.RenderControl(output);
}

There are a few ways how you can save postback value of color picker. However I think the best way is to implement IPostBackDataHandler interface.

C#
public bool LoadPostData(string postDataKey,NameValueCollection postCollection)
{
    String presentValue = Color;
    String postedValue = postCollection[postDataKey];

    if (presentValue == null || !presentValue.Equals(postedValue))
    {
        Color = postedValue;
        return true;
    }
    return false;
}

public virtual void RaisePostDataChangedEvent()
{
    OnColorChanged(EventArgs.Empty);
}

public void OnColorChanged(EventArgs e)
{
    if (ColorChanged != null)
        ColorChanged(this, e);
}

LoadPostData method will be called during each postback, when postback form has token with key this.UniqueID. As you can see above, the rendered input tag has name attribute this.UniqueID.

ColorPicker properties are stored in ControlState by overriding LoadControlState and SaveControlState:

C#
protected override void LoadControlState(object savedState)
{
    Color = (string)(savedState as object[])[0];
    AutoPostBack = (bool)(savedState as object[])[1];
    PopupPosition = (PopupPosition)(savedState as object[])[2];
}

protected override object SaveControlState()
{
    object []saveState = new object[3];
    saveState[0] = Color;
    saveState[1] = AutoPostBack;
    saveState[2] = PopupPosition;
    return (object)saveState;
}

You need to invoke the RegisterRequiresControlState method to register control for participation in control state, otherwise LoadControlState and SaveControlState won't fire.

C#
protected override void OnInit(EventArgs e)
{
    base.OnInit(e);
    Page.RegisterRequiresControlState(this);
}

Build.proj

Now, the final touch: Minification of JavaScript and CSS. I used Yahoo YUI compressor and Microsoft MSBuild. Here is the final MSbuild file:

XML
<?xml version="1.0" encoding="utf-8" ?>
<Project ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>
    <MSBuildCommunityTasksPath>..\References</MSBuildCommunityTasksPath>
    <ProjectName>WebControls</ProjectName>    
  </PropertyGroup>
  <Target Name="Compress">

    <Message Text="Create temp files ..." />
    <Copy SourceFiles=".\$(ProjectName)\Javascript\ColorPicker.js"
        DestinationFiles=".\$(ProjectName)\Javascript\ColorPicker.js.full"/>    
    <Copy SourceFiles=".\$(ProjectName)\Styles\ColorPicker.css"
        DestinationFiles=".\$(ProjectName)\Styles\ColorPicker.css.full"/>    
    <Exec Command=
       "java -jar yuicompressor-2.4.2.jar --type js .\$(ProjectName)\
       Javascript\ColorPicker.js.full >.\$(ProjectName)\Javascript\ColorPicker.js"/>

    <Exec Command=
       "java -jar yuicompressor-2.4.2.jar --type css .\$(ProjectName)\Styles\
       ColorPicker.css.full >.\$(ProjectName)\Styles\ColorPicker.css"/>
  </Target>
  <Import Project=".\References\MSBuild.Community.Tasks.targets" />
  <Target Name="Build" DependsOnTargets="Compress">

    <Message Text="Building Project" />
    <MSBuild Projects="./$(ProjectName)/$(ProjectName).sln"
        Properties="Configuration=Release;Platform=Any CPU" />    
  </Target>
</Project>

Now you even don't need Visual Studio to compile DLL. All that you need is .NET 2.0 installed and then the following console script will do the compilation:

%SystemRoot%\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe  Build.proj /t:Build

Per requests below, I added PopupPosition property, so you can specify position of ColorPicker popup (top left, top right, bottom left, bottom right - default).

I also added AutoPostBack property and ColorChanged event.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer comScore
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralGreat control! Pin
Sandeep Mewara13-Mar-10 22:54
mveSandeep Mewara13-Mar-10 22:54 
GeneralRe: Great control! Pin
Viktar Karpach4-Apr-10 17:27
Viktar Karpach4-Apr-10 17:27 
GeneralRe: Great control! Pin
Sandeep Mewara26-Jun-10 6:27
mveSandeep Mewara26-Jun-10 6:27 
GeneralProblem when using control inside custom control, then multiple instances of that control Pin
Magick9316-Feb-10 21:39
Magick9316-Feb-10 21:39 
GeneralRe: Problem when using control inside custom control, then multiple instances of that control Pin
Viktar Karpach18-Feb-10 17:52
Viktar Karpach18-Feb-10 17:52 
GeneralRe: Problem when using control inside custom control, then multiple instances of that control Pin
Magick9319-Feb-10 10:33
Magick9319-Feb-10 10:33 
GeneralRe: Problem when using control inside custom control, then multiple instances of that control Pin
Viktar Karpach4-Apr-10 17:28
Viktar Karpach4-Apr-10 17:28 
QuestionAwesome Control! A few bugs? Or just me? Pin
bjahelka29-Jan-10 8:34
bjahelka29-Jan-10 8:34 
Viktar, this control is totally bitchin. Thanks for taking the time to code it!

I'm using multiple ColorPickers on my page. At first it appeared that the PopupPosition property wasn't working, then I found out that if you set ALL of the ColorPicker controls to have the same PopupPosition, then it will work. Wondering why I couldn't select each one independently.

The above behavior was noticed because the popup was being cut off on my page. I have these controls on the left side of the page (in a div), and on the right side I have a preview pane with the proposed color changes (also in a div). When the color picker poped up, it would only show in the part of the color picker in the containing div. I thought maybe it had to do with a funky z-order being set somewhere in my css. Couldn't find anything screwy, and besides, the style on this control had a z-order of 10001, so it ~should~ show. If you have any idea on that, I'd like to hear it.

Would be great to get this to work with the RegularExpressionValidator. Is there some default property setting that could be implemented with this custom control to make it work with the built in ASP.NET Validators? Or do they just plain not work with custom controls (what about exposing the textbox inside the ColorPicker somehow)?

Would be very nice if the sliders would be pre-set to the value that is already in the text box.

Click the icon to open the color picker, would be nice if you clicked it again it would close the color picker.

I'm going to tackle the last two and see if I can figure out how to make those work and will post code if I figure something out. But the first two are a little beyond what I know to be sure.

Thanks again for the wonderful control!
AnswerRe: Awesome Control! A few bugs? Or just me? Pin
Viktar Karpach18-Feb-10 17:41
Viktar Karpach18-Feb-10 17:41 
AnswerRe: Awesome Control! A few bugs? Or just me? Pin
Viktar Karpach4-Apr-10 17:29
Viktar Karpach4-Apr-10 17:29 
GeneralColorPicker in gridview EditItemTemplate Pin
judyIsk21-Jul-09 22:45
judyIsk21-Jul-09 22:45 
GeneralRe: ColorPicker in gridview EditItemTemplate Pin
Viktar Karpach23-Jul-09 3:36
Viktar Karpach23-Jul-09 3:36 
GeneralRe: ColorPicker in gridview EditItemTemplate Pin
judyIsk25-Jul-09 21:14
judyIsk25-Jul-09 21:14 
GeneralRe: ColorPicker in gridview EditItemTemplate Pin
Viktar Karpach26-Jul-09 12:07
Viktar Karpach26-Jul-09 12:07 
GeneralBug in Slider Pin
Member 540677930-Jun-09 5:20
Member 540677930-Jun-09 5:20 
GeneralRe: Bug in Slider Pin
Viktar Karpach1-Jul-09 6:44
Viktar Karpach1-Jul-09 6:44 
GeneralMultiple ColorPickers on same page Pin
Lee Walker12127-Mar-09 0:52
Lee Walker12127-Mar-09 0:52 
GeneralRe: Multiple ColorPickers on same page Pin
ankit_vyas21-Apr-09 18:48
ankit_vyas21-Apr-09 18:48 
GeneralRe: Multiple ColorPickers on same page Pin
Lee Walker12121-Apr-09 21:28
Lee Walker12121-Apr-09 21:28 
GeneralRe: Multiple ColorPickers on same page Pin
Viktar Karpach22-Apr-09 2:34
Viktar Karpach22-Apr-09 2:34 
GeneralRe: Multiple ColorPickers on same page Pin
ankit_vyas22-Apr-09 12:36
ankit_vyas22-Apr-09 12:36 
GeneralRe: Multiple ColorPickers on same page Pin
Viktar Karpach23-Apr-09 19:42
Viktar Karpach23-Apr-09 19:42 
GeneralRe: Multiple ColorPickers on same page Pin
ankit_vyas25-Apr-09 13:12
ankit_vyas25-Apr-09 13:12 
GeneralRe: Multiple ColorPickers on same page Pin
Lee Walker1211-May-09 0:33
Lee Walker1211-May-09 0:33 
GeneralValidating the control Pin
Member 124714918-Mar-09 23:10
Member 124714918-Mar-09 23:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.