WynApse Home Page
Home    Blog    About    Contact       
Latest Article:


My Tags:
My Sponsors:







Lifetime Member:

Silverlight Color Picker Utility



[OORRGGBB]:   



Introduction

While working on the gradient tutorials, I had played around with setting the final color of the radial gradient by clicking somewhere in the linear one. This got pretty complicated, and I decided it definitely didn't belong in the tutorial. Somewhere along the line, I thought it would be great to have a color picker written in Silverlight.

This is also the very first time I've had a need to input something from the user. I've harped and harped about the fact that not everything *has* to be in Silverlight, and now I've got an example of it.

I used a standard HTML input control and button to input a colorref in case you want to start with some value. My "Copy to Clipboard" button, however, is all Silverlight, and I customized it to pretty closely match the other one (in XP). I wanted a darker gradient on the button though, so mine is a bit darker. Interesting effect here being that depending upon the operating system you're running, the buttons will either look close (XP) or not... but in all cases, the Silverlight button will stay constant :)


New Paradigm

Another thing I've harped on is the fact that the paradigms for Silverlight doesn't have to match *anything* (IMHO). I've even made the statement either in my articles or on the forum to "invent a new paradigm". So I've done that on this page!

I don't know if I'll have a need to use it again, but I sort of like the way the Color controls work. Wherever you start out with a color, when you mouse down into one of the color controls, that is where you begin. If you don't set a colorref, all the controls are set to FF, so all you can do is move the mouse left to decrease the color. Once you have something other than FF for a control, then you can move left and right to change it appropriately. I thought it was an intuitive action. I'll be interested in hearing what other folks think.


Paradigm Downside

And of course there's a downside. The canvas has to be big enough to support the width you want to deal with, so I had to make the canvas be 600 wide and put the controls in the center to give me 255 each way with a little breathing room. You can still go off the canvas at which point the control loses focus, but at least it works :)


Opacity

I wanted to include the Opacity as well, since that's available to us in Silverlight. I thought it would be cool to place the Silverlight logo onto the html page beneath the canvas, and have the graphic just be hidden by the square of color. It gives an interesting effect to the whole process and of course, using your own graphic would help find colors that work good for you.


UI Touches

At first my thought was to do a very cool border for the whole thing, but that was before I realized I needed the empty realestate on the canvas, so that went away :(

As with most examples, the fact that I wanted to 'feed' a colorref into the application came after the fact and then of course the idea continued that I'd want to copy what I had created to the clipboard, so that got added, but I didn't like using a hyperlink for the "Copy to Clipboard", and once I had a rectangle up, I decided to try to match up the XP button in the HTML for "Set Colorref". This included a gradient across the button and an inner stroke for the rollover effect that I actually used the application to find :) ... "eating your own dogfood"

Another thing that I wanted was a 'glint' on the color controls. They're too small to try to get a 'roundness' effect on and still retain the bright color, so I satisfied myself with an ellipse set inside the color ellipse that has a radial gradient that runs from white to the control color. I like the effect. I think it gives it a little more personality.


Summary

Not much more can be said about this. The Java Script is pretty straightforward. I have the button code commented up, and the button and color controls share just a little because of the mouse capture effects. Nothing new there to discuss.

After I thought about this, I actually wondered why I haven't seen one of these come across yet, maybe because the underlying code is too simple :)

As always, the Java Script and XAML for producing this page is shown below. I don't think there are any problems, but if anyone finds something that doesn't work, let me know... plus I'm always open to suggestions for article or tutorial topics :)


JavaScript for producing this page:


<script type="text/javascript">
var sCaptured = "";
var Red = 255;
var Green = 255;
var Blue = 255;
var Opacity = 255;
var beginX = 0;
var fIsMouseDown = false;
var currX = 0;
var sRed = sGreen = sBlue = sOpacity = "ff";
var Host = null;
var sState = "Normal";
var valid = '0123456789ABCDEFabcdef'; // define valid characters

function MouseExitCanvas(sender, args)
{
sCaptured = "";
sState = "Normal"
}

function CanvasLoaded(sender, args)
{
// Hang onto the value of the sender here
// to use it to find objects later
Host = sender;
}

function SetColorref()
{
// Only allow 8 characters rather than mess with it
if (textbox.value.length != 8)
{
alert("Enter a standard Hex colorref of 6 characters, with a 2-character hex preceeding Opacity");
return;
}

// check for valid hex
if (!isValid(textbox.value, valid))
{
alert("Enter a standard Hex colorref of 6 characters, with a 2-character hex preceeding Opacity");
return;
}
var sColor = textbox.value;

// Set the text display and the color box to the value input
Host.findName("Colorref").Text = "#" + sColor.toUpperCase();
Host.findName("ColorBox").Fill = "#" + sColor.toUpperCase();

// Peel the Opacity off
sOpacity = sColor.substring(0,2);
Host.findName("OpacityValue").Text = sOpacity.toUpperCase();
Opacity = parseInt(sOpacity, 16);

// Now Red
sRed = sColor.substring(2,4);
Host.findName("RedValue").Text = sRed.toUpperCase();
Red = parseInt(sRed, 16);

// Green is next
sGreen = sColor.substring(4,6);
Host.findName("GreenValue").Text = sGreen.toUpperCase();
Green = parseInt(sGreen, 16);

// Blue is left
sBlue = sColor.substring(6);
Host.findName("BlueValue").Text = sBlue.toUpperCase();
Blue = parseInt(sBlue, 16);
}

// Found this function on HTML Goodies
function isValid(string,allowed)
{
for (var i=0; i< string.length; i++)
{
if (allowed.indexOf(string.charAt(i)) == -1)
{
return false;
}
}
return true;
}

// Shared by all 4 controls
// Capture the mouse, set the state, save x position
function ControlLeftMouseDown(sender, mouseEventArgs)
{
sender.CaptureMouse();
sCaptured = sender.name;

fIsMouseDown = true;

beginX = mouseEventArgs.getPosition(null).x;
}

// Shared by all 4 controls
// Release the mouse, reset states
function ControlLeftMouseUp(sender, mouseEventArgs)
{
sender.ReleaseMouseCapture();
sCaptured = "";
fIsMouseDown = false;
}

// Shared by all 4 controls
// Get current position
// Modify value of control based on where we are
// Reset beginX to be 'here'
// check for limits
// Calculate value and set the indicator
// At the bottom, merge into colorref for indicator and color box
function ControlMouseMove(sender, mouseEventArgs)
{
if (fIsMouseDown)
{
// Retrieve the current position of the mouse.
var currX = mouseEventArgs.getPosition(null).x;

switch (sCaptured)
{
case "OpacityCanvas":
Opacity = Opacity - (beginX - currX);
beginX = currX;
if (Opacity > 255)
{
Opacity = 255;
}
else if (Opacity < 0)
{
Opacity = 0;
}

sOpacity = (Math.floor(Opacity)).toString(16);
if (Opacity < 16)
{
sOpacity = "0" + sOpacity;
}
sender.findName("OpacityValue").Text = sOpacity.toUpperCase();
break;

case "RedCanvas":
Red = Red - (beginX - currX);
beginX = currX;
if (Red > 255)
{
Red = 255;
}
else if (Red < 0)
{
Red = 0;
}

sRed = (Math.floor(Red)).toString(16);
if (Red < 16)
{
sRed = "0" + sRed;
}
sender.findName("RedValue").Text = sRed.toUpperCase();
break;

case "GreenCanvas":
Green = Green - (beginX - currX);
beginX = currX;
if (Green > 255)
{
Green = 255;
}
else if (Green < 0)
{
Green = 0;
}

sGreen = (Math.floor(Green)).toString(16);
if (Green < 16)
{
sGreen = "0" + sGreen;
}
sender.findName("GreenValue").Text = sGreen.toUpperCase();
break;

case "BlueCanvas":
Blue = Blue - (beginX - currX);
beginX = currX;
if (Blue > 255)
{
Blue = 255;
}
else if (Blue < 0)
{
Blue = 0;
}

sBlue = (Math.floor(Blue)).toString(16);
if (Blue < 16)
{
sBlue = "0" + sBlue;
}
sender.findName("BlueValue").Text = sBlue.toUpperCase();
break;
}

var sColor = "#"+ sOpacity + sRed + sGreen + sBlue;
sender.findName("ColorBox").Fill = sColor;
sender.findName("Colorref").Text=sColor;
}

}

// Begin Button Handlers

// sCaptured is set to name of control
// sState is "Pressed" or "Normal"

// Mouse Up -- only do the function if we 'have' the mouse
// in any case, release capture and reset states
function MouseButtonUp(sender, args)
{
if ((sCaptured == "Button") && (sState == "Pressed"))
{
sender.releaseMouseCapture();
sCaptured = "";

// reset button to original position
MoveButtonBack(sender.findName("CopyCanvas"));

// Set value for clipboard copy
holdtext.innerText = Host.findName("Colorref").Text.toUpperCase();
Copied = holdtext.createTextRange();
Copied.execCommand("Copy");
}
else
{
sender.releaseMouseCapture();
sCaptured = "";
sState = "Normal"
}
}

// Capture mouse, set states, move button to pressed position
function MouseButtonDown(sender, args)
{
sender.captureMouse();
sCaptured = "Button";
sState = "Pressed";
MoveButtonOver(sender.findName("CopyCanvas"));

}

// Set rollover effect
// if we 'have' the mouse, move the button to pressed position
function MouseEnterButton(sender, args)
{
sender.findName("ButtonEffect").Visibility="Visible";
if (sCaptured == "Button")
{
MoveButtonOver(sender.findName("CopyCanvas"));
sState = "Pressed";
}
}

// Remove rollover effect
// if we 'have' the mouse, move the button to original position
function MouseLeaveButton(sender, args)
{
sender.findName("ButtonEffect").Visibility="Collapsed";
if (sCaptured == "Button")
{
MoveButtonBack(sender.findName("CopyCanvas"));
sState = "Normal";
}
}

// Common function to move the button to the right and down
function MoveButtonOver(button)
{
button["Canvas.Left"] = button["Canvas.Left"] + 1;
button["Canvas.Top"] = button["Canvas.Top"] + 1;
}

// Common function to move the button to original position
function MoveButtonBack(button)
{
button["Canvas.Left"] = button["Canvas.Left"] - 1;
button["Canvas.Top"] = button["Canvas.Top"] - 1;
}

// End of Button handlers

</script>


XAML for producing this page:


<Canvas
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Loaded="CanvasLoaded">

<Rectangle Canvas.Left="0" Canvas.Top="0" Width="600" Height="320" Stroke="White" StrokeThickness="1" MouseLeave="MouseExitCanvas" />

<Canvas Canvas.Top="0" Canvas.Left="0">
<TextBlock FontSize="14" Canvas.Left="230" Canvas.Top="27" Text="Opacity:" />
<Canvas x:Name="OpacityCanvas" Canvas.Left="288" Canvas.Top="25" MouseLeftButtonDown="ControlLeftMouseDown" MouseMove="ControlMouseMove" MouseLeftButtonUp="ControlLeftMouseUp" Cursor="Hand" >
<Ellipse x:Name="OpacityControl" Width="25" Height="25" StrokeThickness="1" Stroke="Black" Fill="#FF000000" />
<Ellipse Width="6" Height="6" Canvas.Left="6" Canvas.Top="6" Opacity="1" >
<Ellipse.Fill>
<RadialGradientBrush GradientOrigin="0, 0" Center="0.5,0.5">
<GradientStop Color="White" Offset="0" />
<GradientStop Color="#FF000000" Offset="2" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Canvas>
<TextBlock x:Name="OpacityValue" FontSize="14" Canvas.Left="320" Canvas.Top="27" Text="FF" />

<TextBlock FontSize="14" Canvas.Left="230" Canvas.Top="57" Text="Red: " />
<Canvas x:Name="RedCanvas" Canvas.Left="288" Canvas.Top="55" MouseLeftButtonDown="ControlLeftMouseDown" MouseMove="ControlMouseMove" MouseLeftButtonUp="ControlLeftMouseUp" Cursor="Hand">
<Ellipse x:Name="RedControl" Width="25" Height="25" StrokeThickness="1" Stroke="Black" Fill="#FFFF0000" />
<Ellipse Width="6" Height="6" Canvas.Left="6" Canvas.Top="6" Opacity="1" >
<Ellipse.Fill>
<RadialGradientBrush GradientOrigin="0, 0" Center="0.5,0.5">
<GradientStop Color="White" Offset="0" />
<GradientStop Color="#FFFF0000" Offset="2" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Canvas>
<TextBlock x:Name="RedValue" FontSize="14" Canvas.Left="320" Canvas.Top="57" Text="FF" />

<TextBlock FontSize="14" Canvas.Left="230" Canvas.Top="87" Text="Green: " />
<Canvas x:Name="GreenCanvas" Canvas.Left="288" Canvas.Top="85" MouseLeftButtonDown="ControlLeftMouseDown" MouseMove="ControlMouseMove" MouseLeftButtonUp="ControlLeftMouseUp" Cursor="Hand">
<Ellipse x:Name="GreenControl" Width="25" Height="25" StrokeThickness="1" Stroke="Black" Fill="#FF00FF00" />
<Ellipse Width="6" Height="6" Canvas.Left="6" Canvas.Top="6" Opacity="1" >
<Ellipse.Fill>
<RadialGradientBrush GradientOrigin="0, 0" Center="0.5,0.5">
<GradientStop Color="White" Offset="0" />
<GradientStop Color="#FF00FF00" Offset="2" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Canvas>
<TextBlock x:Name="GreenValue" FontSize="14" Canvas.Left="320" Canvas.Top="87" Text="FF" />

<TextBlock FontSize="14" Canvas.Left="230" Canvas.Top="117" Text="Blue: " />
<Canvas x:Name="BlueCanvas" Canvas.Left="288" Canvas.Top="115" MouseLeftButtonDown="ControlLeftMouseDown" MouseMove="ControlMouseMove" MouseLeftButtonUp="ControlLeftMouseUp" Cursor="Hand">
<Ellipse x:Name="BlueControl" Width="25" Height="25" StrokeThickness="1" Stroke="Black" Fill="#FF0000FF" />
<Ellipse Width="6" Height="6" Canvas.Left="6" Canvas.Top="6" Opacity="1" >
<Ellipse.Fill>
<RadialGradientBrush GradientOrigin="0, 0" Center="0.5,0.5">
<GradientStop Color="White" Offset="0" />
<GradientStop Color="#FF0000FF" Offset="2" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Canvas>
<TextBlock x:Name="BlueValue" FontSize="14" Canvas.Left="320" Canvas.Top="117" Text="FF" />


<Rectangle x:Name="ColorBox" Canvas.Left="248" Canvas.Top="145" Width="104" Height="104" Stroke="Black" StrokeThickness="1" Fill="#FFFFFFFF" />
</Canvas>

<TextBlock FontSize="14" Canvas.Left="17" Canvas.Top="258" Foreground="Black" Text="Colorref:" />
<TextBlock x:Name="Colorref" FontSize="14" Canvas.Left="80" Canvas.Top="259" Foreground="Blue" Text="#ffffffff" />

<Canvas x:Name="CopyCanvas" Canvas.Left="17" Canvas.Top="280" Cursor="Hand" MouseLeftButtonUp="MouseButtonUp" MouseLeftButtonDown="MouseButtonDown" MouseEnter="MouseEnterButton" MouseLeave="MouseLeaveButton">
<Rectangle Width="145" Height="22" Stroke="Black" StrokeThickness="1" RadiusX="3" RadiusY="3" >
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="White" Offset="0.5" />
<GradientStop Color="Gray" Offset="1.5" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Name="ButtonEffect" Width="143" Height="20" Canvas.Top="1" Canvas.Left="1" Stroke="#FFFFCC00" StrokeThickness="2" RadiusX="3" RadiusY="3" Visibility="Collapsed"/>
<TextBlock x:Name="CopyText" FontSize="14" Canvas.Left="6" Canvas.Top="1" Foreground="Black" Text="Copy To Clipboard" />
</Canvas>

</Canvas>


Microsoft MVP

Member of WPF and Silverlight Insiders
Silverlight Control
Creative Commons License

Copyright © 2006-2014, WynApse