Click here to Skip to main content
15,883,623 members
Articles / Desktop Programming / WPF

WPF Simple ColorPicker Control

Rate me:
Please Sign up or sign in to vote.
4.41/5 (16 votes)
4 Feb 2009CPOL6 min read 65.9K   4.1K   29   9
WPF Simple ColorPicker control with color change notification

ColorPicker.PNG

ColorPickerSample.PNG

Introduction

This is a simple ColorPicker control. It allows you to pick a color from a specified range. This ColorPicker includes some event wrappers for CurrentColorChanged and LastColorChanged. So when the value changes, the respective event will be thrown.

Background

The ColorPicker is split in four main parts:

  1. ColorPickerUnit
  2. ColorSelectorUnit
  3. ColorBarUnit
  4. ColorInfoUnit

The ColorPickerUnit is the left square, the ColorSelectorUnit describes the centered square in the image you can see, the ColorBarUnit is at the bottom, and shows the Current and Last selected color. Last but not least is the ColorInfoUnit which shows you the RGB and HEX value of the color. The main idea of this project was to clip the mouse to the ColorPicker or ColorSelectorUnit in the way which one has been clicked. So what I mean is, the mouse gets "jailed", you can't move out of this location. My second idea was to set the mouse to the last position wherever you clicked one of these units.

So far so good!

Using the Code

My project includes the following classes:

  • _ColorPicker.xaml with codefile: This one is the root element. It includes the basic events and the units mentioned above. So I can say this class controls the main user interaction.
  • CopyBox.cs: This class inherits from border, and the border includes a textbox, and one property called value, which sets or gets the current value of the CopyBoxes. That's all.
  • MouseClipping.cs : This is one tricky class. It allows to clip the mouse to any UIElement. This is possible by using an API-Method called ClipCursor which is stored in the user32.dll.
  • MouseControling.cs : This one also includes some API calls -> SetCursorPos, GetCursorPos, GetPixel, GetWindowDC and ReleaseDC.
  • ValueBox.cs : This class has some properties -> Maximum and Value, and some events. When you set the value property, the class calculates the length of a border. So you can see the value stored in a textbox and the position in graphic mode.

Let's Dig Deeper!

Here is some important code of the _ColorPicer.xaml.cs.

A Dependency Property Implementation

In practice, dependency properties are just normal .NET properties hooked into some extra WPF infrastructure. This is all accomplished via WPF APIs; no .NET languages (other than XAML) has an intrinsic understanding of a dependency property.

In my case, I used the DependencyProperties for getting a notification if Current or last Color changed. For an example, I registered a DependencyProperty called IsCurrentColorProperty. The first parameter is the name of the property, the second one is the type of the property. I used color because the value which will be stored in it will be a RGB color. Typeof _ColorPicker means that the property is registered on the _ColorPicker class. The last one is an FrameworkPropertyMetadata. The first parameter is my default color, which is white, and the last one is an PropertyChangedCallback which call the method OnIsCurrentColorChanged.

A Routed Event Implementation

Just as dependency properties are represented as public static DependencyProperty fields with a conventional Property suffix, routed events are represented as public static RoutedEvent fields with a conventional Event suffix. The routed event is registered much like a dependency property in the static constructor, and a normal .NET event-or event wrapper-is defined to enable more familiar use from procedural code and adding a handler in XAML with event attribute syntax.

Now you can think why he registered an RoutedEvent. He already has a DependencyProperty with a ChangeCallback. That is right but when you use this ColorPicker control, you want to write your own code when some color changes. So my Routed events take four parameters. First the name of the Event, that's the name of the event you will find. The second one is the RoutingStrategy. I used Tunnel which means that the event is first raised on the root, then on each element down the tree until the source element is reached. Typeof RoutedEventHandler simply describes the type of the RoutedEvent, and last typeof _ColorPicker means that the holder of this RoutedEvent is the class _ColorPicker. Slightly below, you will find the RoutedEventWraper. It allows you to add or remove the EventHandler.

C#
#region DependencyProperty - RoutedEvets declaration
       private static readonly DependencyProperty IsCurrentColorProperty =
      DependencyProperty.Register(
           "IsCurrentColor", typeof(Color), typeof(_ColorPicker),
           new FrameworkPropertyMetadata(Color.FromRgb(255, 255, 255),
           new PropertyChangedCallback(OnIsCurrentColorChanged)));

       private static readonly RoutedEvent CurrentColorChangedEvent =
      EventManager.RegisterRoutedEvent(
           "CurrentColorChanged",
           RoutingStrategy.Tunnel,
           typeof(RoutedEventHandler), typeof(_ColorPicker));

       private static readonly DependencyProperty IsLastColorProperty =
      DependencyProperty.Register(
           "IsLastColor", typeof(Color), typeof(_ColorPicker),
           new FrameworkPropertyMetadata(Color.FromRgb(255, 255, 255),
           new PropertyChangedCallback(OnIsLastColorChanged)));

       private static readonly RoutedEvent LastColorChangedEvent =
      EventManager.RegisterRoutedEvent(
           "LastColorChanged",
           RoutingStrategy.Tunnel,
           typeof(RoutedEventHandler),typeof(_ColorPicker));
#endregion

Routed Events Wrappers

As with a property wrapper, an event wrapper must not do anything in its accessors other than call AddHander and RemoveHandler:

C#
#region Event wrappers
       /// <summary>
       /// Occurs when the current color changed.
       /// </summary>
       public event RoutedEventHandler CurrentColorChanged
       {
           add { AddHandler(CurrentColorChangedEvent, value); }
           remove { RemoveHandler(CurrentColorChangedEvent, value); }
       }
       /// <summary>
       /// Occurs when the last color changed.
       /// </summary>
       public event RoutedEventHandler LastColorChanged
       {
           add { AddHandler(LastColorChangedEvent, value); }
           remove { RemoveHandler(LastColorChangedEvent, value); }
       }
#endregion

Change Notification

Whenever the value of a dependency property changes, WPF can automatically trigger a number of actions depending on the property´s metadata. These actions can be re-rendering the appropriate elements, updating the current layout, refreshing data binding, and much more.

In my case, I call a method SetColorInfo which takes a color. This method sets the text of the ValueBoxes and the CopyBoxes. Also I set the _currentColor.Background and very importantly, I raise my RoutedEvent.

C#
private static void OnIsCurrentColorChanged
    (DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            SetColorInfos((Color)e.NewValue);
            _currentColor.Background = new SolidColorBrush((Color)e.NewValue);
            _vbB.RaiseEvent(new RoutedEventArgs(CurrentColorChangedEvent,
        (Colore.NewValue));
        }
        private static void OnIsLastColorChanged(DependencyObject o,
        DependencyPropertyChangedEventArgs e)
        {
            _vbB.RaiseEvent(new RoutedEventArgs(LastColorChangedEvent,
        (Color)e.NewValue));
        } 

Here is some important code of MouseCliping.cs:

That's the basic API method signature to call ClipCursor:

C#
#region API - Methodes
        [DllImport("user32.dll")]
        static extern bool ClipCursor(ref RECT lpRect);
#endregion 

This is the method to calculate a rect from a UIElement's position. myrect is an instance of a struct called RECT. This one has some properties -> Left, Top, Right, Bottom. The struct RECT converts the positions to a System.Drawing.Point.
To view the struct, please download the project source.

C#
/// <summary>
/// Clips the cursor to an UIElement.
/// </summary>
/// <param name=""element""></param>
public static void OnUIElement(UIElement element)
{
    uIEelement = element;
    myrect.Left = (int)(element.PointFromScreen(new Point(0, 0)).X * -1);
    myrect.Top = (int)(element.PointFromScreen(new Point(0, 0)).Y * -1);
    myrect.Right = (int)((element.PointFromScreen(new Point(0, 0)).X * -1) +
    element.RenderSize.Width);
    myrect.Bottom = (int)((element.PointFromScreen(new Point(0, 0)).Y * -1) +
    element.RenderSize.Height);
    ClipCursor(ref myrect);
}

Here is some important code of MouseControling.cs:

  • SetCursorPos - Sets the cursor to a point on the screen
  • GetCursorPos - Gets the cursor position like the WPF method
    (UIElement.PointToScreen (Point point))
  • GetPixel - Gets the color of a pixel on the screen
  • GetWindowDC - Gets the current handle of the window
  • ReleasDC - Release the current handle of the window
C#
[System.Runtime.InteropServices.DllImportAttribute
    ("user32.dll", EntryPoint = "SetCursorPos")]
        [return: System.Runtime.InteropServices.MarshalAsAttribute
    (System.Runtime.InteropServices.UnmanagedType.Bool)]
        private static extern bool SetCursorPos(int X, int Y);
        [DllImport("user32.dll")]
        private static extern int GetCursorPos(ref POINT lpPoint);
        [DllImport("gdi32.dll")]
        private static extern int GetPixel(int hdc, int nXPos, int nYPos);
        [DllImport("user32.dll")]
        private static extern int GetWindowDC(int hWnd);
        [DllImport("user32.dll")]
        private static extern int ReleaseDC(int hWnd, int hDC);

This is a very nice and useful method to get the pixel color under the mouse.

I create an instance point of the struct POINT. The API-method GetCursorPos takes a reference to the Struct. Now the point contains the current mouse position (this is an absolute position from the left top). Next I create a System.Media.Point, and give it the x and y from the struct. Why so complicated? Because The API-method can't handle a System.Media.Point. lDC takes the handle of the Current window. Now we have all information to call GetPixel(). This API-method takes the current handle lDC and the x and y coordinates. Don't forget to call ReleaseDC()! The last step is to extract the bytes. I used an array with the length of 4 to store all ARGB bytes. I found the information where the bytes are stored on www.pinvoke.net.

C#
private struct POINT
       {
           public uint x;
           public uint y;
       }

  /// <summary>
  /// Returns the color of the pixel under the mouse.
   /// </summary>
   /// <returns></returns>
   public static Color PixelUnderMouse()
   {
          POINT point = new POINT();
          GetCursorPos(ref point);
          Point pos = new Point();
          pos.X = point.x;
          pos.Y = point.y;
          int lDC = GetWindowDC(0);
          int intColor = GetPixel(lDC, (int)pos.X, (int)pos.Y);
          ReleaseDC(0, lDC);
          byte[] argb = new byte[4];
          //A
          //argb[0] = (byte)((intColor >> 0x18) & 0xffL);
          //B
           argb[1] = (byte)(intColor & 0xffL);
           //G
          argb[2] = (byte)((intColor >> 8) & 0xffL);
          //B
          argb[3] = (byte)((intColor >> 0x10) & 0xffL);

          return Color.FromRgb(argb[1], argb[2], argb[3]);
    }

Points of Interest

As I started this project, I had no idea of API calls. After spending a few hours on Google, I found a very useful site called www.pinvoke.net. This site contains a large library of API methods and a "how to use" them. The Visual Studio plugin is also very nice and allows you to search for API signatures from Visual Studio.

History

  • 4th February, 2009: Initial post

License

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


Written By
Network Administrator Arthrex GmbH
Germany Germany
Currently im trainee, as an Network Administrator.
Im programming since 2 years. I have good scills in c#,
java, html, asp, and basic knowledge in vb.

Comments and Discussions

 
QuestionMultible Colorpickers Pin
PHP_Error25-Jan-13 3:00
PHP_Error25-Jan-13 3:00 
AnswerRe: Multible Colorpickers Pin
PHP_Error25-Jan-13 4:14
PHP_Error25-Jan-13 4:14 
GeneralZip files broken Pin
TylerBrinks5-Feb-09 10:00
TylerBrinks5-Feb-09 10:00 
GeneralNice but... Pin
Sacha Barber5-Feb-09 3:04
Sacha Barber5-Feb-09 3:04 
Generalzip file problem Pin
schiebel-t5-Feb-09 2:51
schiebel-t5-Feb-09 2:51 
GeneralProblems with the zip files Pin
Ashley Davis5-Feb-09 2:41
Ashley Davis5-Feb-09 2:41 
GeneralOpacity Pin
schiebel-t4-Feb-09 9:23
schiebel-t4-Feb-09 9:23 
GeneralYou may add opacity as well Pin
Hardy Wang4-Feb-09 7:31
Hardy Wang4-Feb-09 7:31 
GeneralRe: You may add opacity as well Pin
Sacha Barber5-Feb-09 3:07
Sacha Barber5-Feb-09 3:07 

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.