2

Popular way (1, 2) for custom painting of items in DataGridViewComboBox is handling of event DataGridView1.EditingControlShowing and setting up DrawItem event handlers there:

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

You see what is wrong: it uses control-level event to handle work for columns. But what if I have 50+ datagridviews? Painting of custom combo boxes should be handled per column instance, leaving control level intact.

Minimal implementation of per-column handling is below. The only problem I have is that OnDrawItem() method is not getting called. What am I missing? (You can see that fuchsia background override from the same class is working.)

(To reproduce, just paste the following into class file and add column of type DataGridViewCustomPaintComboBoxColumn to your DataGridView1.)

public class DataGridViewCustomPaintComboBoxColumn : DataGridViewComboBoxColumn
{
    public DataGridViewCustomPaintComboBoxColumn()
    {
        base.New();
        CellTemplate = new DataGridViewCustomPaintComboBoxCell();
    }

}

public class DataGridViewCustomPaintComboBoxCell : DataGridViewComboBoxCell
{
    public override Type EditType {
        get { return typeof(DataGridViewCustomPaintComboBoxEditingControl); }
    }

    protected override void Paint(...)
    {
        //painting stuff for incative cells here - works well
    }

}

public class DataGridViewCustomPaintComboBoxEditingControl : DataGridViewComboBoxEditingControl
{
    public override Color BackColor { // property override only for testing
        get { return Color.Fuchsia; }    // test value works as expected
        set { base.BackColor = value; }
    }

    protected override void OnPaint(PaintEventArgs e) // never called - why?
    {
        base.OnPaint(e)
    }

    protected override void OnDrawItem(DrawItemEventArgs e)  // never called - why?
    {
        base.OnDrawItem(e)
    }
}
Konamiman
  • 49,681
  • 17
  • 108
  • 138
miroxlav
  • 11,796
  • 5
  • 58
  • 99

1 Answers1

3

The only problem I have is that OnDrawItem() method is not getting called. What am I missing?

What you see inside a focussed DataGridViewComboBoxCell is pretty much a 'real' ComboBox and you need a reference to it in order to hook into its DrawItem event! (You can have a look the Controls collection of the DGV to see that there is one more when a ComboBoxCell has focus.. DataGridViewComboBoxEditingControl is directly descended from ComboBox )

Note: One difference to an out of the box CombBox is that is has set DrawMode = OwnerDrawVariable by default. If you want to replace it by something you create yourself, don't forget to set it in code!

Looks like that is what you are missing!

If you can create your own DataGridViewComboBoxEditingControl and make it appear in your DGV you're all set.


An alternative model I often use is a sort of 'Decorator', that implements the custom drawing for all columns you register with it.

DgvCombBoxPainter.Register(DataGridView dgv, stringOrInt columnNameOrIndex..)

In the registration function it would hook into the necessary DGV methods, that is EditingControlShowing to get at the ComboBox you see, when the DGV has focus and the CellPainting for the other cases.

The decorator can be static and needs only one ComboBox for all columns you have registered, as only one can have focus at a time..

If you can handle all painting code in the decorator all the better. From the events it can always reach back to the DataGridView via sender and the cells' properties via the DataGridViewCellPaintingEventArgs parameter..

You can even put a reference to the current cell into the ComboBox reference's Tag:

theBoxCell.Tag = dataGridView1.CurrentCell;

If you want you could also provide individual delegates to call in the paint event(s) when registering. You would then have to keep a list of those, maybe in a Dictionary..

TaW
  • 53,122
  • 8
  • 69
  • 111
  • I understand your static decorator idea but before accepting the answer, I'm not sure about your reasoning. Exactly as you say, `DataGridViewComboBoxCell` is not a Control. It is used to paint replicas of the control in inactive cells (and serve single control instance to edited cell). So overriding its `OnPaint()` event works as expected (I extended the above source). OTOH `DataGridViewComboBoxEditingControl` *is* a Control (direct descendant of `ComboBox`) so when its instance is created for use with `EditingControlShowing` event, its overridden `OnPaint()` and `ItemDraw()` should work, no? – miroxlav Jul 17 '15 at 06:48
  • Yes, I think you got that all right, except that I'm not sure the Paint/OnPaint events will take effect or even fire; they are replaced by the DrawItem/OnDrawItem event. To draw the portion that is not dropped you need to check for `e.Index == -1`. – TaW Jul 17 '15 at 07:02
  • At the moment I'm not sure if we are thinking the correct way. I need to reaffirm. My points: First : [reference source for DataGridViewComboBoxEditingControl.cs](http://bit.ly/1Ojc1Zl) does *not* show any manipulation with its `ComboBox` precedessor.(No disabling of painting stuff etc.) Second: Overriding `OnPaint()` and `OnDrawItem()` does *not* work even with standard plain `ComboBox`. So maybe after discovering how to correctly create custom-painted combobox, no decorator might be required – just a properly written descendant of `DataGridViewComboBoxEditingControl` :). Let me check that... – miroxlav Jul 17 '15 at 07:42
  • _Overriding OnPaint() and OnDrawItem() does not work even with standard plain ComboBox_. Hm, OnPaint I would not expect to work, it doesn't even show in the designer or Intellisense . But OnDrawItem?? (You did set its DrawMode to some OwnerDrawXxx ?) – TaW Jul 17 '15 at 07:47
  • 1
    I forgot setting DrawMode! It seems it solves it all :) `OnDrawItem()` is getting called now so I can do whatever I need. Make this your answer and I will accept that. Perhaps forget all that stuff with static decorator, it has no added value for custom painting. So I'll accept your answer saying: just add `DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed` (or `OwnerDrawVariable`) into `DataGridViewComboBoxEditingControl` constructor. – miroxlav Jul 17 '15 at 08:02
  • done. I still like to use decorators, as they can plug into any control so easily, especially when added a little late in the developement process ;-) – TaW Jul 17 '15 at 08:07
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/83514/discussion-between-miroxlav-and-taw). – miroxlav Jul 17 '15 at 08:07