Search code examples
c#.netwinformsdatagridviewdatagridviewcolumn

How to format a specific item in the ComboBox in a DataGridViewComboBoxColumn?


I have code similar to this in a method that (re)creates the columns in the DataGridView:

MyColumn = new DataGridViewComboBoxColumn()
{
    Name = "..",
    HeaderText = "..",
    SortMode = DataGridViewColumnSortMode.NotSortable
};       
MyColumn.Items.Clear();
foreach (string s in MyStringList)
{
    MyColumn.Items.Add(s);
}
MyColumn.Items.Add(""); 
// I would like this empty string to be shown as "No group" 
// with an italic grayed out font

I think that I probably must create a class for the items of the ComboBox-es in the column, in which I should override the ToString() method, but I want to know how to format the No Group item.

A related question is here which is a about a normal ComboBox not inside a DataGridView, with the answer solving the problem using the DrawMode prperty and DrawItem event of the ComboBox class.


Solution

  • For custom-painting the ComboBox, you need to handle EditingControlShowing and then get the EditingControl which is DataGridViewComboBoxEditingControl and then set its DrawMode to OwnerDrawFixed and handle its DrawItem event.

    For custom-painting the cell, you need to handle CellPainting event and set different font and color for the cell styles and let the paint continue with new values. You can also paint the whole cell if you want.

    enter image description here

    Example

    Load Sample Data:

    private DataTable LoadProducts()
    {
        var dt = new DataTable();
        dt.Columns.Add("Name");
        dt.Columns.Add("CategoryId", typeof(int));
        dt.Rows.Add("P1", 1);
        dt.Rows.Add("P2", 1);
        dt.Rows.Add("P3", DBNull.Value);
        return dt;
    }
    private DataTable LoadCategories()
    {
        var dt = new DataTable();
        dt.Columns.Add("Id", typeof(int));
        dt.Columns.Add("Name");
        dt.Rows.Add(DBNull.Value, "No Category");
        dt.Rows.Add(1, "C1");
        dt.Rows.Add(2, "C2");
        dt.Rows.Add(2, "C3");
        return dt;
    }
    

    Setup DataGridView Columnms:

    private void Form1_Load(object sender, EventArgs e)
    {
        var products = LoadProducts();
        var categories = LoadCategories();
    
        dataGridView1.Columns.Add(new DataGridViewTextBoxColumn()
        {
            Name = "NameColumn",
            DataPropertyName = "Name",
            HeaderText = "Name"
        });
        dataGridView1.Columns.Add(new DataGridViewComboBoxColumn()
        {
            Name = "CategoryIdColumn",
            DataPropertyName = "CategoryId",
            HeaderText = "Category",
            DataSource = categories,
            ValueMember = "Id",
            DisplayMember = "Name",
            DisplayStyle= DataGridViewComboBoxDisplayStyle.Nothing
        });
        dataGridView1.DataSource = products;
        dataGridView1.EditingControlShowing += DataGridView1_EditingControlShowing;
        dataGridView1.CellPainting += DataGridView1_CellPainting;
    }
    

    Handle EditingControlShowing

    private void DataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
    {
        if (dataGridView1?.CurrentCell?.OwningColumn?.Name != "CategoryIdColumn")
            return;
        var combo = e.Control as DataGridViewComboBoxEditingControl;
        if (combo == null)
            return;
    
        combo.DrawMode = DrawMode.OwnerDrawFixed;
        combo.DrawItem += (obj, args) =>
        {
            var txt = args.Index >= 0 ? combo.GetItemText(combo.Items[args.Index]) : "";
            var textColor = args.Index == 0 ? SystemColors.GrayText : SystemColors.ControlText;
            var font = args.Index == 0 ? new Font(combo.Font, FontStyle.Italic) : combo.Font;
            if ((args.State & DrawItemState.Selected) == DrawItemState.Selected)
            {
                textColor = SystemColors.HighlightText;
            }
            args.DrawBackground();
            TextRenderer.DrawText(args.Graphics, txt, font,
                args.Bounds, textColor,
                TextFormatFlags.VerticalCenter | TextFormatFlags.Left);
        };
    }
    

    Handle CellPainting

    private void DataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
    {
        if (e.ColumnIndex < 0 || e.RowIndex < 0 ||
                dataGridView1.Columns[e.ColumnIndex].Name != "CategoryIdColumn")
            return;
        if (dataGridView1[e.ColumnIndex, e.RowIndex].Value == DBNull.Value)
        {
            e.CellStyle.Font = new Font(e.CellStyle.Font, FontStyle.Italic);
            e.CellStyle.ForeColor = SystemColors.GrayText;
        }
        else
        {
            e.CellStyle.Font = new Font(e.CellStyle.Font, FontStyle.Regular);
            e.CellStyle.ForeColor = SystemColors.ControlText;
        }
    }