注意:DataGridViewコントロールは、.NET Framework 2.0で新しく追加されました。
ここでは、DataGridViewのセルにプログレスバーを表示する方法を例に、カスタムセルクラス(DataGridViewCellクラスの派生クラス)と、そのセルのコレクションをホストするカスタム列クラス(DataGridViewColumnクラスの派生クラス)を作成する方法を紹介します。
補足:ここで紹介するのは、独自にセルを描画することにより、.NET Frameworkに組み込まれているセルクラスとは違った外観となるカスタムセルクラスを作成する方法であり、セルの編集に使用するコントロールを独自に用意する方法ではありません。ここで作成するカスタムセルクラスはDataGridViewTextBoxCellクラスを継承したものであり、見た目はプログレスバーですが、セルの値の編集は従来どおりテキストボックスで行います。独自のコントロールをセルの編集に使用するカスタムセルクラスを作成する方法は、こちらで紹介します。
カスタムセルクラスはDataGridViewCellクラスを継承して作成しますが、ここではDataGridViewTextBoxCellクラスを継承してカスタムセルクラスを作成します。このようにすることにより、セルの編集等の処理を自分で実装する手間が省けます。必要な実装は、プログレスバーをセルに描画するために、DataGridViewTextBoxCellクラスのPaintメソッドをオーバーライドする程度に省略できます。
カスタム列クラスは、DataGridViewColumnクラスを継承して作成します。ここではDataGridViewTextBoxColumnクラスを継承していますが、DataGridViewColumnクラスを直接継承しても手間はあまり変わりません(DataGridViewColumnクラスを直接継承した例は、こちらにあります)。
補足:カスタム列クラスは必ず作成しなければならないということはありません。例えばDataGridViewTextBoxCellクラスから派生したカスタムセルならば、DataGridViewTextBoxColumn列に表示することもできます。このようにする場合は、DataGridViewTextBoxColumnオブジェクトのCellTemplateプロパティにカスタムセルクラスのインスタンスを設定します。
以下にDataGridViewにProgressBarを表示するためのカスタムセルクラス(DataGridViewProgressBarCellクラス)とカスタム列クラス(DataGridViewProgressBarColumnクラス)の例を示します。カスタムセルクラスを作成する例として書いたため、プログレスバーを表示するだけなら無駄と思われる部分もあえて入れています。
Imports System Imports System.Drawing Imports System.Windows.Forms ''' <summary> ''' DataGridViewProgressBarCellオブジェクトの列 ''' </summary> Public Class DataGridViewProgressBarColumn Inherits DataGridViewTextBoxColumn 'コンストラクタ Public Sub New() Me.CellTemplate = New DataGridViewProgressBarCell() End Sub 'CellTemplateの取得と設定 Public Overrides Property CellTemplate() As DataGridViewCell Get Return MyBase.CellTemplate End Get Set(ByVal value As DataGridViewCell) 'DataGridViewProgressBarCell以外はホストしない If Not TypeOf value Is DataGridViewProgressBarCell Then Throw New InvalidCastException( _ "DataGridViewProgressBarCellオブジェクトを" + _ "指定してください。") End If MyBase.CellTemplate = value End Set End Property ''' <summary> ''' ProgressBarの最大値 ''' </summary> Public Property Maximum() As Integer Get Return CType(Me.CellTemplate, DataGridViewProgressBarCell).Maximum End Get Set(ByVal value As Integer) If Me.Maximum = value Then Return End If 'セルテンプレートの値を変更する CType(Me.CellTemplate, DataGridViewProgressBarCell).Maximum = value 'DataGridViewにすでに追加されているセルの値を変更する If Me.DataGridView Is Nothing Then Return End If Dim rowCount As Integer = Me.DataGridView.RowCount Dim i As Integer For i = 0 To rowCount - 1 Dim r As DataGridViewRow = Me.DataGridView.Rows.SharedRow(i) CType(r.Cells(Me.Index), DataGridViewProgressBarCell).Maximum = _ value Next i End Set End Property ''' <summary> ''' ProgressBarの最小値 ''' </summary> Public Property Mimimum() As Integer Get Return CType(Me.CellTemplate, DataGridViewProgressBarCell).Mimimum End Get Set(ByVal value As Integer) If Me.Mimimum = value Then Return End If 'セルテンプレートの値を変更する CType(Me.CellTemplate, DataGridViewProgressBarCell).Mimimum = value 'DataGridViewにすでに追加されているセルの値を変更する If Me.DataGridView Is Nothing Then Return End If Dim rowCount As Integer = Me.DataGridView.RowCount Dim i As Integer For i = 0 To rowCount - 1 Dim r As DataGridViewRow = Me.DataGridView.Rows.SharedRow(i) CType(r.Cells(Me.Index), DataGridViewProgressBarCell).Mimimum = _ value Next i End Set End Property End Class ''' <summary> ''' ProgressBarをDataGridViewに表示する ''' </summary> Public Class DataGridViewProgressBarCell Inherits DataGridViewTextBoxCell 'コンストラクタ Public Sub New() Me.maximumValue = 100 Me.mimimumValue = 0 End Sub Private maximumValue As Integer Public Property Maximum() As Integer Get Return Me.maximumValue End Get Set(ByVal value As Integer) Me.maximumValue = value End Set End Property Private mimimumValue As Integer Public Property Mimimum() As Integer Get Return Me.mimimumValue End Get Set(ByVal value As Integer) Me.mimimumValue = value End Set End Property 'セルの値のデータ型を指定する 'ここでは、整数型とする Public Overrides ReadOnly Property ValueType() As Type Get Return GetType(Integer) End Get End Property '新しいレコード行のセルの既定値を指定する Public Overrides ReadOnly Property DefaultNewRowValue() As Object Get Return 0 End Get End Property '新しいプロパティを追加しているため、 ' Cloneメソッドをオーバーライドする必要がある Public Overrides Function Clone() As Object Dim cell As DataGridViewProgressBarCell = _ CType(MyBase.Clone(), DataGridViewProgressBarCell) cell.Maximum = Me.Maximum cell.Mimimum = Me.Mimimum Return cell End Function Protected Overrides Sub Paint(ByVal graphics As Graphics, _ ByVal clipBounds As Rectangle, _ ByVal cellBounds As Rectangle, _ ByVal rowIndex As Integer, _ ByVal cellState As DataGridViewElementStates, _ ByVal value As Object, _ ByVal formattedValue As Object, _ ByVal errorText As String, _ ByVal cellStyle As DataGridViewCellStyle, _ ByVal advancedBorderStyle As DataGridViewAdvancedBorderStyle, _ ByVal paintParts As DataGridViewPaintParts) '値を決定する Dim intValue As Integer = 0 If TypeOf value Is Integer Then intValue = CInt(value) End If If intValue < Me.mimimumValue Then intValue = Me.mimimumValue End If If intValue > Me.maximumValue Then intValue = Me.maximumValue End If '割合を計算する Dim rate As Double = CDbl(intValue - Me.mimimumValue) / _ (Me.maximumValue - Me.mimimumValue) 'セルの境界線(枠)を描画する If (paintParts And DataGridViewPaintParts.Border) = _ DataGridViewPaintParts.Border Then Me.PaintBorder(graphics, clipBounds, cellBounds, _ cellStyle, advancedBorderStyle) End If '境界線の内側に範囲を取得する Dim borderRect As Rectangle = Me.BorderWidths(advancedBorderStyle) Dim paintRect As New Rectangle(cellBounds.Left + borderRect.Left, _ cellBounds.Top + borderRect.Top, _ cellBounds.Width - borderRect.Right, _ cellBounds.Height - borderRect.Bottom) '背景色を決定する '選択されている時とされていない時で色を変える Dim isSelected As Boolean = _ ((cellState And DataGridViewElementStates.Selected) = _ DataGridViewElementStates.Selected) Dim bkColor As Color If isSelected AndAlso _ (paintParts And DataGridViewPaintParts.SelectionBackground) = _ DataGridViewPaintParts.SelectionBackground Then bkColor = cellStyle.SelectionBackColor Else bkColor = cellStyle.BackColor End If '背景を描画する If (paintParts And DataGridViewPaintParts.Background) = _ DataGridViewPaintParts.Background Then Dim backBrush As New SolidBrush(bkColor) Try graphics.FillRectangle(backBrush, paintRect) Finally backBrush.Dispose() End Try End If 'Paddingを差し引く paintRect.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top) paintRect.Width -= cellStyle.Padding.Horizontal paintRect.Height -= cellStyle.Padding.Vertical 'ProgressBarを描画する If (paintParts And DataGridViewPaintParts.ContentForeground) = _ DataGridViewPaintParts.ContentForeground Then If ProgressBarRenderer.IsSupported Then 'visualスタイルで描画する 'ProgressBarの枠を描画する ProgressBarRenderer.DrawHorizontalBar(graphics, paintRect) 'ProgressBarのバーを描画する Dim barBounds As New Rectangle(paintRect.Left + 3, _ paintRect.Top + 3, _ paintRect.Width - 4, _ paintRect.Height - 6) barBounds.Width = CInt(Math.Round((barBounds.Width * rate))) ProgressBarRenderer.DrawHorizontalChunks(graphics, barBounds) Else 'visualスタイルで描画できない時 graphics.FillRectangle(Brushes.White, paintRect) graphics.DrawRectangle(Pens.Black, paintRect) Dim barBounds As New Rectangle(paintRect.Left + 1, _ paintRect.Top + 1, _ paintRect.Width - 1, _ paintRect.Height - 1) barBounds.Width = CInt(Math.Round((barBounds.Width * rate))) graphics.FillRectangle(Brushes.Blue, barBounds) End If End If 'フォーカスの枠を表示する If Me.DataGridView.CurrentCellAddress.X = Me.ColumnIndex AndAlso _ Me.DataGridView.CurrentCellAddress.Y = Me.RowIndex AndAlso _ (paintParts And DataGridViewPaintParts.Focus) = _ DataGridViewPaintParts.Focus AndAlso _ Me.DataGridView.Focused Then 'フォーカス枠の大きさを適当に決める Dim focusRect As Rectangle = paintRect focusRect.Inflate(-3, -3) ControlPaint.DrawFocusRectangle(graphics, focusRect) '背景色を指定してフォーカス枠を描画する時 'ControlPaint.DrawFocusRectangle( ' graphics, focusRect, Color.Empty, bkColor); End If 'テキストを表示する If (paintParts And DataGridViewPaintParts.ContentForeground) = _ DataGridViewPaintParts.ContentForeground Then '表示するテキストを決定 Dim txt As String = String.Format("{0}%", Math.Round((rate * 100))) 'string txt = formattedValue.ToString(); '本来は、cellStyleによりTextFormatFlagsを決定すべき Dim flags As TextFormatFlags = _ TextFormatFlags.HorizontalCenter Or _ TextFormatFlags.VerticalCenter '色を決定 Dim fColor As Color = cellStyle.ForeColor 'if (isSelected) ' fColor = cellStyle.SelectionForeColor; 'else ' fColor = cellStyle.ForeColor; 'テキストを描画する paintRect.Inflate(-2, -2) TextRenderer.DrawText( _ graphics, txt, cellStyle.Font, paintRect, fColor, flags) End If 'エラーアイコンの表示 If (paintParts And DataGridViewPaintParts.ErrorIcon) = _ DataGridViewPaintParts.ErrorIcon AndAlso _ Me.DataGridView.ShowCellErrors AndAlso _ Not String.IsNullOrEmpty(errorText) Then 'エラーアイコンを表示させる領域を取得 Dim iconBounds As Rectangle = _ Me.GetErrorIconBounds(graphics, cellStyle, rowIndex) iconBounds.Offset(cellBounds.X, cellBounds.Y) 'エラーアイコンを描画 Me.PaintErrorIcon(graphics, iconBounds, cellBounds, errorText) End If End Sub End Class
using System; using System.Drawing; using System.Windows.Forms; /// <summary> /// DataGridViewProgressBarCellオブジェクトの列 /// </summary> public class DataGridViewProgressBarColumn : DataGridViewTextBoxColumn { //コンストラクタ public DataGridViewProgressBarColumn() { this.CellTemplate = new DataGridViewProgressBarCell(); } //CellTemplateの取得と設定 public override DataGridViewCell CellTemplate { get { return base.CellTemplate; } set { //DataGridViewProgressBarCell以外はホストしない if (!(value is DataGridViewProgressBarCell)) { throw new InvalidCastException( "DataGridViewProgressBarCellオブジェクトを" + "指定してください。"); } base.CellTemplate = value; } } /// <summary> /// ProgressBarの最大値 /// </summary> public int Maximum { get { return ((DataGridViewProgressBarCell)this.CellTemplate).Maximum; } set { if (this.Maximum == value) return; //セルテンプレートの値を変更する ((DataGridViewProgressBarCell)this.CellTemplate).Maximum = value; //DataGridViewにすでに追加されているセルの値を変更する if (this.DataGridView == null) return; int rowCount = this.DataGridView.RowCount; for (int i = 0; i < rowCount; i++) { DataGridViewRow r = this.DataGridView.Rows.SharedRow(i); ((DataGridViewProgressBarCell)r.Cells[this.Index]).Maximum = value; } } } /// <summary> /// ProgressBarの最小値 /// </summary> public int Mimimum { get { return ((DataGridViewProgressBarCell)this.CellTemplate).Mimimum; } set { if (this.Mimimum == value) return; //セルテンプレートの値を変更する ((DataGridViewProgressBarCell)this.CellTemplate).Mimimum = value; //DataGridViewにすでに追加されているセルの値を変更する if (this.DataGridView == null) return; int rowCount = this.DataGridView.RowCount; for (int i = 0; i < rowCount; i++) { DataGridViewRow r = this.DataGridView.Rows.SharedRow(i); ((DataGridViewProgressBarCell)r.Cells[this.Index]).Mimimum = value; } } } } /// <summary> /// ProgressBarをDataGridViewに表示する /// </summary> public class DataGridViewProgressBarCell : DataGridViewTextBoxCell { //コンストラクタ public DataGridViewProgressBarCell() { this.maximumValue = 100; this.mimimumValue = 0; } private int maximumValue; public int Maximum { get { return this.maximumValue; } set { this.maximumValue = value; } } private int mimimumValue; public int Mimimum { get { return this.mimimumValue; } set { this.mimimumValue = value; } } //セルの値のデータ型を指定する //ここでは、整数型とする public override Type ValueType { get { return typeof(int); } } //新しいレコード行のセルの既定値を指定する public override object DefaultNewRowValue { get { return 0; } } //新しいプロパティを追加しているため、 // Cloneメソッドをオーバーライドする必要がある public override object Clone() { DataGridViewProgressBarCell cell = (DataGridViewProgressBarCell)base.Clone(); cell.Maximum = this.Maximum; cell.Mimimum = this.Mimimum; return cell; } protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts) { //値を決定する int intValue = 0; if (value is int) intValue = (int)value; if (intValue < this.mimimumValue) intValue = this.mimimumValue; if (intValue > this.maximumValue) intValue = this.maximumValue; //割合を計算する double rate = (double)(intValue - this.mimimumValue) / (this.maximumValue - this.mimimumValue); //セルの境界線(枠)を描画する if ((paintParts & DataGridViewPaintParts.Border) == DataGridViewPaintParts.Border) { this.PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle); } //境界線の内側に範囲を取得する Rectangle borderRect = this.BorderWidths(advancedBorderStyle); Rectangle paintRect = new Rectangle( cellBounds.Left + borderRect.Left, cellBounds.Top + borderRect.Top, cellBounds.Width - borderRect.Right, cellBounds.Height - borderRect.Bottom); //背景色を決定する //選択されている時とされていない時で色を変える bool isSelected = (cellState & DataGridViewElementStates.Selected) == DataGridViewElementStates.Selected; Color bkColor; if (isSelected && (paintParts & DataGridViewPaintParts.SelectionBackground) == DataGridViewPaintParts.SelectionBackground) { bkColor = cellStyle.SelectionBackColor; } else { bkColor = cellStyle.BackColor; } //背景を描画する if ((paintParts & DataGridViewPaintParts.Background) == DataGridViewPaintParts.Background) { using (SolidBrush backBrush = new SolidBrush(bkColor)) { graphics.FillRectangle(backBrush, paintRect); } } //Paddingを差し引く paintRect.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top); paintRect.Width -= cellStyle.Padding.Horizontal; paintRect.Height -= cellStyle.Padding.Vertical; //ProgressBarを描画する if ((paintParts & DataGridViewPaintParts.ContentForeground) == DataGridViewPaintParts.ContentForeground) { if (ProgressBarRenderer.IsSupported) { //visualスタイルで描画する //ProgressBarの枠を描画する ProgressBarRenderer.DrawHorizontalBar(graphics, paintRect); //ProgressBarのバーを描画する Rectangle barBounds = new Rectangle( paintRect.Left + 3, paintRect.Top + 3, paintRect.Width - 4, paintRect.Height - 6); barBounds.Width = (int)Math.Round(barBounds.Width * rate); ProgressBarRenderer.DrawHorizontalChunks(graphics, barBounds); } else { //visualスタイルで描画できない時 graphics.FillRectangle(Brushes.White, paintRect); graphics.DrawRectangle(Pens.Black, paintRect); Rectangle barBounds = new Rectangle( paintRect.Left + 1, paintRect.Top + 1, paintRect.Width - 1, paintRect.Height - 1); barBounds.Width = (int)Math.Round(barBounds.Width * rate); graphics.FillRectangle(Brushes.Blue, barBounds); } } //フォーカスの枠を表示する if (this.DataGridView.CurrentCellAddress.X == this.ColumnIndex && this.DataGridView.CurrentCellAddress.Y == this.RowIndex && (paintParts & DataGridViewPaintParts.Focus) == DataGridViewPaintParts.Focus && this.DataGridView.Focused) { //フォーカス枠の大きさを適当に決める Rectangle focusRect = paintRect; focusRect.Inflate(-3, -3); ControlPaint.DrawFocusRectangle(graphics, focusRect); //背景色を指定してフォーカス枠を描画する時 //ControlPaint.DrawFocusRectangle( // graphics, focusRect, Color.Empty, bkColor); } //テキストを表示する if ((paintParts & DataGridViewPaintParts.ContentForeground) == DataGridViewPaintParts.ContentForeground) { //表示するテキストを決定 string txt = string.Format("{0}%", Math.Round(rate * 100)); //string txt = formattedValue.ToString(); //本来は、cellStyleによりTextFormatFlagsを決定すべき TextFormatFlags flags = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter; //色を決定 Color fColor = cellStyle.ForeColor; //if (isSelected) // fColor = cellStyle.SelectionForeColor; //else // fColor = cellStyle.ForeColor; //テキストを描画する paintRect.Inflate(-2, -2); TextRenderer.DrawText(graphics, txt, cellStyle.Font, paintRect, fColor, flags); } //エラーアイコンの表示 if ((paintParts & DataGridViewPaintParts.ErrorIcon) == DataGridViewPaintParts.ErrorIcon && this.DataGridView.ShowCellErrors && !string.IsNullOrEmpty(errorText)) { //エラーアイコンを表示させる領域を取得 Rectangle iconBounds = this.GetErrorIconBounds( graphics, cellStyle, rowIndex); iconBounds.Offset(cellBounds.X, cellBounds.Y); //エラーアイコンを描画 this.PaintErrorIcon(graphics, iconBounds, cellBounds, errorText); } } }
このように作成したDataGridViewProgressBarColumnの使用法は、例えば次のようになります。詳しくは、こちらなどをご覧ください。
'DataGridViewProgressBarColumnを作成する Dim pbColumn As New DataGridViewProgressBarColumn() 'データソースの"Column1"をバインドする pbColumn.DataPropertyName = "Column1" '列を追加する DataGridView1.Columns.Add(pbColumn)
//DataGridViewProgressBarColumnを作成する DataGridViewProgressBarColumn pbColumn = new DataGridViewProgressBarColumn(); //データソースの"Column1"をバインドする pbColumn.DataPropertyName = "Column1"; //列を追加する DataGridView1.Columns.Add(pbColumn);
基本的な説明は上記のコードにコメントとして記述していますが、少し分かりにくいと思われる箇所を以下に補足します。
Paintメソッドをオーバーライドして、セルを描画します。
この例では、フォーカスを示す枠線を表示していますが、通常はプログレスバーには必要ないでしょう。ここでは背景部分にフォーカス枠を表示するのではなく、プログレスバーの上に表示しているため、ControlPaint.DrawFocusRectangleメソッドに背景色を指定していません。通常は、ControlPaint.DrawFocusRectangleメソッドにセルの背景色を指定すべきでしょう。(Rectangle.Inflateはフォーカス枠をプログレスバーの上に表示するためのものなので、これも通常は不要です。)
テキストを表示している箇所も、普通はプログレスバーには必要ないでしょう。ここではパーセンテージを表示していますが、通常はformattedValueを表示することになるでしょう。
テキストの描画にはGraphics.DrawStringメソッドではなく、TextRenderer.DrawTextメソッドを使用していますが、DataGridViewは内部でTextRendererを使用しているようなので、このようにしています。
カスタムセルクラスに新しいプロパティ(MaximumとMimimum)を追加しているため、Cloneメソッドをオーバーライドする必要があります。カスタムセルクラスに新しいプロパティを追加しないのであれば、その必要はありません。
DataGridViewProgressBarColumnクラスでは、MaximumとMimimumプロパティを独自に追加して、DataGridViewProgressBarCellの同プロパティを設定できるようにしています。ここではCellTemplateを変更して、さらにDataGridViewのすべてのDataGridViewProgressBarCellを変更しています。つまり、プロパティの値を保持しているのは列ではなく、セルです。
列クラスに独自のプロパティを追加する場合、上記のようにセルが値を保持する方法以外に、列が保持する方法もあります。この場合、セルクラスからこのプロパティの値を取得するには、セルクラスのOwningColumnプロパティで列オブジェクトを取得して、プロパティの値を取得します。
プロパティの値をセルで保持する場合、セルクラスのCloneメソッドをオーバーライドして、プロパティの内容が正しくコピーされるようにする必要があります。プロパティの値を列で保持する場合は、列クラスのCloneメソッドをオーバーライドする必要があります。(もちろん両方で保持する場合は、両方のCloneメソッドをオーバーライドする必要があります。)
プロパティの値を列で保持するケースは、その列の全てのセルが同じ設定でなければならないという場合です。例えば、DataGridViewButtonColumnクラスのTextプロパティなどです。それ以外は、セルでプロパティの値を保持します。
プロパティの値を列で保持する例は、こちらで紹介します。
注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。