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.
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.
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;
}
}