How to change the DataGridViewCheckBoxColumn checkbox selection background color?

78 Views Asked by At

I'm using a DataGridView in a Windows Forms application in which I use DataGridViewCheckBoxColumns. The problem is that they have a standard selection backcolor which I can't seem to change. What I mean is the blue color in the checkboxes as can be seen in the image below.

enter image description here

I have tried to change the color like this (while looping trough the DataGridViewColumns in the initialization of the DataGridView):

if (col.GetType() == typeof(DataGridViewCheckBoxColumn))
{
    col.CellTemplate.Style.SelectionBackColor = WinFormsUITheme.styleColor;
    col.CellTemplate.Style.SelectionForeColor = WinFormsUITheme.styleColor;
}

But this doesn't work. How should this color be changed? Or is this not possible? Is there maybe a way to change the standard windows selection highlight color for a whole Windows forms project? Or maybe this should be done in the DataGridViewCellPainting event?

1

There are 1 best solutions below

3
IV. On

As I understand it, your goal is to have control over the selection back color and possibly the color of the fill on the checkboxes themselves.

custom colors

This snippet manipulates the DefaultCellStyle.SelectionBackColor of the entire DGV but you can override this for an individual cell using its Style property, or for a single column using the column'sDefaultCellStyle. So, if this is all you need to do, you don't necessarily have to custom paint at all.

public MainForm()
{
    InitializeComponent();
    Disposed += (sender, e) => Provider.Dispose();
    foreach (var radio in tableLayoutPanelRadios.Controls.OfType<RadioButton>())
    {
        radio.CheckedChanged += (sender, e) =>
        {
            switch (radio.Name)
            {
                case nameof(radioNone):     // <= Used in `CellPainting` handler for e.PaintBackground.
                case nameof(radioDefault):
                    dataGridView.DefaultCellStyle.SelectionBackColor = Color.FromArgb(0, 0x78, 0xd7);
                    dataGridView.DefaultCellStyle.SelectionForeColor = Color.White;
                    break;
                case nameof(radioYellow):
                    dataGridView.DefaultCellStyle.SelectionBackColor = Color.LightYellow;
                    dataGridView.DefaultCellStyle.SelectionForeColor = Color.Red;
                    break;
            }
        };
    }
}

Your comment mentions the CellPainting event and one decent way to handle it is by drawing a unicode text glyph. For example, the custom glyph font checkbox-icons.ttf used in the above screenshot was made on Fontello, set as an embedded resource, and loaded using the utility extension shown below.

fontello


MainForm

public partial class MainForm : Form
{
    .
    .
    .
    FontFamily CheckBoxFont { get; } = "checkbox-icons".LoadFamilyFromEmbeddedFont();
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        {
            dataGridView.DataSource = Recordset;
            dataGridView.RowHeadersVisible = false;
            dataGridView.AllowUserToAddRows = false;
            dataGridView.CellPainting += (sender, e) =>
            {
                if (e.RowIndex >= 0 && e.ColumnIndex >= 0 && dataGridView[e.ColumnIndex, e.RowIndex] is DataGridViewCheckBoxCell checkbox)
                {
                    if(dataGridView[e.ColumnIndex, e.RowIndex].IsInEditMode == true)
                    {
                        dataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
                    }
                    e.Handled = true;
                    e.PaintBackground(e.CellBounds, false);
                    using (var glyphFont = new Font(CheckBoxFont, dataGridView.DefaultCellStyle.Font.Size + 1))
                    using (var sf = new StringFormat
                    {
                        Alignment = StringAlignment.Center,
                        LineAlignment = StringAlignment.Center,
                    })
                    {
                        Brush brush;   
                        switch (dataGridView.Columns[e.ColumnIndex].Name)
                        { 
                            // Use a static brush (doesn't require dispose)
                            case nameof(DemoRecordClass.IsGreen): brush = Brushes.Green; break;
                            case nameof(DemoRecordClass.IsBlue): brush = Brushes.Blue; break;
                            case nameof(DemoRecordClass.IsRed): brush = Brushes.Red; break;
                            default:
                                return;
                        }
                        var glyph = e.Value is bool isChecked && isChecked ? "\uE801" : "\uE800";
                        e.Graphics?.DrawString(glyph, glyphFont, brush, e.CellBounds, sf);
                    }
                }
            };
            foreach (DataGridViewColumn col in dataGridView.Columns)
            {
                col.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
            }
            Recordset.Add(new DemoRecordClass{ IsGreen = true, IsRed = true, });
            Recordset.Add(new DemoRecordClass{ IsBlue= true, });
        }
    }
    BindingList<DemoRecordClass> Recordset { get; } = new BindingList<DemoRecordClass>();
}

Example Record Class
class DemoRecordClass
{
    static int count = 1;
    public string Name { get; set; } = $"Item {count++}";
    public bool IsGreen { get;set; }
    public bool IsBlue { get;set; }
    public bool IsRed { get;set; }
}

Font loading utility
public static class Provider
{
    public static FontFamily LoadFamilyFromEmbeddedFont(this string ttf)
    {
        var asm = typeof(Provider).Assembly;
#if DEBUG
        var names = asm.GetManifestResourceNames();
        { }
#endif
        var fontFamily = privateFontCollection.Families.FirstOrDefault(_ => _.Name.Equals(ttf));
        if (fontFamily == null)
        {
            var resourceName = asm.GetManifestResourceNames().FirstOrDefault(_ => _.Contains(ttf));
            if (string.IsNullOrWhiteSpace(resourceName))
            {
                throw new InvalidOperationException("Expecting font file is embedded resource.");
            }
            else
            {
                // Get the embedded font resource stream
                using (Stream fontStream = asm.GetManifestResourceStream(resourceName)!)
                {
                    if (fontStream == null)
                    {
                        throw new InvalidOperationException($"Font resource '{resourceName}' not found.");
                    }
                    else
                    {
                        // Load the font into the PrivateFontCollection
                        byte[] fontData = new byte[fontStream.Length];
                        fontStream.Read(fontData, 0, (int)fontStream.Length);

                        IntPtr fontPtr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(fontData.Length);
                        System.Runtime.InteropServices.Marshal.Copy(fontData, 0, fontPtr, fontData.Length);
                        privateFontCollection.AddMemoryFont(fontPtr, fontData.Length);

                        fontFamily = privateFontCollection.Families.First(_ => _.Name.Equals(ttf));
                    }
                }
            }
        }
        else
        {   /* G T K */
            // Already loaded
        }
        return fontFamily;
    }
    public static void Dispose() => privateFontCollection?.Dispose();
    private static PrivateFontCollection privateFontCollection { get; } = new PrivateFontCollection();
}