如何:自定义 Windows 窗体 DataGridView 控件中行的外观
更新:2007 年 11 月
您可以通过处理 DataGridView.RowPrePaint 和 DataGridView.RowPostPaint 事件或二者之一控制 DataGridView 中行的外观。这些事件旨在让您只需绘制想要绘制的内容,而让 DataGridView 控件绘制其他内容。例如,如果您想要绘制自定义背景,则可处理 DataGridView.RowPrePaint 事件并让各个单元格自行绘制前景内容。或者,您也可以让单元格自行进行绘制,并在 DataGridView.RowPostPaint 事件的处理程序中添加自定义前景内容。您还可以禁用单元格绘制并在 DataGridView.RowPrePaint 事件处理程序中自行绘制任何内容。
下面的代码示例实现了上述两个事件的处理程序,以便为选择的内容提供一个渐变的背景并提供跨越多列的自定义前景内容。
示例
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;
}
}
编译代码
此示例要求:
- 对 System、System.Drawing 和 System.Windows.Forms 程序集的引用。
有关从 Visual Basic 或 Visual C# 的命令行生成此示例的信息,请参见从命令行生成 (Visual Basic) 或在命令行上使用 csc.exe 生成。也可以通过将代码粘贴到新项目,在 Visual Studio 中生成此示例。 如何:使用 Visual Studio 编译和运行完整的 Windows 窗体代码示例
如何:使用 Visual Studio 编译和运行完整的 Windows 窗体代码示例
如何:使用 Visual Studio 编译和运行完整的 Windows 窗体代码示例
如何:使用 Visual Studio 编译和运行完整的 Windows 窗体代码示例
如何:使用 Visual Studio 编译和运行完整的 Windows 窗体代码示例