Archive Browser
Download Cyotek.Windows.Forms.ColorPicker-1.0.2.0.zip version 1.0.2.0, last updated 13/07/2013 (382.27 KB)
Download- md5: 34597ba8a4143a1794524236d9f43524
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Windows.Forms;
#if USEEXTERNALCYOTEKLIBS
using Cyotek.Drawing;
#endif
namespace Cyotek.Windows.Forms
{
// Cyotek Color Picker controls library
// Copyright © 2013 Cyotek. All Rights Reserved.
// http://cyotek.com/blog/tag/colorpicker
// If you use this code in your applications, donations or attribution are welcome
[DefaultProperty("Color")]
[DefaultEvent("ColorChanged")]
public class ColorWheel : Control, IColorEditor
{
#region Instance Fields
private Brush _brush;
private PointF _centerPoint;
private Color _color;
private int _colorStep;
private HslColor _hslColor;
private int _largeChange;
private int _selectionSize;
private int _smallChange;
private int _updateCount;
#endregion
#region Constructors
public ColorWheel()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.Selectable | ControlStyles.StandardClick | ControlStyles.StandardDoubleClick, true);
this.UpdateStyles();
this.Color = Color.Black;
this.ColorStep = 4;
this.SelectionSize = 10;
this.SmallChange = 1;
this.LargeChange = 5;
this.SelectionGlyph = this.CreateSelectionGlyph();
}
#endregion
#region Events
/// <summary>
/// Occurs when the Color property value changes
/// </summary>
[Category("Property Changed")]
public event EventHandler ColorChanged;
/// <summary>
/// Occurs when the ColorStep property value changes
/// </summary>
[Category("Property Changed")]
public event EventHandler ColorStepChanged;
/// <summary>
/// Occurs when the HslColor property value changes
/// </summary>
[Category("Property Changed")]
public event EventHandler HslColorChanged;
/// <summary>
/// Occurs when the LargeChange property value changes
/// </summary>
[Category("Property Changed")]
public event EventHandler LargeChangeChanged;
/// <summary>
/// Occurs when the SelectionSize property value changes
/// </summary>
[Category("Property Changed")]
public event EventHandler SelectionSizeChanged;
/// <summary>
/// Occurs when the SmallChange property value changes
/// </summary>
[Category("Property Changed")]
public event EventHandler SmallChangeChanged;
#endregion
#region Overridden Properties
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override Font Font
{
get { return base.Font; }
set { base.Font = value; }
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override Color ForeColor
{
get { return base.ForeColor; }
set { base.ForeColor = value; }
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override string Text
{
get { return base.Text; }
set { base.Text = value; }
}
#endregion
#region Overridden Members
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (_brush != null)
_brush.Dispose();
if (this.SelectionGlyph != null)
this.SelectionGlyph.Dispose();
}
base.Dispose(disposing);
}
protected override bool IsInputKey(Keys keyData)
{
bool result;
if ((keyData & Keys.Left) == Keys.Left || (keyData & Keys.Up) == Keys.Up || (keyData & Keys.Down) == Keys.Down || (keyData & Keys.Right) == Keys.Right || (keyData & Keys.PageUp) == Keys.PageUp || (keyData & Keys.PageDown) == Keys.PageDown)
result = true;
else
result = base.IsInputKey(keyData);
return result;
}
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
this.Invalidate();
}
protected override void OnKeyDown(KeyEventArgs e)
{
HslColor color;
double hue;
int step;
color = this.HslColor;
hue = color.H;
step = e.Shift ? this.LargeChange : this.SmallChange;
switch (e.KeyCode)
{
case Keys.Right:
case Keys.Up:
hue += step;
break;
case Keys.Left:
case Keys.Down:
hue -= step;
break;
case Keys.PageUp:
hue += this.LargeChange;
break;
case Keys.PageDown:
hue -= this.LargeChange;
break;
}
if (hue >= 360)
hue = 0;
if (hue < 0)
hue = 359;
if (hue != color.H)
{
color.H = hue;
// As the Color and HslColor properties update each other, need to temporarily disable this and manually set both
// otherwise the wheel "sticks" due to imprecise conversion from RGB to HSL
this.LockUpdates = true;
this.Color = color.ToRgbColor();
this.HslColor = color;
this.LockUpdates = false;
e.Handled = true;
}
base.OnKeyDown(e);
}
protected override void OnLostFocus(EventArgs e)
{
base.OnLostFocus(e);
this.Invalidate();
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (!this.Focused && this.TabStop)
this.Focus();
if (e.Button == MouseButtons.Left)
this.SetColor(e.Location);
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.Button == MouseButtons.Left)
this.SetColor(e.Location);
}
protected override void OnPaddingChanged(EventArgs e)
{
base.OnPaddingChanged(e);
this.RefreshWheel();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (this.AllowPainting)
{
base.OnPaintBackground(e); // HACK: Easiest way of supporting things like BackgroundImage, BackgroundImageLayout etc
if (_brush != null)
e.Graphics.FillPie(_brush, this.ClientRectangle, 0, 360);
this.PaintCurrentColor(e);
}
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
this.RefreshWheel();
}
#endregion
#region Properties
[Category("Appearance")]
[DefaultValue(typeof(Color), "Black")]
public virtual Color Color
{
get { return _color; }
set
{
if (this.Color != value)
{
_color = value;
this.OnColorChanged(EventArgs.Empty);
}
}
}
[Category("Appearance")]
[DefaultValue(4)]
public virtual int ColorStep
{
get { return _colorStep; }
set
{
if (value < 1 || value > 359)
throw new IndexOutOfRangeException("Value must be between 1 and 359");
if (this.ColorStep != value)
{
_colorStep = value;
this.OnColorStepChanged(EventArgs.Empty);
}
}
}
[Category("Appearance")]
[DefaultValue(typeof(HslColor), "0, 0, 0")]
[Browsable(false) /* disable editing until I write a proper type convertor */]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public virtual HslColor HslColor
{
get { return _hslColor; }
set
{
if (this.HslColor != value)
{
_hslColor = value;
this.OnHslColorChanged(EventArgs.Empty);
}
}
}
[Category("Behavior")]
[DefaultValue(5)]
public virtual int LargeChange
{
get { return _largeChange; }
set
{
if (this.LargeChange != value)
{
_largeChange = value;
this.OnLargeChangeChanged(EventArgs.Empty);
}
}
}
[Category("Appearance")]
[DefaultValue(10)]
public virtual int SelectionSize
{
get { return _selectionSize; }
set
{
if (this.SelectionSize != value)
{
_selectionSize = value;
this.OnSelectionSizeChanged(EventArgs.Empty);
}
}
}
[Category("Behavior")]
[DefaultValue(1)]
public virtual int SmallChange
{
get { return _smallChange; }
set
{
if (this.SmallChange != value)
{
_smallChange = value;
this.OnSmallChangeChanged(EventArgs.Empty);
}
}
}
/// <summary>
/// Gets a value indicating whether painting of the control is allowed.
/// </summary>
/// <value>
/// <c>true</c> if painting of the control is allowed; otherwise, <c>false</c>.
/// </value>
protected virtual bool AllowPainting
{
get { return _updateCount == 0; }
}
protected Color[] Colors { get; set; }
protected bool LockUpdates { get; set; }
protected PointF[] Points { get; set; }
protected Image SelectionGlyph { get; set; }
#endregion
#region Members
/// <summary>
/// Disables any redrawing of the image box
/// </summary>
public virtual void BeginUpdate()
{
_updateCount++;
}
/// <summary>
/// Enables the redrawing of the image box
/// </summary>
public virtual void EndUpdate()
{
if (_updateCount > 0)
_updateCount--;
if (this.AllowPainting)
this.Invalidate();
}
protected virtual void CalculateWheel()
{
List<PointF> points;
List<Color> colors;
points = new List<PointF>();
colors = new List<Color>();
// Only define the points if the control is above a minimum size, otherwise if it's too small, you get an "out of memory" exceptions (of all things) when creating the brush
if (this.ClientSize.Width > 16 && this.ClientSize.Height > 16)
{
double radius;
int w;
int h;
w = this.ClientSize.Width;
h = this.ClientSize.Height;
_centerPoint = new PointF(w / 2.0F, h / 2.0F);
radius = this.GetRadius(_centerPoint);
for (double angle = 0; angle < 360; angle += this.ColorStep)
{
double angleR;
PointF location;
angleR = angle * (Math.PI / 180);
location = this.GetColorLocation(angleR, radius);
points.Add(location);
colors.Add(new HslColor(angle, 1, 0.5).ToRgbColor());
}
}
this.Points = points.ToArray();
this.Colors = colors.ToArray();
}
protected virtual Brush CreateGradientBrush()
{
Brush result;
if (this.Points.Length != 0 && this.Points.Length == this.Colors.Length)
{
result = new PathGradientBrush(this.Points, WrapMode.Clamp)
{
CenterPoint = _centerPoint,
CenterColor = Color.White,
SurroundColors = this.Colors
};
}
else
result = null;
return result;
}
protected virtual Image CreateSelectionGlyph()
{
Image image;
int halfSize;
halfSize = this.SelectionSize / 2;
image = new Bitmap(this.SelectionSize + 1, this.SelectionSize + 1, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(image))
{
Point[] diamondOuter;
diamondOuter = new[]
{
new Point(halfSize, 0), new Point(this.SelectionSize, halfSize), new Point(halfSize, this.SelectionSize), new Point(0, halfSize)
};
g.FillPolygon(SystemBrushes.Control, diamondOuter);
g.DrawPolygon(SystemPens.ControlDark, diamondOuter);
using (Pen pen = new Pen(Color.FromArgb(128, SystemColors.ControlDark)))
{
g.DrawLine(pen, halfSize, 1, this.SelectionSize - 1, halfSize);
g.DrawLine(pen, halfSize, 2, this.SelectionSize - 2, halfSize);
g.DrawLine(pen, halfSize, this.SelectionSize - 1, this.SelectionSize - 2, halfSize + 1);
g.DrawLine(pen, halfSize, this.SelectionSize - 2, this.SelectionSize - 3, halfSize + 1);
}
using (Pen pen = new Pen(Color.FromArgb(196, SystemColors.ControlLightLight)))
g.DrawLine(pen, halfSize, this.SelectionSize - 1, 1, halfSize);
g.DrawLine(SystemPens.ControlLightLight, 1, halfSize, halfSize, 1);
}
return image;
}
protected PointF GetColorLocation(Color color)
{
return this.GetColorLocation(new HslColor(color));
}
protected virtual PointF GetColorLocation(HslColor color)
{
double angleR = color.H * Math.PI / 180;
double radius = this.GetRadius(_centerPoint);
radius *= color.S;
return this.GetColorLocation(angleR, radius);
}
protected PointF GetColorLocation(double angleR, double radius)
{
double x;
double y;
x = this.Padding.Left + _centerPoint.X + Math.Cos(angleR) * radius;
y = this.Padding.Top + _centerPoint.Y - Math.Sin(angleR) * radius;
return new PointF((float)x, (float)y);
}
protected float GetRadius(PointF centerPoint)
{
return Math.Min(centerPoint.X, centerPoint.Y) - (Math.Max(this.Padding.Horizontal, this.Padding.Vertical) + (this.SelectionSize / 2));
}
/// <summary>
/// Raises the <see cref="ColorChanged" /> event.
/// </summary>
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
protected virtual void OnColorChanged(EventArgs e)
{
EventHandler handler;
if (!this.LockUpdates)
this.HslColor = new HslColor(this.Color);
this.Refresh();
handler = this.ColorChanged;
if (handler != null)
handler(this, e);
}
/// <summary>
/// Raises the <see cref="ColorStepChanged" /> event.
/// </summary>
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
protected virtual void OnColorStepChanged(EventArgs e)
{
EventHandler handler;
this.RefreshWheel();
handler = this.ColorStepChanged;
if (handler != null)
handler(this, e);
}
/// <summary>
/// Raises the <see cref="HslColorChanged" /> event.
/// </summary>
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
protected virtual void OnHslColorChanged(EventArgs e)
{
EventHandler handler;
if (!this.LockUpdates)
this.Color = this.HslColor.ToRgbColor();
this.Invalidate();
handler = this.HslColorChanged;
if (handler != null)
handler(this, e);
}
/// <summary>
/// Raises the <see cref="LargeChangeChanged" /> event.
/// </summary>
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
protected virtual void OnLargeChangeChanged(EventArgs e)
{
EventHandler handler;
handler = this.LargeChangeChanged;
if (handler != null)
handler(this, e);
}
/// <summary>
/// Raises the <see cref="SelectionSizeChanged" /> event.
/// </summary>
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
protected virtual void OnSelectionSizeChanged(EventArgs e)
{
EventHandler handler;
if (this.SelectionGlyph != null)
this.SelectionGlyph.Dispose();
this.SelectionGlyph = this.CreateSelectionGlyph();
this.Invalidate();
handler = this.SelectionSizeChanged;
if (handler != null)
handler(this, e);
}
/// <summary>
/// Raises the <see cref="SmallChangeChanged" /> event.
/// </summary>
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
protected virtual void OnSmallChangeChanged(EventArgs e)
{
EventHandler handler;
handler = this.SmallChangeChanged;
if (handler != null)
handler(this, e);
}
protected virtual void PaintCurrentColor(PaintEventArgs e)
{
if (!this.Color.IsEmpty)
{
PointF location;
location = this.GetColorLocation(this.HslColor);
if (!float.IsNaN(location.X) && !float.IsNaN(location.Y))
{
int x;
int y;
x = (int)location.X - (this.SelectionSize / 2);
y = (int)location.Y - (this.SelectionSize / 2);
if (this.SelectionGlyph == null)
e.Graphics.DrawRectangle(Pens.Black, x, y, this.SelectionSize, this.SelectionSize);
else
e.Graphics.DrawImage(this.SelectionGlyph, x, y);
if (this.Focused)
ControlPaint.DrawFocusRectangle(e.Graphics, new Rectangle(x - 1, y - 1, this.SelectionSize + 2, this.SelectionSize + 2));
}
}
}
protected virtual void SetColor(Point point)
{
double radius;
double dx;
double dy;
double angle;
double distance;
double saturation;
radius = this.GetRadius(_centerPoint);
dx = Math.Abs(point.X - _centerPoint.X - this.Padding.Left);
dy = Math.Abs(point.Y - _centerPoint.Y - this.Padding.Top);
angle = Math.Atan(dy / dx) / Math.PI * 180;
distance = Math.Pow((Math.Pow(dx, 2) + (Math.Pow(dy, 2))), 0.5);
saturation = distance / radius;
if (distance < 6)
saturation = 0; // snap to center
if (point.X < _centerPoint.X)
angle = 180 - angle;
if (point.Y > _centerPoint.Y)
angle = 360 - angle;
this.LockUpdates = true;
this.HslColor = new HslColor(angle, saturation, 0.5);
this.Color = this.HslColor.ToRgbColor();
this.LockUpdates = false;
}
private void RefreshWheel()
{
if (_brush != null)
_brush.Dispose();
this.CalculateWheel();
_brush = this.CreateGradientBrush();
this.Invalidate();
}
#endregion
}
}
Donate
This software may be used free of charge, but as with all free software there are costs involved to develop and maintain.
If this site or its services have saved you time, please consider a donation to help with running costs and timely updates.
Donate