2

I'm trying to custom paint a DataGridView, but I'm having trouble getting it to look right after the DataGridView is scrolled. There are lines that show up across my cells. The problem is that the paint event seems to not be redrawing the cells when the control is scrolled, so cells that were drawn at the edge of the form are not redrawn once they are scrolled to the center.

I can eliminate the problem by ignoring the fact that part of the cells on the edge of DataGridView are not displayed and drawing them as if they are fully visible, but that will make my application not look as nice.

My question is: Is there a better way for me to paint the DataGridView so that I can adjust my cell painting for partially displayed cells? I've looked through other questions and tried using other paint events, but I haven't been able to get anything to work.

Here is a simple program that demonstrates the problem I'm talking about.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        for (int i = 0; i < 256; i++)
            dataGridView1.Rows.Add(new object[] { string.Format("Test_text_{0}", i) });

        dataGridView1.Paint += DataGridView1_Paint;
        dataGridView1.CellPainting += (s, e) => { if (e.RowIndex > 0 && e.ColumnIndex >= 0) e.Handled = true; };
    }

    private void DataGridView1_Paint(object sender, PaintEventArgs e)
    {
        Rectangle rect;

        using (Brush gridBrush = new SolidBrush(dataGridView1.GridColor),
            cellBrush = new SolidBrush(dataGridView1.DefaultCellStyle.BackColor),
            textBrush = new SolidBrush(dataGridView1.DefaultCellStyle.ForeColor))
        {
            using (Pen gridLinePen = new Pen(gridBrush))
            {
                for (int row = 0; row < dataGridView1.Rows.Count; row++)
                {
                    if (!dataGridView1.Rows[row].Displayed)
                        continue;

                    if (dataGridView1.Rows[row].Cells[0].Value != null)
                    {
                        //draw cell
                        rect = dataGridView1.GetCellDisplayRectangle(0, row, true);//Setting cutOverflow to false removes lines
                        e.Graphics.FillRectangle(cellBrush, rect);
                        e.Graphics.DrawLine(gridLinePen, rect.Left, rect.Bottom - 1, rect.Right - 1, rect.Bottom - 1);
                        e.Graphics.DrawLine(gridLinePen, rect.Right - 1, rect.Top, rect.Right - 1, rect.Bottom);

                        //add text
                        e.Graphics.DrawString(dataGridView1.Rows[row].Cells[0].Value.ToString(), 
                            dataGridView1.DefaultCellStyle.Font, textBrush, rect.X + 4, rect.Y + 4);
                    }
                    else
                        //handle empty rows
                        e.Graphics.FillRectangle(gridBrush, dataGridView1.GetCellDisplayRectangle(0, row, true));
                }
            }
        }
    }
}

Designer code

partial class Form1
{
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.dataGridView1 = new System.Windows.Forms.DataGridView();
        this.Column1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
        ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
        this.SuspendLayout();
        // 
        // dataGridView1
        // 
        this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
        this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
        this.Column1});
        this.dataGridView1.Location = new System.Drawing.Point(46, 36);
        this.dataGridView1.Name = "dataGridView1";
        this.dataGridView1.Size = new System.Drawing.Size(184, 428);
        this.dataGridView1.TabIndex = 0;
        // 
        // Column1
        // 
        this.Column1.HeaderText = "Column1";
        this.Column1.Name = "Column1";
        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(281, 522);
        this.Controls.Add(this.dataGridView1);
        this.Name = "Form1";
        this.Text = "Form1";
        ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
        this.ResumeLayout(false);

    }

    #endregion

    private System.Windows.Forms.DataGridView dataGridView1;
    private System.Windows.Forms.DataGridViewTextBoxColumn Column1;
}

Here's a screenshot

enter image description here

Tobbs
  • 1,120
  • 1
  • 6
  • 15

2 Answers2

3

The problem was that both the Paint and CellPainting events were only painting the parts of the cell that were not shown before the DataGridView was scrolled. I was able to fix the problem by forcing the entire DataGridView to redraw in the Scroll event.

    dataGridView1.Scroll += (s, e) => dataGridView1.Invalidate();
Tobbs
  • 1,120
  • 1
  • 6
  • 15
0

You have disabled the cell painting by writing

dataGridView1.CellPainting += (s, e) =>
    { if (e.RowIndex > 0 && e.ColumnIndex >= 0) e.Handled = true; };

Perform the cell painting with the CellPainting event instead of Paint event.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • I only disabled cell painting for the cells that I'm painting in the `Paint` event. I've tried using the `CellPainting` event, using the same code except on individual cells, but I get the same result. (And in my application, I need to join adjacent columns on some rows, so I'd prefer to use the 'Paint' event or one of the Pre/Post row painting events if possible.) – Tobbs Apr 05 '17 at 15:26
  • Maybe this SO solution helps: [DataGridView CellPainting Not Fully Working on Scroll](http://stackoverflow.com/a/1145680/880990) – Olivier Jacot-Descombes Apr 05 '17 at 16:07
  • 1
    ... orr this [Merged cells by handling DataGridView.Paint smears when scrolled](https://social.msdn.microsoft.com/Forums/windows/en-US/baca9e53-d927-4020-87af-6da08f1c991e/merged-cells-by-handling-datagridviewpaint-smears-when-scrolled?forum=winformsapplications) – Olivier Jacot-Descombes Apr 05 '17 at 16:13
  • 1
    The second link had the answer. I needed to invalidate the control. Thanks! – Tobbs Apr 05 '17 at 17:48