ColorPicker Control for WPF/Silverlight


A while back I was looking around for a color picker control for Live Geometry. The ColorPicker from http://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


Comments (8)

  1. Jack says:

    When I compile the program it prompts me an error that "The tag ‘VisualStateGroup’ does not exist in XML namespace ‘clr-namespace:System.Windows;assembly=System.Windows’. int the ..themesgeneric.xaml"

  2. 1. Are you using Silverlight 3 / VS 2008 SP1 / Silverlight 3 Tools?

    2. Themes/generic.xaml should have Build Action: Page in it’s Properties

    3. Try Clean Solution/Rebuild Solution

    4. In generic.xaml, make sure xmlns:sc="clr-namespace:SilverlightContrib.Controls;assembly=ColorPickerDemo" > contains the name of your assembly (ColorPickerDemo in my example)

    Does that help?

  3. ZodiacIs69 says:

    For the Expand and Collapse functionality you put in was that just on the StackPanels themselves? Hiding the lower stackpanel containing the color selection and leaving the top stackpanel?

    That is how I set mine up but it wants to stretch the top stackpanel to the full height of the ColorPicker…  Wanted to see if you could clarify how you did it.

  4. Pedro says:

    getting the same problem.

    Error 1 The tag ‘VisualStateGroup’ does not exist in XML namespace ‘clr-namespace:System.Windows;assembly=System.Windows’. Line 111 Position 30.

    the name space is set:

    xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"

  5. Paul says:

    Is there a way to set the initial color as a specific RGB color?

  6. Paul, just set the SelectedColor property

  7. Pedro and ZodiacIs69: I've rewritted the control in C# only (no XAML). Full source code is at:

    livegeometry.codeplex.com/…/56298

  8. prespic says:

    Hi, i am using the source code in your last post. First i want to thank you – good job. The Picker is working, but i cant bind them.

    Here is code in XAML where is used:

    <tools:ColorPicker.SelectedColor>

       <MultiBinding Converter="{StaticResource RgbConverter}" Mode="TwoWay">

           <Binding Path="fontColorR" Source="{x:Static prop:Settings.Default}" />

           <Binding Path="fontColorG" Source="{x:Static prop:Settings.Default}" />

           <Binding Path="fontColorB" Source="{x:Static prop:Settings.Default}" />

           <Binding Path="fontColorA" Source="{x:Static prop:Settings.Default}" />

       </MultiBinding>

    </tools:ColorPicker.SelectedColor>

    I have no errors, no crashes. But if Picer show (collapsed), he is black. When i expand, he take the color from binding, but this is the end. From here it behave like no bounded.

    I found the code whitch change color after expanding, on line 227:

                       Dispatcher.BeginInvoke(new Action(() => SelectedColor = SelectedColor), DispatcherPriority.Render);

    Maybe it helps.

    Hoping you can help me, bye and have a nice day. Prespic

Skip to main content