Edit

Share via


How to: Customize the Appearance of Rows in the Windows Forms DataGridView Control

You can control the appearance of DataGridView rows by handling one or both of the DataGridView.RowPrePaint and DataGridView.RowPostPaint events. These events are designed so that you can paint only what you want to while letting the DataGridView control paint the rest. For example, if you want to paint a custom background, you can handle the DataGridView.RowPrePaint event and let the individual cells paint their own foreground content. Alternately, you can let the cells paint themselves and add custom foreground content in a handler for the DataGridView.RowPostPaint event. You can also disable cell painting and paint everything yourself in a DataGridView.RowPrePaint event handler.

The following code example implements handlers for both events in order to provide a gradient selection background and some custom foreground content that spans multiple columns.

Example

using System;
using System.Drawing;
using System.Windows.Forms;

class DataGridViewRowPainting : Form
{
    private DataGridView dataGridView1 = new DataGridView();
    private Int32 oldRowIndex = 0;
    private const Int32 CUSTOM_CONTENT_HEIGHT = 30;

    [STAThreadAttribute()]
    public static void Main()
    {
        Application.Run(new DataGridViewRowPainting());
    }

    public DataGridViewRowPainting()
    {
        this.dataGridView1.Dock = DockStyle.Fill;
        this.Controls.Add(this.dataGridView1);
        this.Load += new EventHandler(DataGridViewRowPainting_Load);
        this.Text = "DataGridView row painting demo";
    }

    void DataGridViewRowPainting_Load(object sender, EventArgs e)
    {
        // Set a cell padding to provide space for the top of the focus
        // rectangle and for the content that spans multiple columns.
        Padding newPadding = new Padding(0, 1, 0, CUSTOM_CONTENT_HEIGHT);
        this.dataGridView1.RowTemplate.DefaultCellStyle.Padding = newPadding;

        // Set the selection background color to transparent so
        // the cell won't paint over the custom selection background.
        this.dataGridView1.RowTemplate.DefaultCellStyle.SelectionBackColor =
            Color.Transparent;

        // Set the row height to accommodate the content that
        // spans multiple columns.
        this.dataGridView1.RowTemplate.Height += CUSTOM_CONTENT_HEIGHT;

        // Initialize other DataGridView properties.
        this.dataGridView1.AllowUserToAddRows = false;
        this.dataGridView1.EditMode = DataGridViewEditMode.EditOnKeystrokeOrF2;
        this.dataGridView1.CellBorderStyle = DataGridViewCellBorderStyle.None;
        this.dataGridView1.SelectionMode =
            DataGridViewSelectionMode.FullRowSelect;

        // Set the column header names.
        this.dataGridView1.ColumnCount = 4;
        this.dataGridView1.Columns[0].Name = "Recipe";
        this.dataGridView1.Columns[0].SortMode =
            DataGridViewColumnSortMode.NotSortable;
        this.dataGridView1.Columns[1].Name = "Category";
        this.dataGridView1.Columns[2].Name = "Main Ingredients";
        this.dataGridView1.Columns[3].Name = "Rating";

        // Hide the column that contains the content that spans
        // multiple columns.
        this.dataGridView1.Columns[2].Visible = false;

        // Populate the rows of the DataGridView.
        string[] row1 = new string[]{"Meatloaf", "Main Dish",
            "1 lb. lean ground beef, 1/2 cup bread crumbs, " +
            "1/4 cup ketchup, 1/3 tsp onion powder, 1 clove of garlic, " +
            "1/2 pack onion soup mix, dash of your favorite BBQ Sauce",
            "****"};
        string[] row2 = new string[]{"Key Lime Pie", "Dessert",
            "lime juice, whipped cream, eggs, evaporated milk", "****"};
        string[] row3 = new string[]{"Orange-Salsa Pork Chops",
            "Main Dish", "pork chops, salsa, orange juice, pineapple", "****"};
        string[] row4 = new string[]{"Black Bean and Rice Salad",
            "Salad", "black beans, brown rice", "****"};
        string[] row5 = new string[]{"Chocolate Cheesecake",
            "Dessert", "cream cheese, unsweetened chocolate", "***"};
        string[] row6 = new string[]{"Black Bean Dip", "Appetizer",
            "black beans, sour cream, salsa, chips", "***"};
        object[] rows = new object[] { row1, row2, row3, row4, row5, row6 };
        foreach (string[] rowArray in rows)
        {
            this.dataGridView1.Rows.Add(rowArray);
        }

        // Adjust the row heights to accommodate the normal cell content.
        this.dataGridView1.AutoResizeRows(
            DataGridViewAutoSizeRowsMode.AllCellsExceptHeaders);

        // Attach handlers to DataGridView events.
        this.dataGridView1.ColumnWidthChanged += new
            DataGridViewColumnEventHandler(dataGridView1_ColumnWidthChanged);
        this.dataGridView1.RowPrePaint += new
            DataGridViewRowPrePaintEventHandler(dataGridView1_RowPrePaint);
        this.dataGridView1.RowPostPaint += new
            DataGridViewRowPostPaintEventHandler(dataGridView1_RowPostPaint);
        this.dataGridView1.CurrentCellChanged += new
            EventHandler(dataGridView1_CurrentCellChanged);
        this.dataGridView1.RowHeightChanged += new
            DataGridViewRowEventHandler(dataGridView1_RowHeightChanged);
    }

    // Forces the control to repaint itself when the user
    // manually changes the width of a column.
    void dataGridView1_ColumnWidthChanged(object sender,
        DataGridViewColumnEventArgs e)
    {
        this.dataGridView1.Invalidate();
    }

    // Forces the row to repaint itself when the user changes the
    // current cell. This is necessary to refresh the focus rectangle.
    void dataGridView1_CurrentCellChanged(object sender, EventArgs e)
    {
        if (oldRowIndex != -1)
        {
            this.dataGridView1.InvalidateRow(oldRowIndex);
        }
        oldRowIndex = this.dataGridView1.CurrentCellAddress.Y;
    }

    // Paints the custom selection background for selected rows.
    void dataGridView1_RowPrePaint(object sender,
            DataGridViewRowPrePaintEventArgs e)
    {
        // Do not automatically paint the focus rectangle.
        e.PaintParts &= ~DataGridViewPaintParts.Focus;

        // Determine whether the cell should be painted
        // with the custom selection background.
        if ((e.State & DataGridViewElementStates.Selected) ==
                    DataGridViewElementStates.Selected)
        {
            // Calculate the bounds of the row.
            Rectangle rowBounds = new Rectangle(
                this.dataGridView1.RowHeadersWidth, e.RowBounds.Top,
                this.dataGridView1.Columns.GetColumnsWidth(
                    DataGridViewElementStates.Visible) -
                this.dataGridView1.HorizontalScrollingOffset + 1,
                e.RowBounds.Height);

            // Paint the custom selection background.
            using (Brush backbrush =
                new System.Drawing.Drawing2D.LinearGradientBrush(rowBounds,
                    this.dataGridView1.DefaultCellStyle.SelectionBackColor,
                    e.InheritedRowStyle.ForeColor,
                    System.Drawing.Drawing2D.LinearGradientMode.Horizontal))
            {
                e.Graphics.FillRectangle(backbrush, rowBounds);
            }
        }
    }

    // Paints the content that spans multiple columns and the focus rectangle.
    void dataGridView1_RowPostPaint(object sender,
        DataGridViewRowPostPaintEventArgs e)
    {
        // Calculate the bounds of the row.
        Rectangle rowBounds = new Rectangle(
            this.dataGridView1.RowHeadersWidth, e.RowBounds.Top,
            this.dataGridView1.Columns.GetColumnsWidth(
                DataGridViewElementStates.Visible) -
            this.dataGridView1.HorizontalScrollingOffset + 1,
            e.RowBounds.Height);

        SolidBrush forebrush = null;
        try
        {
            // Determine the foreground color.
            if ((e.State & DataGridViewElementStates.Selected) ==
                DataGridViewElementStates.Selected)
            {
                forebrush = new SolidBrush(e.InheritedRowStyle.SelectionForeColor);
            }
            else
            {
                forebrush = new SolidBrush(e.InheritedRowStyle.ForeColor);
            }

            // Get the content that spans multiple columns.
            object recipe =
                this.dataGridView1.Rows.SharedRow(e.RowIndex).Cells[2].Value;

            if (recipe != null)
            {
                String text = recipe.ToString();

                // Calculate the bounds for the content that spans multiple
                // columns, adjusting for the horizontal scrolling position
                // and the current row height, and displaying only whole
                // lines of text.
                Rectangle textArea = rowBounds;
                textArea.X -= this.dataGridView1.HorizontalScrollingOffset;
                textArea.Width += this.dataGridView1.HorizontalScrollingOffset;
                textArea.Y += rowBounds.Height - e.InheritedRowStyle.Padding.Bottom;
                textArea.Height -= rowBounds.Height -
                    e.InheritedRowStyle.Padding.Bottom;
                textArea.Height = (textArea.Height / e.InheritedRowStyle.Font.Height) *
                    e.InheritedRowStyle.Font.Height;

                // Calculate the portion of the text area that needs painting.
                RectangleF clip = textArea;
                clip.Width -= this.dataGridView1.RowHeadersWidth + 1 - clip.X;
                clip.X = this.dataGridView1.RowHeadersWidth + 1;
                RectangleF oldClip = e.Graphics.ClipBounds;
                e.Graphics.SetClip(clip);

                // Draw the content that spans multiple columns.
                e.Graphics.DrawString(
                    text, e.InheritedRowStyle.Font, forebrush, textArea);

                e.Graphics.SetClip(oldClip);
            }
        }
        finally
        {
            forebrush.Dispose();
        }

        if (this.dataGridView1.CurrentCellAddress.Y == e.RowIndex)
        {
            // Paint the focus rectangle.
            e.DrawFocus(rowBounds, true);
        }
    }

    // Adjusts the padding when the user changes the row height so that
    // the normal cell content is fully displayed and any extra
    // height is used for the content that spans multiple columns.
    void dataGridView1_RowHeightChanged(object sender,
        DataGridViewRowEventArgs e)
    {
        // Calculate the new height of the normal cell content.
        Int32 preferredNormalContentHeight =
            e.Row.GetPreferredHeight(e.Row.Index,
            DataGridViewAutoSizeRowMode.AllCellsExceptHeader, true) -
            e.Row.DefaultCellStyle.Padding.Bottom;

        // Specify a new padding.
        Padding newPadding = e.Row.DefaultCellStyle.Padding;
        newPadding.Bottom = e.Row.Height - preferredNormalContentHeight;
        e.Row.DefaultCellStyle.Padding = newPadding;
    }
}

Compiling the Code

This example requires:

  • References to the System, System.Drawing, and System.Windows.Forms assemblies.

See also


Additional resources