Introduction
Welcome to the second installment of this short series on creating your own color picker in .NET. In Part 1, “Creating a Custom Color Picker in .NET, Part 1: The Color Wells,” we concentrated on creating the color wells. It was a lot of work… What we will do today is to bring it all together; luckily it is not so much work as the previous installment.
Let’s get started!
Currently (if you have followed Part 1), we are sitting with the Color Wells object that we’ve created. This object just hosts all the various colors we would like to be able to choose from. We need to put this object inside a parent object, such as a panel, or a group control, or, even better, another User Control. The reason why a User Control is more apt for this situation is because of versatility, and the ability to add our own Properties, Methods, and flavor to our project.
Add a new Class to your project. Name it anything useful, such as ctlColorPanel, because this will ultimately be the name of the object we will make use of in code and from anywhere on our Forms or other projects.
Add the necessary Namespaces to include all the libraries we will work with.
C#
using System.ComponentModel; using System.Windows.Forms;
VB.NET
Imports System.ComponentModel
Add a Delegate.
C#
internal delegate void ColorPanelClosingEventHandler(object sender, System.EventArgs e);
VB.NET
Friend Delegate Sub ColorPanelClosingEventHandler(ByVal sender _ As Object, ByVal e As EventArgs)
Add the events and Properties.
C#
internal class ctlColorPanel : WellPanel { private IContainer components = null; private int width = 300; public ctlColorPanel() { InitializeComponent(); } protected override void Dispose(bool disposing) { if (disposing) { if (components != null) { components.Dispose(); } } base.Dispose(disposing); } private void InitializeComponent() { Name = "ctlColorPanel"; } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if (Capture) { if (!ClientRectangle.Contains(e.X, e.Y)) { OnClosePanel(); } } } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); Capture = true; } protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (e.KeyCode == Keys.Escape) { OnClosePanel(); } } [Browsable(true)] internal event ColorPanelClosingEventHandler PanelClosing; protected virtual void OnClosePanel() { PanelClosing?.Invoke(this, new System.EventArgs()); } internal int ParentWidth { set { width = value; AutoSizePanel(); } } protected override int GetPreferredWidth() { return width; } protected override void OnGotFocus(System.EventArgs e) { base.OnGotFocus(e); Capture = true; } }
VB.NET
Friend Class ctlColorPanel Inherits WellPanel Private components As IContainer = Nothing Private wwidth As Integer = 300 Public Sub New() InitializeComponent() End Sub Protected Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If components IsNot Nothing Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub Private Sub InitializeComponent() Name = "ctlColorPanel" End Sub Protected Overrides Sub OnMouseDown(ByVal e As MouseEventArgs) MyBase.OnMouseDown(e) If Capture Then If Not ClientRectangle.Contains(e.X, e.Y) Then OnClosePanel() End If End If End Sub Protected Overrides Sub OnMouseUp(ByVal e As MouseEventArgs) MyBase.OnMouseUp(e) Capture = True End Sub Protected Overrides Sub OnKeyDown(ByVal e As KeyEventArgs) MyBase.OnKeyDown(e) If e.KeyCode = Keys.Escape Then OnClosePanel() End If End Sub <Browsable(True)> Friend Event PanelClosing As ColorPanelClosingEventHandler Protected Overridable Sub OnClosePanel() RaiseEvent PanelClosing(Me, New EventArgs()) End Sub Friend WriteOnly Property ParentWidth As Integer Set(ByVal value As Integer) wwidth = value AutoSizePanel() End Set End Property Protected Overrides Function GetPreferredWidth() As Integer Return wwidth End Function Protected Overrides Sub OnGotFocus(ByVal e As EventArgs) MyBase.OnGotFocus(e) Capture = True End Sub End Class
We Inherit from the WellPanel object we created in Part 1 and handle the events.
Add the Utilities Class.
C#
using System; public class Utilities { private Utilities() { } public static void CheckValidEnumValue(string arg, object val, System.Type Class) { if (!Enum.IsDefined(Class, val)) { throw new System.ComponentModel.InvalidEnumArgument Exception(arg, (int)val, Class); } } }
VB.NET
Public Class Utilities Public Shared Sub CheckValidEnumValue(ByVal arg As String, _ ByVal val As Object, ByVal [Class] As Type) If Not [Enum].IsDefined([Class], val) Then Throw New ComponentModel.InvalidEnumArgumentException _ (arg, CInt(val), [Class]) End If End Sub End Class
The Utilities class is responsible for determining whether the Enums are validly selected.
All should be fine, so Build the project. After the Build has succeeded, we need to add the Control to the Form. Ensure your Design of your Form resembles Figure 1.
Figure 1: Design
Add the following code to your Form.
C#
using System; using System.Drawing; using System.Windows.Forms; public partial class Form1 : Form { public Form1() { InitializeComponent(); cbEnable.Checked = ctPicker.Enabled; lblDisplay.BackColor = ctPicker.Color; lblDisplay.Text = ctPicker.Color.Name; } private void cbEnable_CheckedChanged(object sender, EventArgs e) { ctPicker.Enabled = cbEnable.Checked; } private void Form1_Load(object sender, EventArgs e) { duSort.Items.AddRange(Enum.GetValues(typeof(Order))); duSort.SelectedIndex = 0; duScheme.Items.AddRange(Enum.GetValues(typeof(Scheme))); duScheme.SelectedIndex = 0; SetColor(lblDisplay, ctPicker.Color); } private void duSort_SelectedItemChanged(object sender, EventArgs e) { ctPicker.SortOrder = (Order)duSort.SelectedItem; } private void duScheme_SelectedItemChanged(object sender, EventArgs e) { ctPicker.ColorScheme = (Scheme)duScheme.SelectedItem; } private void ctPicker_ColorChanged(object sender, ColorChangedEventArgs e) { SetColor(lblDisplay, e.Color); } private void SetColor(Control ctrl, Color col) { ctrl.BackColor = col; string s = string.Format("{0}, {1:X}", col.Name, col.ToArgb()); ctrl.Text = s; ctrl.ForeColor = (col.GetBrightness() < 0.3) ? (Color.White) : (Color.Black); } }
VB.NET
Public Class Form1 Public Sub New() InitializeComponent() cbEnable.Checked = ctPicker.Enabled lblDisplay.BackColor = ctPicker.Color lblDisplay.Text = ctPicker.Color.Name End Sub Private Sub cbEnable_CheckedChanged(ByVal sender As Object, _ ByVal e As EventArgs) ctPicker.Enabled = cbEnable.Checked End Sub Private Sub Form1_Load(ByVal sender As Object, _ ByVal e As EventArgs) duSort.Items.AddRange([Enum].GetValues(GetType(Order))) duSort.SelectedIndex = 0 duScheme.Items.AddRange([Enum].GetValues(GetType(Scheme))) duScheme.SelectedIndex = 0 SetColor(lblDisplay, ctPicker.Color) End Sub Private Sub duSort_SelectedItemChanged(ByVal sender As Object, _ ByVal e As EventArgs) ctPicker.SortOrder = CType(duSort.SelectedItem, Order) End Sub Private Sub duScheme_SelectedItemChanged(ByVal sender As Object, _ ByVal e As EventArgs) ctPicker.ColorScheme = CType(duScheme.SelectedItem, Scheme) End Sub Private Sub ctPicker_ColorChanged(ByVal sender As Object, _ ByVal e As ColorChangedEventArgs) SetColor(lblDisplay, e.Color) End Sub Private Sub SetColor(ByVal ctrl As Control, ByVal col As Color) ctrl.BackColor = col Dim s As String = String.Format("{0}, {1:X}", col.Name, _ col.ToArgb()) ctrl.Text = s ctrl.ForeColor = If((col.GetBrightness() < 0.3), _ (Color.White), (Color.Black)) End Sub End Class
On the form, we add the Scheme and the Order to the DomainUpDown controls. Then, we set the new Scheme or Order once a selection is made. This loads a new sequence of colors in our box. Finally, when we have selected, it updates the display label, as shown in Figure 2.
Figure 2: Running
Conclusion
Well, this turned out much more complicated than I initially thought. There is a lot information and code to digest, but I am sure that you will find it useful. Until next time, cheers!