Condividi tramite


Procedura: personalizzare l'aspetto delle righe nel controllo DataGridView di Windows Form

È possibile controllare l'aspetto delle righe DataGridView mediante la gestione di uno degli eventi o entrambi gli eventi DataGridView.RowPrePaint e DataGridView.RowPostPaint. Questi eventi sono stati progettati in modo che l'utente possa disegnare solo ciò che desidera e lasciare che il controllo DataGridView disegni il resto. Ad esempio, se si desidera disegnare uno sfondo personalizzato, è possibile gestire l'evento DataGridView.RowPrePaint e lasciare che le singole celle disegnino il contenuto in primo piano. In alternativa è possibile lasciare che le celle si disegnino da sole e aggiungere il contenuto in primo piano in un gestore per l'evento DataGridView.RowPostPaint. È inoltre possibile disabilitare il disegno delle celle e disegnare tutto personalmente in un gestore dell'evento DataGridView.RowPrePaint.

Nell'esempio di codice seguente i gestori vengono implementati per entrambi gli eventi per fornire uno sfondo con sfumature selezionabili e un contenuto in primo piano personalizzato che occupa più colonne.

Esempio

Imports System
Imports System.Drawing
Imports System.Windows.Forms

Class DataGridViewRowPainting
    Inherits Form
    Private WithEvents dataGridView1 As New DataGridView()
    Private oldRowIndex As Int32 = 0
    Private Const CUSTOM_CONTENT_HEIGHT As Int32 = 30

    <STAThreadAttribute()> _
    Public Shared Sub Main()

        Application.Run(New DataGridViewRowPainting())

    End Sub 'Main

    Public Sub New()

        Me.dataGridView1.Dock = DockStyle.Fill
        Me.Controls.Add(Me.dataGridView1)
        Me.Text = "DataGridView row painting demo"

    End Sub 'New

    Sub DataGridViewRowPainting_Load(ByVal sender As Object, _
        ByVal e As EventArgs) Handles Me.Load

        ' Set a cell padding to provide space for the top of the focus 
        ' rectangle and for the content that spans multiple columns. 
        Dim newPadding As New Padding(0, 1, 0, CUSTOM_CONTENT_HEIGHT)
        Me.dataGridView1.RowTemplate.DefaultCellStyle.Padding = newPadding

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

        ' Set the row height to accommodate the normal cell content and the 
        ' content that spans multiple columns.
        Me.dataGridView1.RowTemplate.Height += CUSTOM_CONTENT_HEIGHT

        ' Initialize other DataGridView properties.
        Me.dataGridView1.AllowUserToAddRows = False
        Me.dataGridView1.EditMode = DataGridViewEditMode.EditOnKeystrokeOrF2
        Me.dataGridView1.CellBorderStyle = DataGridViewCellBorderStyle.None
        Me.dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect

        ' Set the column header names.
        Me.dataGridView1.ColumnCount = 4
        Me.dataGridView1.Columns(0).Name = "Recipe"
        Me.dataGridView1.Columns(0).SortMode = _
            DataGridViewColumnSortMode.NotSortable
        Me.dataGridView1.Columns(1).Name = "Category"
        Me.dataGridView1.Columns(2).Name = "Main Ingredients"
        Me.dataGridView1.Columns(3).Name = "Rating"

        ' Hide the column that contains the content that spans 
        ' multiple columns.
        Me.dataGridView1.Columns(2).Visible = False

        ' Populate the rows of the DataGridView.
        Dim row1() As 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", "****"}
        Dim row2() As String = {"Key Lime Pie", "Dessert", _
            "lime juice, whipped cream, eggs, evaporated milk", "****"}
        Dim row3() As String = {"Orange-Salsa Pork Chops", "Main Dish", _
            "pork chops, salsa, orange juice, pineapple", "****"}
        Dim row4() As String = {"Black Bean and Rice Salad", "Salad", _
            "black beans, brown rice", "****"}
        Dim row5() As String = {"Chocolate Cheesecake", "Dessert", _
            "cream cheese, unsweetened chocolate", "***"}
        Dim row6() As String = {"Black Bean Dip", "Appetizer", _
            "black beans, sour cream, salsa, chips", "***"}
        Dim rows() As Object = {row1, row2, row3, row4, row5, row6}
        Dim rowArray As String()
        For Each rowArray In rows
            Me.dataGridView1.Rows.Add(rowArray)
        Next rowArray

        ' Adjust the row heights to accommodate the normal cell content.
        Me.dataGridView1.AutoResizeRows( _
            DataGridViewAutoSizeRowsMode.AllCellsExceptHeaders)
    End Sub 'DataGridViewRowPainting_Load

    ' Forces the control to repaint itself when the user 
    ' manually changes the width of a column.
    Sub dataGridView1_ColumnWidthChanged(ByVal sender As Object, _
        ByVal e As DataGridViewColumnEventArgs) _
        Handles dataGridView1.ColumnWidthChanged

        Me.dataGridView1.Invalidate()

    End Sub 'dataGridView1_ColumnWidthChanged

    ' Forces the row to repaint itself when the user changes the 
    ' current cell. This is necessary to refresh the focus rectangle.
    Sub dataGridView1_CurrentCellChanged(ByVal sender As Object, _
        ByVal e As EventArgs) Handles dataGridView1.CurrentCellChanged

        If oldRowIndex <> -1 Then
            Me.dataGridView1.InvalidateRow(oldRowIndex)
        End If
        oldRowIndex = Me.dataGridView1.CurrentCellAddress.Y

    End Sub 'dataGridView1_CurrentCellChanged

    ' Paints the custom selection background for selected rows.
    Sub dataGridView1_RowPrePaint(ByVal sender As Object, _
        ByVal e As DataGridViewRowPrePaintEventArgs) _
        Handles dataGridView1.RowPrePaint

        ' Do not automatically paint the focus rectangle.
        e.PaintParts = e.PaintParts And Not DataGridViewPaintParts.Focus

        ' Determine whether the cell should be painted with the 
        ' custom selection background.
        If (e.State And DataGridViewElementStates.Selected) = _
            DataGridViewElementStates.Selected Then

            ' Calculate the bounds of the row.
            Dim rowBounds As New Rectangle( _
                Me.dataGridView1.RowHeadersWidth, e.RowBounds.Top, _
                Me.dataGridView1.Columns.GetColumnsWidth( _
                DataGridViewElementStates.Visible) - _
                Me.dataGridView1.HorizontalScrollingOffset + 1, _
                e.RowBounds.Height)

            ' Paint the custom selection background.
            Dim backbrush As New _
                System.Drawing.Drawing2D.LinearGradientBrush(rowBounds, _
                Me.dataGridView1.DefaultCellStyle.SelectionBackColor, _
                e.InheritedRowStyle.ForeColor, _
                System.Drawing.Drawing2D.LinearGradientMode.Horizontal)
            Try
                e.Graphics.FillRectangle(backbrush, rowBounds)
            Finally
                backbrush.Dispose()
            End Try
        End If

    End Sub 'dataGridView1_RowPrePaint

    ' Paints the content that spans multiple columns and the focus rectangle.
    Sub dataGridView1_RowPostPaint(ByVal sender As Object, _
        ByVal e As DataGridViewRowPostPaintEventArgs) _
        Handles dataGridView1.RowPostPaint

        ' Calculate the bounds of the row.
        Dim rowBounds As New Rectangle(Me.dataGridView1.RowHeadersWidth, _
            e.RowBounds.Top, Me.dataGridView1.Columns.GetColumnsWidth( _
            DataGridViewElementStates.Visible) - _
            Me.dataGridView1.HorizontalScrollingOffset + 1, e.RowBounds.Height)

        Dim forebrush As SolidBrush = Nothing
        Try
            ' Determine the foreground color.
            If (e.State And DataGridViewElementStates.Selected) = _
                DataGridViewElementStates.Selected Then

                forebrush = New SolidBrush(e.InheritedRowStyle.SelectionForeColor)
            Else
                forebrush = New SolidBrush(e.InheritedRowStyle.ForeColor)
            End If

            ' Get the content that spans multiple columns.
            Dim recipe As Object = _
                Me.dataGridView1.Rows.SharedRow(e.RowIndex).Cells(2).Value

            If (recipe IsNot Nothing) Then
                Dim text As String = 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.
                Dim textArea As Rectangle = rowBounds
                textArea.X -= Me.dataGridView1.HorizontalScrollingOffset
                textArea.Width += Me.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.
                Dim clip As RectangleF = textArea
                clip.Width -= Me.dataGridView1.RowHeadersWidth + 1 - clip.X
                clip.X = Me.dataGridView1.RowHeadersWidth + 1
                Dim oldClip As RectangleF = 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)
            End If
        Finally
            forebrush.Dispose()
        End Try

        If Me.dataGridView1.CurrentCellAddress.Y = e.RowIndex Then
            ' Paint the focus rectangle.
            e.DrawFocus(rowBounds, True)
        End If

    End Sub 'dataGridView1_RowPostPaint

    ' 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.
    Sub dataGridView1_RowHeightChanged(ByVal sender As Object, _
        ByVal e As DataGridViewRowEventArgs) _
        Handles dataGridView1.RowHeightChanged

        ' Calculate the new height of the normal cell content.
        Dim preferredNormalContentHeight As Int32 = _
            e.Row.GetPreferredHeight(e.Row.Index, _
            DataGridViewAutoSizeRowMode.AllCellsExceptHeader, True) - _
            e.Row.DefaultCellStyle.Padding.Bottom()

        ' Specify a new padding.
        Dim newPadding As Padding = e.Row.DefaultCellStyle.Padding
        newPadding.Bottom = e.Row.Height - preferredNormalContentHeight
        e.Row.DefaultCellStyle.Padding = newPadding

    End Sub

End Class 'DataGridViewRowPainting
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;
    }
}

Compilazione del codice

L'esempio presenta i seguenti requisiti:

  • Riferimenti agli assembly System, System.Drawing e System.Windows.Forms.

Per informazioni sulla compilazione di questo esempio dalla riga di comando per Visual Basic o Visual C#, vedere Building from the Command Line (Visual Basic) o Compilazione dalla riga di comando con csc.exe. È anche possibile compilare questo esempio in Visual Studio incollando il codice in un nuovo progetto. Per ulteriori informazioni, vedere Procedura: compilare ed eseguire un esempio di codice Windows Form completo tramite Visual Studio e Procedura: compilare ed eseguire un esempio di codice Windows Form completo tramite Visual Studio e Procedura: compilare ed eseguire un esempio di codice Windows Form completo tramite Visual Studio e Procedura: compilare ed eseguire un esempio di codice Windows Form completo tramite Visual Studio e Procedura: compilare ed eseguire un esempio di codice Windows Form completo tramite Visual Studio.

Vedere anche

Riferimenti

DataGridView

DataGridView.RowPrePaint

DataGridView.RowPostPaint

Concetti

Architettura del controllo DataGridView (Windows Form)

Altre risorse

Personalizzazione del controllo DataGridView Windows Form