ColorPicker Control for WPF/Silverlight

A while back I was looking around for a color picker control for Live Geometry. The ColorPicker from https://silverlightcontrib.codeplex.com was exactly what I was looking for:

Get Microsoft Silverlight

(live preview needs Silverlight 3.0)

Using the control in your code

I just took the source from CodePlex and embedded it in my project. You need 5 files:

image

Alternatively, you can reference the binary which you can download from the SilverlightContrib CodePlex project site. Pay attention that generic.xaml contains the template for the control, so don’t forget the xaml. The control will work just fine with WPF and Silverlight, which is really a great thing, especially if you’re multitargeting.

To include the control in your application, here’s the basic code:

 <sc:ColorPicker SelectedColor="LightGreen" />

Don’t forget to add an XML namespace:

 xmlns:sc="clr-namespace:SilverlightContrib.Controls"

How does the gradient work?

The source code for this control is very good for educational purposes. For instance, I had no idea how they create the big gradient for every possible hue. Well, it’s genius and it’s simple. In generic.xaml:

 <Canvas Canvas.Top="0" Canvas.Left="20">
    <Rectangle x:Name="ColorSample" Width="180" Height="180" Fill="Red"></Rectangle>
    <Rectangle x:Name="WhiteGradient" IsHitTestVisible="False" Width="180" Height="180">
        <Rectangle.Fill>
            <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                <GradientStop Offset="0" Color="#ffffffff"/>
                <GradientStop Offset="1" Color="#00ffffff"/>
            </LinearGradientBrush>
        </Rectangle.Fill>
    </Rectangle>
    <Rectangle x:Name="BlackGradient" IsHitTestVisible="False" Width="180" Height="180">
        <Rectangle.Fill>
            <LinearGradientBrush StartPoint="0,1" EndPoint="0, 0">
                <GradientStop Offset="0" Color="#ff000000"/>
                <GradientStop Offset="1" Color="#00000000"/>
            </LinearGradientBrush>
        </Rectangle.Fill>
    </Rectangle>
    <Canvas x:Name="SampleSelector" IsHitTestVisible="False" Width="10" Height="10" Canvas.Left="100" Canvas.Top="96">
        <Ellipse Width="10" Height="10" StrokeThickness="3" Stroke="#FFFFFFFF"/>
        <Ellipse Width="10" Height="10" StrokeThickness="1" Stroke="#FF000000"/>
    </Canvas>
</Canvas>

This canvas contains layers like in a cake. ZIndex of objects is stacked bottom to top, so the solid rectangle with initially red background is on the bottom.

Above it, there is a horizontal white gradient fill, completely white on the left and completely transparent on the right.

Above it, there is a vertical black gradient fill, completely black on the bottom and completely transparent on the top.

As these gradients overlay, the transparency over the initial solid background creates the desired effect – the actual color is in top-right, where both gradients are 100% transparent. The white spot is in top-left, where the white gradient is most intense and black gradient fades out. Same for the black edge of the gradient.

Also, it is well worth studying how the template is written – I learned a lot from this sample.

My fixes

Since I conveniently borrowed the source code for my project, I did several fixes for my own purposes. Ideally I should contribute the fixes back to SilverlightContrib, but I can never get around to it.

First of all, I reordered the two StackPanels in the template so that the actual selected color is on top. I also made it collapsible and collapsed by default. You can expand the control by clicking it like a combobox. Unlike a combobox, you have to explicitly click the color area to collapse it again.

Get Microsoft Silverlight

I’ve enabled this by adding an Expanded property:

 public bool Expanded
{
    get
    {
        return m_chooserArea.Visibility == Visibility.Visible;
    }
    set
    {
        var visibility = value ? Visibility.Visible : Visibility.Collapsed;
        if (m_chooserArea.Visibility == visibility)
        {
            return;
        }
        m_chooserArea.Visibility = visibility;
    }
}

When clicking on the color area, I just call Enabled = !Enabled to toggle it and it does the magic for me. The default value for this is obviously the default visibility of m_chooserArea, which is specified in XAML template (Visibility=”Visible” to set to true by default).

Other fixes are not as interesting. I fixed a division by zero in ColorSpace.ConvertRgbToHsv (they had h = 60 * (g - b) / (max - min); and didn’t check if min == max). There are a couple of other things which I don’t remember off the top of my head. I’d have to view TFS history to remember what those were. If you’re willing to help and incorporate these fixes to the original project, I’ll dig this up, just let me know :)

Conclusion

Both Sara Ford and myself agree that this control deserves both thumbs up:

image