如何为 DataGridView 创建 ProgressBar 列






4.95/5 (108投票s)
一篇关于如何在 DataGridView 中创建 ProgressBar 列的文章。

引言
几天前,我在做一个允许用户在服务器上上传多个文件的程序。我意识到为每个文件单独显示上传进度会很好。我尝试找到解决这个问题的方法。经过几个小时的“谷歌搜索”,我找到了一些如何在 DataGridView
对象中显示进度条的解决方案,所以我决定创建我自己的自定义进度条列。我发现的文章为我提供了一些指导,并且我学习了一些关于如何自定义 DataGridView
列的想法。我实现了一些功能,我将在后面描述。
背景
DataGridView
控件提供了几种列类型,允许用户以多种方式输入和编辑值。有时这些列类型不能满足需求,但这没关系,因为您可以创建自己的列类型来容纳您需要的控件。当您想创建自己的列类型时,您必须定义一些派生自 DataGridViewColumn
和 DataGridViewCell
的类。
在这个简短的示例中,我将向您展示如何创建一个进度条列。此示例不会直接将进度条控件插入到单元格中,它只会绘制一个看起来像进度条的矩形。
为此,您必须定义派生自 DataGridViewColumn
和 DataGridViewImageCell
的类。(您也可以直接从 DataGridViewCell
派生,它是所有单元格的基本类型,但在这种情况下,从 DataGridViewImageCell
派生更合适,因为我们将使用一些图形操作。)
主要步骤
正如我之前提到的,必须创建两个类。我创建的第一个类是 DataGridViewProgressColumn
,它派生自 DataGridViewImageColumn
。此类创建一个自定义列来托管进度条单元格,并重写属性 CellTemplate
。
public class DataGridViewProgressColumn : DataGridViewImageColumn
{
. . .
public override DataGridViewCell CellTemplate
{
get
{
return base.CellTemplate;
}
set
{
if (value != null &&
!value.GetType().IsAssignableFrom(typeof(DataGridViewProgressCell)))
{
throw new InvalidCastException("Must be a DataGridViewProgressCell");
}
base.CellTemplate = value;
}
}
. . .
}
我创建的第二个类是 DataGridViewProgressBarCell
。此类创建一个自定义单元格来托管进度条。此类派生自 DataGridViewImageCell
。
在开发自定义单元格类型时,检查是否需要重写某些虚方法也很重要。在此示例中,需要重写两个方法:Clone()
方法和 Paint()
方法。
Paint() 方法
为了以我们想要的方式绘制单元格的内容,我们必须重载 paint
方法。对于任何单元格类型来说,此方法都是至关重要的。它负责绘制单元格。它设置各种属性,并负责调用 Graphics.FillRectangle(…)
和 Graphics.DrawString(…)
方法,这些方法绘制进度条并写入文本。
protected override void Paint(System.Drawing.Graphics g,
System.Drawing.Rectangle clipBounds,
System.Drawing.Rectangle cellBounds,
int rowIndex,
DataGridViewElementStates cellState,
object value, object formattedValue,
string errorText,
DataGridViewCellStyle cellStyle,
DataGridViewAdvancedBorderStyle advancedBorderStyle,
DataGridViewPaintParts paintParts)
{
if (Convert.ToInt16(value) == 0 || value == null)
{
value = 0;
}
int progressVal = Convert.ToInt32(value);
// Need to convert to float before division;
//otherwise C# returns int which is 0 for anything but 100%.
float percentage = ((float)progressVal / 100.0f);
Brush backColorBrush = new SolidBrush(cellStyle.BackColor);
Brush foreColorBrush = new SolidBrush(cellStyle.ForeColor);
// Draws the cell grid
base.Paint(g, clipBounds, cellBounds, rowIndex,
cellState, value, formattedValue, errorText,
cellStyle, advancedBorderStyle,
(paintParts & ~DataGridViewPaintParts.ContentForeground));
float posX = cellBounds.X;
float posY = cellBounds.Y;
float textWidth = TextRenderer.MeasureText
(progressVal.ToString() + "%", cellStyle.Font).Width;
float textHeight = TextRenderer.MeasureText
(progressVal.ToString() + "%", cellStyle.Font).Height;
//evaluating text position according to selected alignment
//default text alignment is TopLeft
//variables posX and posY determine position of progress value text (percentage+"%")
switch (cellStyle.Alignment)
{
case DataGridViewContentAlignment.BottomCenter:
posX = cellBounds.X + (cellBounds.Width / 2) - textWidth / 2;
posY = cellBounds.Y + cellBounds.Height - textHeight;
break;
case DataGridViewContentAlignment.BottomLeft:
posX = cellBounds.X;
posY = cellBounds.Y + cellBounds.Height - textHeight;
break;
case DataGridViewContentAlignment.BottomRight:
posX = cellBounds.X + cellBounds.Width - textWidth;
posY = cellBounds.Y + cellBounds.Height - textHeight;
break;
case DataGridViewContentAlignment.MiddleCenter:
posX = cellBounds.X + (cellBounds.Width / 2) - textWidth / 2;
posY = cellBounds.Y + (cellBounds.Height / 2) - textHeight / 2;
break;
case DataGridViewContentAlignment.MiddleLeft:
posX = cellBounds.X;
posY = cellBounds.Y + (cellBounds.Height / 2) - textHeight / 2;
break;
case DataGridViewContentAlignment.MiddleRight:
posX = cellBounds.X + cellBounds.Width - textWidth;
posY = cellBounds.Y + (cellBounds.Height / 2) - textHeight / 2;
break;
case DataGridViewContentAlignment.TopCenter:
posX = cellBounds.X + (cellBounds.Width / 2) - textWidth / 2;
posY = cellBounds.Y;
break;
case DataGridViewContentAlignment.TopLeft:
posX = cellBounds.X;
posY = cellBounds.Y;
break;
case DataGridViewContentAlignment.TopRight:
posX = cellBounds.X + cellBounds.Width - textWidth;
posY = cellBounds.Y;
break;
}
if (percentage >= 0.0)
{
// Draw the progress
g.FillRectangle(new SolidBrush(_ProgressBarColor), cellBounds.X + 2,
cellBounds.Y + 2, Convert.ToInt32((percentage * cellBounds.Width * 0.8)),
cellBounds.Height / 1 - 5);
//Draw text
g.DrawString(progressVal.ToString() + "%",
cellStyle.Font, foreColorBrush, posX, posY);
}
else
{
//if percentage is negative, we don't want to draw progress bar
//we want only text
if (this.DataGridView.CurrentRow.Index == rowIndex)
{
g.DrawString(progressVal.ToString() + "%", cellStyle.Font,
new SolidBrush(cellStyle.SelectionForeColor), posX, posX);
}
else
{
g.DrawString(progressVal.ToString() + "%", cellStyle.Font,
foreColorBrush,posX, posY);
}
}
}
此方法还负责根据用户选择的对齐方式正确呈现文本。可以在 CellStyle Builder 窗口中选择此对齐方式。
Clone() 方法
DataGridViewImageCell
派生自实现 ICloneable
接口的 DataGridViewCell
类。每个自定义单元格类型通常都需要重写 Clone()
方法来复制其自定义属性。单元格是可克隆的,因为特定的单元格实例可以用于网格中的多行。当单元格属于共享行时就是这种情况。当行被取消共享时,需要克隆它的单元格。以下是 Clone()
方法的实现
public override object Clone()
{
DataGridViewProgressCell dataGridViewCell = base.Clone() as DataGridViewProgressCell;
if (dataGridViewCell != null)
{
dataGridViewCell.ProgressBarColor = this.ProgressBarColor;
}
return dataGridViewCell;
}
ProgressBarColor 属性
我创建了一个名为 ProgressBarColor
的新属性,它设置呈现的进度条的颜色。必须在两个类(DataGridViewProgressBarCell
和 DataGridViewProgressBarColumn
)中实现此属性。在 DataGridViewProgressBarColumn
类中,此方法设置 CellTemplate
的 ProgressBarColor
属性(必须将其强制转换为 DataGridViewProgressCell
)。CellTemplate
的 ProgressBarColor
值接下来被传递到 DataGridViewProgressBarCell
类的 _progressBarColor
变量中。_progressBarColor
变量在 Paint(…)
方法中使用。以下是 ProgressBarColor
属性的实现
[Browsable(true)]
public Color ProgressBarColor
{
get {
if (this.ProgressBarCellTemplate == null)
{
throw new InvalidOperationException
("Operation cannot be completed because this DataGridViewColumn
does not have a CellTemplate.");
}
return this.ProgressBarCellTemplate.ProgressBarColor;
}
set {
if (this.ProgressBarCellTemplate == null)
{
throw new InvalidOperationException
("Operation cannot be completed because this DataGridViewColumn
does not have a CellTemplate.");
}
this.ProgressBarCellTemplate.ProgressBarColor = value;
if (this.DataGridView != null)
{
DataGridViewRowCollection dataGridViewRows = this.DataGridView.Rows;
int rowCount = dataGridViewRows.Count;
for (int rowIndex = 0; rowIndex < rowCount; rowIndex++)
{
DataGridViewRow dataGridViewRow = dataGridViewRows.SharedRow(rowIndex);
DataGridViewProgressCell dataGridViewCell =
dataGridViewRow.Cells[this.Index] as DataGridViewProgressCell;
if (dataGridViewCell != null)
{
dataGridViewCell.SetProgressBarColor(rowIndex, value);
}
}
this.DataGridView.InvalidateColumn(this.Index);
}
}
}
正如我之前提到的,当我们想将 ProgressBarColor
值传递到 CellTemplate
时,我们必须首先获取此 CellTemplate
。我们可以从 DataGridProgressColumn
类的 ProgressBarCellTemplate
属性中获取 CellTemplate
。当我们得到这个,我们可以很容易地设置 CellTemplate
的 ProgressBarColor
属性。
Using the Code
我创建了一个名为 ProgressBarColumn
的简单项目。在这个项目中,我包含一个名为 frmMain
的窗体,其中包含一个具有 2 列的 DataGridView
。第一列是 DataGridViewTextBoxColumn
类型,第二列是 DataGridViewProgressColumn
类型。在这个项目中,我向您展示如何将数据加载到 DataGridView
中,以及此网格如何呈现 DataGridViewProgressColumn
。
历史
- 2010 年 10 月 10 日 - 发布原始版本