0

I have an application in which one of my screens makes heavy use of DataGridViewCheckBoxCells. In short, I have the table set up in VirtualMode with an Undo system involved, validation, and so on. In some cases it doesn't make sense to check a box where another box is not first checked and so on. I let users do it, but I have a checking function that sets the cell's ErrorText property and doesn't let them continue.

Blah, blah. I've already verified it's not my code clearing the ErrorText or anything like that. The problem is DataGridViewCheckBoxCells do not paint the error icon / glyph thingy when it is the current cell. I cannot reason why they were designed this way. Indeed the reference source (DataGridViewCheckBoxCell.cs) backs me up:

In DataGridViewCheckBoxCell.cs:

protected override Rectangle GetErrorIconBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex)
{
    // some checks ...

    Point ptCurrentCell = this.DataGridView.CurrentCellAddress;
    if (ptCurrentCell.X == this.ColumnIndex &&
        ptCurrentCell.Y == rowIndex && this.DataGridView.IsCurrentCellInEditMode)
    {
        // PaintPrivate does not paint the error icon if this is the current cell.
        // So don't set the ErrorIconBounds either.
        return Rectangle.Empty;
    }

    // behavior ...
}

and taking a look at DataGridViewCheckBoxCell.PaintPrivate:

private Rectangle PaintPrivate(Graphics g, 
    Rectangle clipBounds,
    Rectangle cellBounds, 
    int rowIndex, 
    DataGridViewElementStates elementState,
    object formattedValue,
    string errorText,
    DataGridViewCellStyle cellStyle,
    DataGridViewAdvancedBorderStyle advancedBorderStyle,
    DataGridViewPaintParts paintParts,
    bool computeContentBounds,
    bool computeErrorIconBounds,
    bool paint)
{
    // blah blah

    // here it determines "If I am the current cell DO NOT draw the error icon!"
    Point ptCurrentCell = this.DataGridView.CurrentCellAddress;
    if (ptCurrentCell.X == this.ColumnIndex && 
        ptCurrentCell.Y == rowIndex && this.DataGridView.IsCurrentCellInEditMode)
    {
        drawErrorText = false;
    }

    // sure enough, drawErrorText must be true here for the error icon to be drawn
    if (paint && DataGridViewCell.PaintErrorIcon(paintParts) && drawErrorText && this.DataGridView.ShowCellErrors)
    {
        PaintErrorIcon(g, cellStyle, rowIndex, cellBounds, errorBounds, errorText);
    }
}

How can I get around this? I tried to subclass DataGridViewCheckBoxCell to little avail. There are so many internal functions and constants I can't hit. I tried to hack it in the Form that uses this table, and I managed to de-select the current cell if it is a DataGridViewCheckBoxCell after its DirtyStateChanged which I am intercepting anyway (for reasons). But this doesn't work--when a user selects a cell, sure enough, the error icon disappears. I can't intercept and block DataGridViewCheckBoxCells from being selected with SelectionChanged, because selection is the first step in the process of editing a cell's value.

What can I do?

Community
  • 1
  • 1
Gutblender
  • 1,340
  • 1
  • 12
  • 25
  • 1
    [This post](http://stackoverflow.com/questions/24562966/reposition-the-error-icon-of-a-datagridview/24600915#24600915) describes a workaround: Instead of the ErrorIcon is uses a Backcolor to indicate an error.. – TaW Nov 03 '14 at 18:50
  • That could be one way, thanks. Unfortunately, I am already using `BackColor` for a visual aid to understanding the context of the cell, or its meaning. But, perhaps I could mesh these together by "flashing" back and forth between two colors. – Gutblender Nov 03 '14 at 19:09

1 Answers1

0

The following code draws a "beautiful" error icon in every cell that has an error text. You may have to adapt it to your specific case :

static Icon m_errorIcon;

public static Icon ErrorIcon
{
    get
    {
        if (m_errorIcon == null)
        {
            ErrorProvider errorProvider = new ErrorProvider();
            m_errorIcon = errorProvider.Icon;
            errorProvider.Dispose();
        }
        return m_errorIcon;
    }
}

private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
    try
    {
        if ((e.PaintParts & DataGridViewPaintParts.ErrorIcon) != 0 && !string.IsNullOrEmpty(e.ErrorText))
        {
            Icon icon = ErrorIcon;
            int pixelMargin = 2;

            Rectangle cellBounds = new Rectangle(e.CellBounds.X + pixelMargin, e.CellBounds.Y, e.CellBounds.Width - 2 * pixelMargin, e.CellBounds.Height);
            Rectangle firstIconRectangle = new Rectangle(cellBounds.Left, cellBounds.Top + Math.Max((cellBounds.Height - icon.Width) / 2, 0), Math.Min(icon.Width, cellBounds.Width), Math.Min(icon.Width, cellBounds.Height));

            e.Paint(e.ClipBounds, e.PaintParts & ~DataGridViewPaintParts.ErrorIcon);
            e.Graphics.DrawIcon(icon, firstIconRectangle);
            e.Handled = true;
        }
    }
    catch (Exception exception)
    {
    }
}
Bioukh
  • 1,888
  • 1
  • 16
  • 27