注意:DataGridViewコントロールは、.NET Framework 2.0で新しく追加されました。
ここでは、MaskedTextBoxコントロールを使用してDataGridViewのセルを編集できるようにする方法を紹介します。つまり、DataGridViewTextBoxCellがテキストボックス、DataGridViewComboBoxCellがコンボボックスを表示するように、MaskedTextBoxコントロールを表示してセルを編集できるようにします。ここで紹介する方法は、MaskedTextBox以外のコントロールをセルの編集に使用したい場合でも参考になるでしょう。
補足:ここではセルの編集に使用するコントロールを独自にホストする方法だけを紹介し、セルの描画についてはDataGridViewTextBoxCellクラスに全て任せてしまいます。カスタムセルクラスでセルを自分で描画する方法は、こちらで紹介しています。
補足:MSDNの「方法 : Windows フォーム DataGridView Cells でコントロールをホストする」では、DateTimePickerコントロールをセルの編集に使用する方法が紹介されています。また、「DataGridView: Masked Edit Column」では、この記事と同じように、MaskedTextBoxコントロールをホストするコードが紹介されています。これらも参考にしてください。
任意のコントロールをセルの編集に使用できるようにするには、3つのクラスを作成する必要があります。カスタムセルクラス(DataGridViewCellクラスの派生クラス)、カスタム列クラス(DataGridViewColumnクラスの派生クラス)、そして、編集に使用するコントロールを継承し、IDataGridViewEditingControlインターフェイスを実装したクラスです。
補足:カスタム列クラスは必ず作成しなければならないということはありません。例えばDataGridViewTextBoxCellクラスから派生したカスタムセルならば、DataGridViewTextBoxColumn列に表示することもできます。このようにする場合は、DataGridViewTextBoxColumnオブジェクトのCellTemplateプロパティにカスタムセルクラスのインスタンスを設定します。
以下にこれら3つのクラスのコードの例を示します。説明の多くはコメントとして書き加えています。
Imports System Imports System.Windows.Forms ''' <summary> ''' DataGridViewMaskedTextBoxCellオブジェクトの列を表します。 ''' </summary> Public Class DataGridViewMaskedTextBoxColumn Inherits DataGridViewColumn 'CellTemplateとするDataGridViewMaskedTextBoxCellオブジェクトを指定して '基本クラスのコンストラクタを呼び出す Public Sub New() MyBase.New(New DataGridViewMaskedTextBoxCell()) End Sub Private maskValue As String = "" ''' <summary> ''' MaskedTextBoxのMaskプロパティに適用する値 ''' </summary> Public Property Mask() As String Get Return Me.maskValue End Get Set(ByVal value As String) Me.maskValue = value End Set End Property '新しいプロパティを追加しているため、 ' Cloneメソッドをオーバーライドする必要がある Public Overrides Function Clone() As Object Dim col As DataGridViewMaskedTextBoxColumn = _ DirectCast(MyBase.Clone(), DataGridViewMaskedTextBoxColumn) col.Mask = Me.Mask Return col End Function 'CellTemplateの取得と設定 Public Overrides Property CellTemplate() As DataGridViewCell Get Return MyBase.CellTemplate End Get Set(ByVal value As DataGridViewCell) 'DataGridViewMaskedTextBoxCellしか ' CellTemplateに設定できないようにする If Not (TypeOf value Is DataGridViewMaskedTextBoxCell) Then Throw New InvalidCastException( _ "DataGridViewMaskedTextBoxCellオブジェクトを" & _ "指定してください。") End If MyBase.CellTemplate = value End Set End Property End Class ''' <summary> ''' MaskedTextBoxで編集できるテキスト情報を ''' DataGridViewコントロールに表示します。 ''' </summary> Public Class DataGridViewMaskedTextBoxCell Inherits DataGridViewTextBoxCell 'コンストラクタ Public Sub New() End Sub '編集コントロールを初期化する '編集コントロールは別のセルや列でも使いまわされるため、初期化の必要がある Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer, _ ByVal initialFormattedValue As Object, _ ByVal dataGridViewCellStyle As DataGridViewCellStyle) MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, _ dataGridViewCellStyle) '編集コントロールの取得 Dim maskedBox As DataGridViewMaskedTextBoxEditingControl = _ TryCast(Me.DataGridView.EditingControl, _ DataGridViewMaskedTextBoxEditingControl) If maskedBox IsNot Nothing Then 'Textを設定 Dim maskedText As String = TryCast(initialFormattedValue, String) maskedBox.Text = If(maskedText IsNot Nothing, maskedText, "") 'カスタム列のプロパティを反映させる Dim column As DataGridViewMaskedTextBoxColumn = _ TryCast(Me.OwningColumn, DataGridViewMaskedTextBoxColumn) If column IsNot Nothing Then maskedBox.Mask = column.Mask End If End If End Sub '編集コントロールの型を指定する Public Overrides ReadOnly Property EditType() As Type Get Return GetType(DataGridViewMaskedTextBoxEditingControl) End Get End Property 'セルの値のデータ型を指定する 'ここでは、Object型とする '基本クラスと同じなので、オーバーライドの必要なし Public Overrides ReadOnly Property ValueType() As Type Get Return GetType(Object) End Get End Property '新しいレコード行のセルの既定値を指定する Public Overrides ReadOnly Property DefaultNewRowValue() As Object Get Return MyBase.DefaultNewRowValue End Get End Property End Class ''' <summary> ''' DataGridViewMaskedTextBoxCellでホストされる ''' MaskedTextBoxコントロールを表します。 ''' </summary> Public Class DataGridViewMaskedTextBoxEditingControl Inherits MaskedTextBox Implements IDataGridViewEditingControl '編集コントロールが表示されているDataGridView Private dataGridView As DataGridView '編集コントロールが表示されている行 Private rowIndex As Integer '編集コントロールの値とセルの値が違うかどうか Private valueChanged As Boolean 'コンストラクタ Public Sub New() Me.TabStop = False End Sub #Region "IDataGridViewEditingControl メンバ" '編集コントロールで変更されたセルの値 Public Function GetEditingControlFormattedValue( _ ByVal context As DataGridViewDataErrorContexts) As Object _ Implements IDataGridViewEditingControl.GetEditingControlFormattedValue Return Me.Text End Function '編集コントロールで変更されたセルの値 Public Property EditingControlFormattedValue() As Object _ Implements IDataGridViewEditingControl.EditingControlFormattedValue Get Return Me.GetEditingControlFormattedValue( _ DataGridViewDataErrorContexts.Formatting) End Get Set(ByVal value As Object) Me.Text = DirectCast(value, String) End Set End Property 'セルスタイルを編集コントロールに適用する '編集コントロールの前景色、背景色、フォントなどをセルスタイルに合わせる Public Sub ApplyCellStyleToEditingControl( _ ByVal dataGridViewCellStyle As DataGridViewCellStyle) _ Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl Me.Font = dataGridViewCellStyle.Font Me.ForeColor = dataGridViewCellStyle.ForeColor Me.BackColor = dataGridViewCellStyle.BackColor Select Case dataGridViewCellStyle.Alignment Case DataGridViewContentAlignment.BottomCenter, _ DataGridViewContentAlignment.MiddleCenter, _ DataGridViewContentAlignment.TopCenter Me.TextAlign = HorizontalAlignment.Center Exit Select Case DataGridViewContentAlignment.BottomRight, _ DataGridViewContentAlignment.MiddleRight, _ DataGridViewContentAlignment.TopRight Me.TextAlign = HorizontalAlignment.Right Exit Select Case Else Me.TextAlign = HorizontalAlignment.Left Exit Select End Select End Sub '編集するセルがあるDataGridView Public Property EditingControlDataGridView() As DataGridView _ Implements IDataGridViewEditingControl.EditingControlDataGridView Get Return Me.dataGridView End Get Set(ByVal value As DataGridView) Me.dataGridView = value End Set End Property '編集している行のインデックス Public Property EditingControlRowIndex() As Integer _ Implements IDataGridViewEditingControl.EditingControlRowIndex Get Return Me.rowIndex End Get Set(ByVal value As Integer) Me.rowIndex = value End Set End Property '値が変更されたかどうか '編集コントロールの値とセルの値が違うかどうか Public Property EditingControlValueChanged() As Boolean _ Implements IDataGridViewEditingControl.EditingControlValueChanged Get Return Me.valueChanged End Get Set(ByVal value As Boolean) Me.valueChanged = value End Set End Property '指定されたキーをDataGridViewが処理するか、編集コントロールが処理するか 'Trueを返すと、編集コントロールが処理する 'dataGridViewWantsInputKeyがTrueの時は、DataGridViewが処理できる Public Function EditingControlWantsInputKey(ByVal keyData As Keys, _ ByVal dataGridViewWantsInputKey As Boolean) As Boolean _ Implements IDataGridViewEditingControl.EditingControlWantsInputKey 'Keys.Left、Right、Home、Endの時は、Trueを返す 'このようにしないと、これらのキーで別のセルにフォーカスが移ってしまう Select Case keyData And Keys.KeyCode Case Keys.Right, Keys.[End], Keys.Left, Keys.Home Return True Case Else Return Not dataGridViewWantsInputKey End Select End Function 'マウスカーソルがEditingPanel上にあるときのカーソルを指定する 'EditingPanelは編集コントロールをホストするパネルで、 '編集コントロールがセルより小さいとコントロール以外の部分がパネルとなる Public ReadOnly Property EditingPanelCursor() As Cursor _ Implements IDataGridViewEditingControl.EditingPanelCursor Get Return MyBase.Cursor End Get End Property 'コントロールで編集する準備をする 'テキストを選択状態にしたり、挿入ポインタを末尾にしたりする Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) _ Implements IDataGridViewEditingControl.PrepareEditingControlForEdit If selectAll Then '選択状態にする Me.SelectAll() Else '挿入ポインタを末尾にする Me.SelectionStart = Me.TextLength End If End Sub '値が変更した時に、セルの位置を変更するかどうか '値が変更された時に編集コントロールの大きさが変更される時はTrue Public ReadOnly Property RepositionEditingControlOnValueChange() As Boolean _ Implements IDataGridViewEditingControl.RepositionEditingControlOnValueChange Get Return False End Get End Property #End Region '値が変更された時 Protected Overrides Sub OnTextChanged(ByVal e As EventArgs) MyBase.OnTextChanged(e) '値が変更されたことをDataGridViewに通知する Me.valueChanged = True Me.dataGridView.NotifyCurrentCellDirty(True) End Sub End Class
using System; using System.Windows.Forms; /// <summary> /// DataGridViewMaskedTextBoxCellオブジェクトの列を表します。 /// </summary> public class DataGridViewMaskedTextBoxColumn : DataGridViewColumn { //CellTemplateとするDataGridViewMaskedTextBoxCellオブジェクトを指定して //基本クラスのコンストラクタを呼び出す public DataGridViewMaskedTextBoxColumn() : base(new DataGridViewMaskedTextBoxCell()) { } private string maskValue = ""; /// <summary> /// MaskedTextBoxのMaskプロパティに適用する値 /// </summary> public string Mask { get { return this.maskValue; } set { this.maskValue = value; } } //新しいプロパティを追加しているため、 // Cloneメソッドをオーバーライドする必要がある public override object Clone() { DataGridViewMaskedTextBoxColumn col = (DataGridViewMaskedTextBoxColumn)base.Clone(); col.Mask = this.Mask; return col; } //CellTemplateの取得と設定 public override DataGridViewCell CellTemplate { get { return base.CellTemplate; } set { //DataGridViewMaskedTextBoxCellしか // CellTemplateに設定できないようにする if (!(value is DataGridViewMaskedTextBoxCell)) { throw new InvalidCastException( "DataGridViewMaskedTextBoxCellオブジェクトを" + "指定してください。"); } base.CellTemplate = value; } } } /// <summary> /// MaskedTextBoxで編集できるテキスト情報を /// DataGridViewコントロールに表示します。 /// </summary> public class DataGridViewMaskedTextBoxCell : DataGridViewTextBoxCell { //コンストラクタ public DataGridViewMaskedTextBoxCell() { } //編集コントロールを初期化する //編集コントロールは別のセルや列でも使いまわされるため、初期化の必要がある public override void InitializeEditingControl( int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle) { base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle); //編集コントロールの取得 DataGridViewMaskedTextBoxEditingControl maskedBox = this.DataGridView.EditingControl as DataGridViewMaskedTextBoxEditingControl; if (maskedBox != null) { //Textを設定 string maskedText = initialFormattedValue as string; maskedBox.Text = maskedText != null ? maskedText : ""; //カスタム列のプロパティを反映させる DataGridViewMaskedTextBoxColumn column = this.OwningColumn as DataGridViewMaskedTextBoxColumn; if (column != null) { maskedBox.Mask = column.Mask; } } } //編集コントロールの型を指定する public override Type EditType { get { return typeof(DataGridViewMaskedTextBoxEditingControl); } } //セルの値のデータ型を指定する //ここでは、Object型とする //基本クラスと同じなので、オーバーライドの必要なし public override Type ValueType { get { return typeof(object); } } //新しいレコード行のセルの既定値を指定する public override object DefaultNewRowValue { get { return base.DefaultNewRowValue; } } } /// <summary> /// DataGridViewMaskedTextBoxCellでホストされる /// MaskedTextBoxコントロールを表します。 /// </summary> public class DataGridViewMaskedTextBoxEditingControl : MaskedTextBox, IDataGridViewEditingControl { //編集コントロールが表示されているDataGridView DataGridView dataGridView; //編集コントロールが表示されている行 int rowIndex; //編集コントロールの値とセルの値が違うかどうか bool valueChanged; //コンストラクタ public DataGridViewMaskedTextBoxEditingControl() { this.TabStop = false; } #region IDataGridViewEditingControl メンバ //編集コントロールで変更されたセルの値 public object GetEditingControlFormattedValue( DataGridViewDataErrorContexts context) { return this.Text; } //編集コントロールで変更されたセルの値 public object EditingControlFormattedValue { get { return this.GetEditingControlFormattedValue( DataGridViewDataErrorContexts.Formatting); } set { this.Text = (string)value; } } //セルスタイルを編集コントロールに適用する //編集コントロールの前景色、背景色、フォントなどをセルスタイルに合わせる public void ApplyCellStyleToEditingControl( DataGridViewCellStyle dataGridViewCellStyle) { this.Font = dataGridViewCellStyle.Font; this.ForeColor = dataGridViewCellStyle.ForeColor; this.BackColor = dataGridViewCellStyle.BackColor; switch (dataGridViewCellStyle.Alignment) { case DataGridViewContentAlignment.BottomCenter: case DataGridViewContentAlignment.MiddleCenter: case DataGridViewContentAlignment.TopCenter: this.TextAlign = HorizontalAlignment.Center; break; case DataGridViewContentAlignment.BottomRight: case DataGridViewContentAlignment.MiddleRight: case DataGridViewContentAlignment.TopRight: this.TextAlign = HorizontalAlignment.Right; break; default: this.TextAlign = HorizontalAlignment.Left; break; } } //編集するセルがあるDataGridView public DataGridView EditingControlDataGridView { get { return this.dataGridView; } set { this.dataGridView = value; } } //編集している行のインデックス public int EditingControlRowIndex { get { return this.rowIndex; } set { this.rowIndex = value; } } //値が変更されたかどうか //編集コントロールの値とセルの値が違うかどうか public bool EditingControlValueChanged { get { return this.valueChanged; } set { this.valueChanged = value; } } //指定されたキーをDataGridViewが処理するか、編集コントロールが処理するか //Trueを返すと、編集コントロールが処理する //dataGridViewWantsInputKeyがTrueの時は、DataGridViewが処理できる public bool EditingControlWantsInputKey( Keys keyData, bool dataGridViewWantsInputKey) { //Keys.Left、Right、Home、Endの時は、Trueを返す //このようにしないと、これらのキーで別のセルにフォーカスが移ってしまう switch (keyData & Keys.KeyCode) { case Keys.Right: case Keys.End: case Keys.Left: case Keys.Home: return true; default: return !dataGridViewWantsInputKey; } } //マウスカーソルがEditingPanel上にあるときのカーソルを指定する //EditingPanelは編集コントロールをホストするパネルで、 //編集コントロールがセルより小さいとコントロール以外の部分がパネルとなる public Cursor EditingPanelCursor { get { return base.Cursor; } } //コントロールで編集する準備をする //テキストを選択状態にしたり、挿入ポインタを末尾にしたりする public void PrepareEditingControlForEdit(bool selectAll) { if (selectAll) { //選択状態にする this.SelectAll(); } else { //挿入ポインタを末尾にする this.SelectionStart = this.TextLength; } } //値が変更した時に、セルの位置を変更するかどうか //値が変更された時に編集コントロールの大きさが変更される時はTrue public bool RepositionEditingControlOnValueChange { get { return false; } } #endregion //値が変更された時 protected override void OnTextChanged(EventArgs e) { base.OnTextChanged(e); //値が変更されたことをDataGridViewに通知する this.valueChanged = true; this.dataGridView.NotifyCurrentCellDirty(true); } }
DataGridViewMaskedTextBoxColumnクラスの使用法は、例えば次のようになります。詳しくは、こちらなどをご覧ください。
'DataGridViewMaskedTextBoxColumnを作成 Dim maskedColumn As New DataGridViewMaskedTextBoxColumn() 'データソースの"Column1"をバインドする maskedColumn.DataPropertyName = "Column1" 'MaskedTextBoxのMaskプロパティとなる値を設定する maskedColumn.Mask = "000" 'DataGridViewに列を追加する DataGridView1.Columns.Add(maskedColumn)
//DataGridViewMaskedTextBoxColumnを作成 DataGridViewMaskedTextBoxColumn maskedColumn = new DataGridViewMaskedTextBoxColumn(); //データソースの"Column1"をバインドする maskedColumn.DataPropertyName = "Column1"; //MaskedTextBoxのMaskプロパティとなる値を設定する maskedColumn.Mask = "000"; //DataGridViewに列を追加する DataGridView1.Columns.Add(maskedColumn);
ここで作成したDataGridViewMaskedTextBoxColumnクラスには、「Mask」という新しいプロパティを追加しています。こちらで説明したように、列クラスに独自のプロパティを追加したとき、そのプロパティの値をセルで保持するか、列で保持するかにより、方法が異なります。ここではプロパティ値を列で保持しています。そのため、その値をセルで取得するために、セルクラスのInitializeEditingControlメソッドで列オブジェクトを取得して、プロパティの値を取得しています。
セルでプロパティの値を保持する方法は、こちらで紹介しています。
補足:上記の例の場合、Maskプロパティの値はセルで保持するようにするのが妥当でしょう。しかしここでは、そうではないサンプルを提示するためにあえてこのようにしています。
注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。