views:

898

answers:

1

I have a list of objects (of type 'TrackedSet') that is data bound to a DataGridView. When a new row is added, I want to be able to change the row appearance based on a property of the data bound object.

I can paint a custom background of the row in the RowPrePaint event, but I'm struggling to change the forecolor for the row.

From the code below, I am changing this by constantly changing the DefaultCellStyle property for the row to a predefined cell style which has the forecolour set to what I want.

Is this right? It seems clunky and just plain wrong.

Any advice appreciated.

/// <summary>
        /// Handles the drawing of each DataGridView row depending on TrackedSet error or flagged state.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void setLogDataGridView_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
        {
            if ((e.State & DataGridViewElementStates.Displayed) == DataGridViewElementStates.Displayed)
            {
                // Always disable normal painting of selection background and focus.
                e.PaintParts &= ~(DataGridViewPaintParts.SelectionBackground |
                                  DataGridViewPaintParts.Focus);

                // Get the tracked set associated with the row being painted.
                TrackedSet set = this._setLogBindingSource[e.RowIndex] as TrackedSet;

                if (set.IsFlagged || set.IsError) // Row requires custom painting.
                {
                    Color backColour1 = Color.Empty;
                    Color backColour2 = Color.Empty;

                    // Get rectangle of area to paint with custom brush.
                    Rectangle rowBounds = new Rectangle(
                        e.RowBounds.Left + 1,                                   // Location x
                        e.RowBounds.Top,                                        // Location y
                        this.setLogDataGridView.Columns.GetColumnsWidth(
                            DataGridViewElementStates.Visible) -
                        this.setLogDataGridView.HorizontalScrollingOffset + 1,  // Width
                        e.RowBounds.Height);                                    // Height

                    // Disable painting of backgrounds when custom painting row.
                    e.PaintParts &= ~(DataGridViewPaintParts.Background |
                                      DataGridViewPaintParts.ContentBackground);

                    if (set.IsFlagged) // Highlight colour.
                    {
                        backColour1 = this._highlightBackColour1;
                        backColour2 = this._highlightBackColour2;
                        this.setLogDataGridView.Rows[e.RowIndex].DefaultCellStyle = this._highlightStyle;
                    }
                    else // Error colour.
                    {
                        backColour1 = this._errorBackColour1;
                        backColour2 = this._errorBackColour2;
                        this.setLogDataGridView.Rows[e.RowIndex].DefaultCellStyle = this._errorStyle;
                    }

                    // Paint the custom background.
                    using (Brush lgb = new LinearGradientBrush(rowBounds, backColour1, backColour2, LinearGradientMode.Vertical))
                    {
                        e.Graphics.FillRectangle(lgb, rowBounds);
                    }
                }
            }
        }
+1  A: 

You can try setting the background and foreground color in the CellFormatting event.

void grid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
    TrackedSet set = this._setLogBindingSource[e.RowIndex] as TrackedSet;
    if (set.IsFlagged)
    {
        e.CellStyle.BackColor = Color.Blue;
        e.CellStyle.ForeColor = Color.White;
    }
    else if (set.IsError)
    {
        e.CellStyle.BackColor = Color.Red;
        e.CellStyle.ForeColor = Color.Blue;
    }
}

To have your custom gradiant background you would probably still need to do that in the RowPrePaint event, but try setting the ForeColor in the CellFormatting event.

You could also try setting the DefaultCellStyle on the row right after you bind it to the DataGridView, but I believe that certain events will trigger the DefaultCellStyle to be reset back to the RowTemplate. You may be able to set the DefaultCellStyle on the RowTemplate after you bind the data to the grid so your cell style won't be reset.

Adam Hughes
Thanks Adam, trying your code above *almost* works. Each added row that needs custom forecolor gets drawn with he DefaultCellStyle colour initially, and then gets the custom colour as it scrolls up the grid (as it is repainted I guess). (BTW, rows are getting added periodically as my app runs.) I thought I did the hard bit by getting the gradient paint to work! :(
Andy
Sorry to hear that Andy. I use code like this pretty often and it usually works fine, but in some cases we will see a single cell that doesn't get it's background color set properly. Clicking on the cell gives it the proper color. Try refreshing the DataGridView or invalidating the row after you add rows to see if it draws properly.
Adam Hughes
I am marking this answer as accepted, however I have changed the way I implement it slightly. I assign a predefined CellyCsyle to the e.CellStyle property, one that has the BackColor and SelectedBackColor set to the same. This produces the effect I am after, although I have decided to sacrifice my gradient background, and I am only colouring one cell instead of the entire row.
Andy