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.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
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

  /// <summary>
  /// Represents a control that allows the editing of a color in a variety of ways.
  /// </summary>
  [DefaultProperty("Color")]
  [DefaultEvent("ColorChanged")]
  public partial class ColorEditor : UserControl, IColorEditor
  {
    #region Instance Fields

    private HslColor _hslColor;

    private Orientation _orientation;

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="ColorEditor"/> class.
    /// </summary>
    public ColorEditor()
    {
      this.InitializeComponent();

      this.Color = Color.Black;
      this.Orientation = Orientation.Vertical;
      this.Size = new Size(200, 260);

      foreach (PropertyInfo property in typeof(Color).GetProperties(BindingFlags.Public | BindingFlags.Static).Where(property => property.PropertyType == typeof(Color)))
      {
        Color color;

        color = (Color)property.GetValue(typeof(Color), null);
        if (!color.IsEmpty)
          hexTextBox.Items.Add(color.Name);
      }

      this.SetDropDownWidth();
    }

    #endregion

    #region Events

    /// <summary>
    /// Occurs when the Color property value changes
    /// </summary>
    [Category("Property Changed")]
    public event EventHandler ColorChanged;

    /// <summary>
    /// Occurs when the Orientation property value changes
    /// </summary>
    [Category("Property Changed")]
    public event EventHandler OrientationChanged;

    #endregion

    #region Overridden Members

    /// <summary>
    /// Raises the <see cref="E:System.Windows.Forms.Control.DockChanged" /> event.
    /// </summary>
    /// <param name="e">An <see cref="T:System.EventArgs" /> that contains the event data.</param>
    protected override void OnDockChanged(EventArgs e)
    {
      base.OnDockChanged(e);

      this.ResizeComponents();
    }

    protected override void OnFontChanged(EventArgs e)
    {
      base.OnFontChanged(e);

      this.SetDropDownWidth();
    }

    /// <summary>
    /// Raises the <see cref="E:System.Windows.Forms.UserControl.Load" /> event.
    /// </summary>
    /// <param name="e">An <see cref="T:System.EventArgs" /> that contains the event data.</param>
    protected override void OnLoad(EventArgs e)
    {
      base.OnLoad(e);

      this.ResizeComponents();
    }

    /// <summary>
    /// Raises the <see cref="E:System.Windows.Forms.Control.PaddingChanged" /> event.
    /// </summary>
    /// <param name="e">An <see cref="T:System.EventArgs" /> that contains the event data.</param>
    protected override void OnPaddingChanged(EventArgs e)
    {
      base.OnPaddingChanged(e);

      this.ResizeComponents();
    }

    /// <summary>
    /// Raises the <see cref="E:Resize" /> event.
    /// </summary>
    /// <param name="e">An <see cref="T:System.EventArgs" /> that contains the event data.</param>
    protected override void OnResize(EventArgs e)
    {
      base.OnResize(e);

      this.ResizeComponents();
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets or sets the component color.
    /// </summary>
    /// <value>The component color.</value>
    [Category("Appearance")]
    [DefaultValue(typeof(Color), "0, 0, 0")]
    public virtual Color Color
    {
      get { return this.HslColor.ToRgbColor(); }
      set { this.HslColor = new HslColor(value); }
    }

    /// <summary>
    /// Gets or sets the component color as a HSL structure.
    /// </summary>
    /// <value>The component color.</value>
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public virtual HslColor HslColor
    {
      get { return _hslColor; }
      set
      {
        if (this.HslColor != value)
        {
          _hslColor = value;

          this.OnColorChanged(EventArgs.Empty);
        }
      }
    }

    /// <summary>
    /// Gets or sets the orientation of the editor.
    /// </summary>
    /// <value>The orientation.</value>
    [Category("Appearance")]
    [DefaultValue(typeof(Orientation), "Vertical")]
    public virtual Orientation Orientation
    {
      get { return _orientation; }
      set
      {
        if (this.Orientation != value)
        {
          _orientation = value;

          this.OnOrientationChanged(EventArgs.Empty);
        }
      }
    }

    /// <summary>
    /// Gets or sets a value indicating whether input changes should be processed.
    /// </summary>
    /// <value><c>true</c> if input changes should be processed; otherwise, <c>false</c>.</value>
    protected bool LockUpdates { get; set; }

    #endregion

    #region Members

    /// <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;

      this.UpdateFields(false);

      handler = this.ColorChanged;

      if (handler != null)
        handler(this, e);
    }

    /// <summary>
    /// Raises the <see cref="OrientationChanged" /> event.
    /// </summary>
    /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
    protected virtual void OnOrientationChanged(EventArgs e)
    {
      EventHandler handler;

      this.ResizeComponents();

      handler = this.OrientationChanged;

      if (handler != null)
        handler(this, e);
    }

    /// <summary>
    /// Resizes the editing components.
    /// </summary>
    protected virtual void ResizeComponents()
    {
      try
      {
        int group1HeaderLeft;
        int group1BarLeft;
        int group1EditLeft;
        int group2HeaderLeft;
        int group2BarLeft;
        int group2EditLeft;
        int barWidth;
        int editWidth;
        int top;
        int innerMargin;
        int columnWidth;
        int rowHeight;
        int labelOffset;
        int colorBarOffset;
        int editOffset;

        top = this.Padding.Top;
        innerMargin = 3;
        editWidth = TextRenderer.MeasureText(new string('W', 6), this.Font).Width;
        rowHeight = Math.Max(Math.Max(rLabel.Height, rColorBar.Height), rNumericUpDown.Height);
        labelOffset = (rowHeight - rLabel.Height) / 2;
        colorBarOffset = (rowHeight - rColorBar.Height) / 2;
        editOffset = (rowHeight - rNumericUpDown.Height) / 2;

        switch (this.Orientation)
        {
          case Orientation.Horizontal:
            columnWidth = (this.ClientSize.Width - (this.Padding.Horizontal + innerMargin)) / 2;
            break;
          default:
            columnWidth = this.ClientSize.Width - this.Padding.Horizontal;
            break;
        }

        group1HeaderLeft = this.Padding.Left;
        group1EditLeft = columnWidth - editWidth;
        group1BarLeft = group1HeaderLeft + aLabel.Width + innerMargin;

        if (this.Orientation == Orientation.Horizontal)
        {
          group2HeaderLeft = this.Padding.Left + columnWidth + innerMargin;
          group2EditLeft = this.ClientSize.Width - (this.Padding.Right + editWidth);
          group2BarLeft = group2HeaderLeft + aLabel.Width + innerMargin;
        }
        else
        {
          group2HeaderLeft = group1HeaderLeft;
          group2EditLeft = group1EditLeft;
          group2BarLeft = group1BarLeft;
        }

        barWidth = group1EditLeft - (group1BarLeft + innerMargin);

        // RGB header
        rgbHeaderLabel.SetBounds(group1HeaderLeft, top + labelOffset, 0, 0, BoundsSpecified.Location);
        top += rowHeight + innerMargin;

        // R row
        rLabel.SetBounds(group1HeaderLeft, top + labelOffset, 0, 0, BoundsSpecified.Location);
        rColorBar.SetBounds(group1BarLeft, top + colorBarOffset, barWidth, 0, BoundsSpecified.Location | BoundsSpecified.Width);
        rNumericUpDown.SetBounds(group1EditLeft + editOffset, top, editWidth, 0, BoundsSpecified.Location | BoundsSpecified.Width);
        top += rowHeight + innerMargin;

        // G row
        gLabel.SetBounds(group1HeaderLeft, top + labelOffset, 0, 0, BoundsSpecified.Location);
        gColorBar.SetBounds(group1BarLeft, top + colorBarOffset, barWidth, 0, BoundsSpecified.Location | BoundsSpecified.Width);
        gNumericUpDown.SetBounds(group1EditLeft + editOffset, top, editWidth, 0, BoundsSpecified.Location | BoundsSpecified.Width);
        top += rowHeight + innerMargin;

        // B row
        bLabel.SetBounds(group1HeaderLeft, top + labelOffset, 0, 0, BoundsSpecified.Location);
        bColorBar.SetBounds(group1BarLeft, top + colorBarOffset, barWidth, 0, BoundsSpecified.Location | BoundsSpecified.Width);
        bNumericUpDown.SetBounds(group1EditLeft + editOffset, top, editWidth, 0, BoundsSpecified.Location | BoundsSpecified.Width);
        top += rowHeight + innerMargin;

        // Hex row
        hexLabel.SetBounds(group1HeaderLeft, top + labelOffset, 0, 0, BoundsSpecified.Location);
        hexTextBox.SetBounds(group1EditLeft, top + colorBarOffset, editWidth, 0, BoundsSpecified.Location | BoundsSpecified.Width);
        top += rowHeight + innerMargin;

        // reset top
        if (this.Orientation == Orientation.Horizontal)
          top = this.Padding.Top;

        // HSL header
        hslLabel.SetBounds(group2HeaderLeft, top + labelOffset, 0, 0, BoundsSpecified.Location);
        top += rowHeight + innerMargin;

        // H row
        hLabel.SetBounds(group2HeaderLeft, top + labelOffset, 0, 0, BoundsSpecified.Location);
        hColorBar.SetBounds(group2BarLeft, top + colorBarOffset, barWidth, 0, BoundsSpecified.Location | BoundsSpecified.Width);
        hNumericUpDown.SetBounds(group2EditLeft, top + editOffset, editWidth, 0, BoundsSpecified.Location | BoundsSpecified.Width);
        top += rowHeight + innerMargin;

        // S row
        sLabel.SetBounds(group2HeaderLeft, top + labelOffset, 0, 0, BoundsSpecified.Location);
        sColorBar.SetBounds(group2BarLeft, top + colorBarOffset, barWidth, 0, BoundsSpecified.Location | BoundsSpecified.Width);
        sNumericUpDown.SetBounds(group2EditLeft, top + editOffset, editWidth, 0, BoundsSpecified.Location | BoundsSpecified.Width);
        top += rowHeight + innerMargin;

        // L row
        lLabel.SetBounds(group2HeaderLeft, top + labelOffset, 0, 0, BoundsSpecified.Location);
        lColorBar.SetBounds(group2BarLeft, top + colorBarOffset, barWidth, 0, BoundsSpecified.Location | BoundsSpecified.Width);
        lNumericUpDown.SetBounds(group2EditLeft, top + editOffset, editWidth, 0, BoundsSpecified.Location | BoundsSpecified.Width);
        top += rowHeight + innerMargin;

        // A row
        aLabel.SetBounds(group2HeaderLeft, top + labelOffset, 0, 0, BoundsSpecified.Location);
        aColorBar.SetBounds(group2BarLeft, top + colorBarOffset, barWidth, 0, BoundsSpecified.Location | BoundsSpecified.Width);
        aNumericUpDown.SetBounds(group2EditLeft, top + editOffset, editWidth, 0, BoundsSpecified.Location | BoundsSpecified.Width);
      }
        // ReSharper disable EmptyGeneralCatchClause
      catch
        // ReSharper restore EmptyGeneralCatchClause
      {
        // ignore errors
      }
    }

    /// <summary>
    /// Updates the editing field values.
    /// </summary>
    /// <param name="userAction">if set to <c>true</c> the update is due to user interaction.</param>
    protected virtual void UpdateFields(bool userAction)
    {
      if (!this.LockUpdates)
      {
        try
        {
          this.LockUpdates = true;

          // RGB
          if (!(userAction && rNumericUpDown.Focused))
            rNumericUpDown.Value = this.Color.R;
          if (!(userAction && gNumericUpDown.Focused))
            gNumericUpDown.Value = this.Color.G;
          if (!(userAction && bNumericUpDown.Focused))
            bNumericUpDown.Value = this.Color.B;
          rColorBar.Value = this.Color.R;
          rColorBar.Color = this.Color;
          gColorBar.Value = this.Color.G;
          gColorBar.Color = this.Color;
          bColorBar.Value = this.Color.B;
          bColorBar.Color = this.Color;

          // HTML
          if (!(userAction && hexTextBox.Focused))
            hexTextBox.Text = string.Format("{0:X2}{1:X2}{2:X2}", this.Color.R, this.Color.G, this.Color.B);

          // HSL
          if (!(userAction && hNumericUpDown.Focused))
            hNumericUpDown.Value = (int)this.HslColor.H;
          if (!(userAction && sNumericUpDown.Focused))
            sNumericUpDown.Value = (int)(this.HslColor.S * 100);
          if (!(userAction && lNumericUpDown.Focused))
            lNumericUpDown.Value = (int)(this.HslColor.L * 100);
          hColorBar.Value = (int)this.HslColor.H;
          sColorBar.Color = this.Color;
          sColorBar.Value = (int)(this.HslColor.S * 100);
          lColorBar.Color = this.Color;
          lColorBar.Value = (int)(this.HslColor.L * 100);

          // Alpha
          if (!(userAction && aNumericUpDown.Focused))
            aNumericUpDown.Value = this.Color.A;
          aColorBar.Color = this.Color;
          aColorBar.Value = this.Color.A;
        }
        finally
        {
          this.LockUpdates = false;
        }
      }
    }

    private string AddSpaces(string text)
    {
      string result;

      //http://stackoverflow.com/a/272929/148962

      if (!string.IsNullOrEmpty(text))
      {
        StringBuilder newText;

        newText = new StringBuilder(text.Length * 2);
        newText.Append(text[0]);
        for (int i = 1; i < text.Length; i++)
        {
          if (char.IsUpper(text[i]) && text[i - 1] != ' ')
            newText.Append(' ');
          newText.Append(text[i]);
        }

        result = newText.ToString();
      }
      else
        result = null;

      return result;
    }

    private void SetDropDownWidth()
    {
      if (hexTextBox.Items.Count != 0)
        hexTextBox.DropDownWidth = (hexTextBox.ItemHeight * 2) + hexTextBox.Items.Cast<string>().Max(s => TextRenderer.MeasureText(s, this.Font).Width);
    }

    #endregion

    #region Event Handlers

    /// <summary>
    /// Change handler for editing components.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
    private void ValueChangedHandler(object sender, EventArgs e)
    {
      if (!this.LockUpdates)
      {
        bool useHsl;
        bool useRgb;

        useHsl = false;
        useRgb = false;

        this.LockUpdates = true;

        if (sender == hexTextBox)
        {
          string text;
          int namedIndex;

          text = hexTextBox.Text;
          if (text.StartsWith("#"))
            text = text.Substring(1);

          namedIndex = hexTextBox.FindStringExact(text);

          if (namedIndex != -1 || text.Length == 6 || text.Length == 8)
          {
            try
            {
              Color color;

              color = namedIndex != -1 ? Color.FromName(text) : ColorTranslator.FromHtml("#" + text);
              aNumericUpDown.Value = color.A;
              rNumericUpDown.Value = color.R;
              bNumericUpDown.Value = color.B;
              gNumericUpDown.Value = color.G;

              useRgb = true;
            }
              // ReSharper disable EmptyGeneralCatchClause
            catch
            { }
            // ReSharper restore EmptyGeneralCatchClause
          }
        }
        else if (sender == aColorBar || sender == rColorBar || sender == gColorBar || sender == bColorBar)
        {
          aNumericUpDown.Value = (int)aColorBar.Value;
          rNumericUpDown.Value = (int)rColorBar.Value;
          gNumericUpDown.Value = (int)gColorBar.Value;
          bNumericUpDown.Value = (int)bColorBar.Value;

          useRgb = true;
        }
        else if (sender == aNumericUpDown || sender == rNumericUpDown || sender == gNumericUpDown || sender == bNumericUpDown)
          useRgb = true;
        else if (sender == hColorBar || sender == lColorBar || sender == sColorBar)
        {
          hNumericUpDown.Value = (int)hColorBar.Value;
          sNumericUpDown.Value = (int)sColorBar.Value;
          lNumericUpDown.Value = (int)lColorBar.Value;

          useHsl = true;
        }
        else if (sender == hNumericUpDown || sender == sNumericUpDown || sender == lNumericUpDown)
          useHsl = true;

        if (useRgb)
        {
          Color color;

          color = Color.FromArgb((int)aNumericUpDown.Value, (int)rNumericUpDown.Value, (int)gNumericUpDown.Value, (int)bNumericUpDown.Value);
          this.HslColor = new HslColor(color);
        }
        else if (useHsl)
        {
          HslColor color;

          color = new HslColor((int)aNumericUpDown.Value, (double)hNumericUpDown.Value, (double)sNumericUpDown.Value / 100, (double)lNumericUpDown.Value / 100);
          this.HslColor = color;
        }

        this.LockUpdates = false;
        this.UpdateFields(true);
      }
    }

    private void hexTextBox_DrawItem(object sender, DrawItemEventArgs e)
    {
      // TODO: Really, this should be another control - ColorComboBox or ColorListBox etc.

      if (e.Index != -1)
      {
        Rectangle colorBox;
        string name;

        e.DrawBackground();

        name = (string)hexTextBox.Items[e.Index];
        colorBox = new Rectangle(e.Bounds.Left + 1, e.Bounds.Top + 1, e.Bounds.Height - 3, e.Bounds.Height - 3);

        using (Brush brush = new SolidBrush(Color.FromName(name)))
          e.Graphics.FillRectangle(brush, colorBox);
        e.Graphics.DrawRectangle(SystemPens.ControlText, colorBox);

        using (Brush brush = new SolidBrush(e.ForeColor))
          e.Graphics.DrawString(this.AddSpaces(name), this.Font, brush, colorBox.Right + 3, colorBox.Top);

        e.DrawFocusRectangle();
      }
    }

    private void hexTextBox_SelectedIndexChanged(object sender, EventArgs e)
    {
      if (hexTextBox.SelectedIndex != -1)
        this.Color = Color.FromName((string)hexTextBox.SelectedItem);
    }

    #endregion
  }
}

Donate

Donate