1

I can easily paint items in DataGridViewComboBox dropdown list. But I can't figure out how to paint inactive cells in the same column.

Example of DataGridViewComboBox

I've seen, studied and tried numberous examples for classical ComboBox, but I don't understand all aspects of DataGridViewComboBox.

Currently I have DataGridViewCustomPaintComboBox class derived from DataGridViewComboBox. What is the minimum set of overrides to supply? Can you point me in right direction?

miroxlav
  • 11,796
  • 5
  • 58
  • 99

2 Answers2

3

Update:

Custom drawing a DataGridViewComboBox is a little involved. In fact it consists of four different drawing cases:

For the unfocused cells you need to code the CellPainting event:

private void dataGridView1_CellPainting(object sender, 
                                        DataGridViewCellPaintingEventArgs e)
{
   // drawstuff nr 1: draw the unfocused cell
}

When the cell has focus the actual DataGridViewComboBoxEditingControl (which is a direct descendent from ComboBox) is created and shown.

You need get a handle to it (in the EditingControlShowing event) and then should code three more cases in its DrawItem event:

void theBoxCell_DrawItem(object sender, DrawItemEventArgs e)
{
    if (e.Index < 0)
    {
       // drawstuff 2: draw the undropped top portion
    }
    else
    {
       if ((e.State & DrawItemState.Selected) != DrawItemState.None
       {
          // drawstuff 3:  draw a selected item
       }
       else
       {
          // drawstuff 4:  draw an unselected item   
       }
    }
 }

A few notes on the various paint codes:

  • drawstuff 1: Here you should draw an arrow after drawing the text. For this best make use of the ComboBoxRenderer.DrawDropDownButton method. You will need to know the position and size, SystemInformation.VerticalScrollBarWidth should help with that.. note that the TextRenderer.DrawText not only lets you use nice TextFormatFlags to help with alignment, but also a Backcolor!

  • drawstuff 2: Note that the ComboBox unfortunately doesn't pick up the BackColor of its cell. It will still help to set it, so you can refer to it as the target color. like in the following darw codes you will wnat to use a combination of e.Graphics.DrawXxx calls and TextRenderer.DrawText. For easier reference to the DGV cells it belongs to, you may want to store the CurrentCell in the reference ComboBox's Tag when setting it in the EditingControlShowing event.

  • drawstuff 3: The selected Item may have special colors for font and background

  • drawstuff 4: The regular item is just that: rather regular..


The code below is the original version of my answer, only covering cases 3&4:

Here is a short example to show you how to paint a DataGridViewComboBox. Note that I only show the bare minimum and didn't go into painting the colored squares..:

We start by defining a class level reference to the cell:

ComboBox theBoxCell = null;

In the EditingControlShowing we set the reference, add an event handler and set it to an owner-draw mode:

private void dataGridView1_EditingControlShowing(object sender,
             DataGridViewEditingControlShowingEventArgs e)
{
    theBoxCell = (ComboBox) e.Control;
    theBoxCell.DrawItem += theBoxCell_DrawItem;
    theBoxCell.DrawMode = DrawMode.OwnerDrawVariable;
}

Finally we add the draw code:

void theBoxCell_DrawItem(object sender, DrawItemEventArgs e)
{
    if (e.Index < 0) return;
    string t = theBoxCell.Items[e.Index].ToString();
    using (SolidBrush brush = new SolidBrush(
        (e.State & DrawItemState.Selected) != DrawItemState.None ?
                   Color.LightCyan : Color.LightGray))
        e.Graphics.FillRectangle(brush, e.Bounds);
    e.DrawFocusRectangle();
    e.Graphics.DrawString(t, Font, Brushes.DarkGoldenrod, e.Bounds.X + 6, e.Bounds.Y + 1);

}

We could have added the draw code in a linq to the event hook-up as well..

Here is the result:

enter image description here

TaW
  • 53,122
  • 8
  • 69
  • 111
  • I'm sorry if I improperly formulated my question. I already can paint all you are showing. But what about inactive DataGridView rows without a focus? – miroxlav Jul 16 '15 at 12:29
  • In the meantime, I think that I figured that out. I added the answer. – miroxlav Jul 16 '15 at 12:41
  • Well, this does not answer the question, what to actually do the painting. The user should see an arrow after all and if you want that to be colored as well you are in for some lines of code, no? – TaW Jul 16 '15 at 14:45
  • Ha, these things happen in comments - both parties not quite understanding what the other means. I'll update my answer to include the painting of the DGVComboBoxCells in a nice way later, but atm I'm sitting inisde heatwave ;-) - I will involve the `CellPainting` event of the DGV and a `ComboBoxRenderer` for the arrow.. - Also an extension to the `DrawItem` code to properly draw when `e.Index==-1` – TaW Jul 16 '15 at 21:18
  • I'll be happy with suggested additions and this would create value for others, too. The biggest downside of the solution proposed in answer is that the column is bound to `DataGridView1.EditingControlShowing` event. So you have to create unique EditingControlShowing method + DrawItem() painting again and again for each DataGridView. Not very clever if one has 50+ grids as I do. I think much more innovative approach would be binding *custom painting to the instance of DGV column*. Do you think you can [help me with that?](http://stackoverflow.com/q/31466145/2392157) – miroxlav Jul 16 '15 at 23:40
3

The required minimum to paint inactive cells without the focus seems to be CellTemplate assignment and Paint() override:

public class DataGridViewCustomPaintComboBox : DataGridViewComboBoxColumn
{

    public DataGridViewCustomPaintComboBox()
    {
        base.New();
        CellTemplate = new DataGridViewCustomPaintComboBoxCell();
    }

}

public class DataGridViewCustomPaintComboBoxCell : DataGridViewComboBoxCell
{

    protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
    {
        // modify received arguments here
        base.Paint(...); // paint default parts (see paintParts argument)
        // add any custom painting here

    }

}
miroxlav
  • 11,796
  • 5
  • 58
  • 99