Skip to content

Instantly share code, notes, and snippets.

@CamelCaseName
Created July 24, 2023 18:20
Show Gist options
  • Save CamelCaseName/792e9b782df5b7abfb92727b36596464 to your computer and use it in GitHub Desktop.
Save CamelCaseName/792e9b782df5b7abfb92727b36596464 to your computer and use it in GitHub Desktop.
c# WinForms CheckListBox with 3 different colors and no double click!
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace CustomComponents
{
[SupportedOSPlatform("Windows")]
public static class Winutils
{
public const int WM_LBUTTONDOWN = 0x201;
public const int WM_LBUTTONDBLCLK = 0x203;
public const int WM_PAINT = 0x00f;
public const int WM_ERASEBKGND = 0x00e;
public const int WM_MOUSEMOVE = 0x200;
}
/// <summary>
/// Borrowed from https://stackoverflow.com/questions/2130934/how-change-the-color-of-selecteditem-in-checkedlistbox-in-windowsforms
/// Creates a coloured Rectangle in each element, depending on checked state.
/// </summary>
public class ColoredCheckedListBox : CheckedListBox
{
/// <summary>
/// List containing all indices that are part of the search result
/// </summary>
public List<int> ColorList1 = new();
/// <summary>
/// list containing all indices that are duplicates fo the english string
/// </summary>
public List<string> ColorList2 = new();
/// <summary>
/// Use double buffering, removes flicker
/// </summary>
protected new bool DoubleBuffered = true;
/// <summary>
/// A Override for the default OnDrawItem which replaces the background with a coloured rectangle given a List of indices
/// </summary>
/// <param name="e">The DrawItemEventArgs which will be manipulated</param>
protected override void OnDrawItem(DrawItemEventArgs e)
{
var e2 =
new DrawItemEventArgs
(
e.Graphics,
e.Font,
e.Bounds,
e.Index,
e.State,
e.ForeColor, //colour yellow if it is part of the search, else colour normally
e.Index > -1 ?
ColorList1.Contains(e.Index) ?
Color.DarkOrange :
ColorList2.Contains(Items[e.Index].ToString() ?? string.Empty) ?
Color.FromArgb(130, 80, 130) :
CheckedIndices.Contains(e.Index) ?
Color.FromArgb(80, 130, 80) :
Color.FromArgb(130, 80, 80)
:
e.BackColor
);
base.OnDrawItem(e2);
}
/// <summary>
/// Hack to get exactly the behaviour we want (https://stackoverflow.com/a/3897126) adapted and expanded.
/// Only allows double clicks on the far left of the control, or only single clicks elsewhere.
/// </summary>
/// <param name="m"> The windows Message to be used</param>
protected override void WndProc(ref Message m)
{
// Filter WM_LBUTTONDBLCLK and MW_LBUTTONDOWN
if (m.Msg != Winutils.WM_LBUTTONDBLCLK && m.Msg != Winutils.WM_LBUTTONDOWN)
{
base.WndProc(ref m);
}
else if (m.Msg == Winutils.WM_LBUTTONDOWN) //our own handle
{
//get mouse cursor position from message https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-lbuttondown
byte[] bytes = BitConverter.GetBytes(m.LParam.ToInt64());
short x = BitConverter.ToInt16(bytes, 0);
short y = BitConverter.ToInt16(bytes, 2);
//create point from cursor pos. low word is x, high word is y
var CursorPosition = new Point(x, y);
//if a new item would be selected, we can do that. or if the cursor is on one of the check marks. but else we do nothing
if (SelectedIndex != IndexFromPoint(CursorPosition) || x < 14)
{
base.WndProc(ref m);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment