65.9K
CodeProject 正在变化。 阅读更多。
Home

如何为 DataGridView 创建 ProgressBar 列

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (108投票s)

2010年10月10日

CPOL

4分钟阅读

viewsIcon

308982

downloadIcon

19423

一篇关于如何在 DataGridView 中创建 ProgressBar 列的文章。

ProgressBarColumn.jpg

引言

几天前,我在做一个允许用户在服务器上上传多个文件的程序。我意识到为每个文件单独显示上传进度会很好。我尝试找到解决这个问题的方法。经过几个小时的“谷歌搜索”,我找到了一些如何在 DataGridView 对象中显示进度条的解决方案,所以我决定创建我自己的自定义进度条列。我发现的文章为我提供了一些指导,并且我学习了一些关于如何自定义 DataGridView 列的想法。我实现了一些功能,我将在后面描述。

背景

DataGridView 控件提供了几种列类型,允许用户以多种方式输入和编辑值。有时这些列类型不能满足需求,但这没关系,因为您可以创建自己的列类型来容纳您需要的控件。当您想创建自己的列类型时,您必须定义一些派生自 DataGridViewColumnDataGridViewCell 的类。

在这个简短的示例中,我将向您展示如何创建一个进度条列。此示例不会直接将进度条控件插入到单元格中,它只会绘制一个看起来像进度条的矩形。

为此,您必须定义派生自 DataGridViewColumnDataGridViewImageCell 的类。(您也可以直接从 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 的新属性,它设置呈现的进度条的颜色。必须在两个类(DataGridViewProgressBarCellDataGridViewProgressBarColumn)中实现此属性。在 DataGridViewProgressBarColumn 类中,此方法设置 CellTemplateProgressBarColor 属性(必须将其强制转换为 DataGridViewProgressCell)。CellTemplateProgressBarColor 值接下来被传递到 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。当我们得到这个,我们可以很容易地设置 CellTemplateProgressBarColor 属性。

Using the Code

我创建了一个名为 ProgressBarColumn 的简单项目。在这个项目中,我包含一个名为 frmMain 的窗体,其中包含一个具有 2 列的 DataGridView。第一列是 DataGridViewTextBoxColumn 类型,第二列是 DataGridViewProgressColumn 类型。在这个项目中,我向您展示如何将数据加载到 DataGridView 中,以及此网格如何呈现 DataGridViewProgressColumn

历史

  • 2010 年 10 月 10 日 - 发布原始版本
© . All rights reserved.