Introduction
You want to use a MaskedTextBox
in your DataGridViewColumn
? Here it is. Add a DataGridViewMaskedTextColumn
to your DataGridView
and set the Mask
string. Then you get a MaskedTextBox
using this Mask
as EditingControl
.
Background
The .NET Framework contains for DataGridView
columns a standard TextBox
only. By data binding, it's no problem to restrict the input to numbers, e.g. TextBox.CharacterCasing
can be set by DataGridView.EditingControlShowing
event. More complex restrictions are available in MaskedTextBox
. Therefore it's useful to host a MaskedTextBox
in a DataGridViewColumn
.
Yildirim Kocdag's solution Generic DataGridView (updated 24th January, 2008) contains a MaskedTextColumn
, but there's a problem if one needs to add two (or more) MaskedTextColumn
s with different Mask
strings: In this case, the control keeps one mask only because the DataGridView
contains one specific EditingControl
for all columns.
Microsoft's example How to: Host Controls in Windows Forms DataGridView Cells gives a simple way how to create your own DataGridViewColumn
class. But it's insufficient because the Mask
property needs to implement the Clone
method, while the example doesn't need to. I found my solution after I studied all the original DataGridView
classes using Lutz Roeder's Reflector.
Using the Code
The MaskedTextColumn
solution consists of three combined classes, added by an attribute class.
The <code>ReferencedDescriptionAttribute
class is used in the designer to call an existing description. See details here.
The MaskedText
classes are included into your own ExtendedControls.dll assembly, or you store the prepared DLL directly. Add a reference to this assembly, and you can use the DataGridViewMaskedTextColumn
. You need to use this class only; the DataGridViewMaskedTextCell
class and the DataGridViewMaskedTextControl
class are used automatically by the framework itself.
Working by Code
Add a DataGridViewMaskedTextColumn
to your DataGridView
like any other DataGridViewColumn
and set the Mask
property:
using JThomas.Controls;
DataGridViewMaskedTextColumn column = new DataGridViewMaskedTextColumn("0000999");
column.DataPropertyName = "Number";
column.HeaderText = "Nummer";
column.Name = "column";
column.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
column.Width = 70;
dgv.Columns.Add(column);
Working by Designer
After adding the reference to the ExtendedControls.dll assembly, you can open the IDE and select a DataGridViewMaskedTextColumn
as DataGridViewColumn
– either in the Edit Columns window, like in the image above, or in the Add Column window. There you can set the Mask
string.
Mask Details
Check all Mask
possibilities in the SDK documentation for the MaskedTextBox.Mask
property.
How It Works
As shown in Microsoft's example, we have to define three classes.
DataGridViewMaskedTextColumn
This class is derived from DataGridViewColumn
in the same way as the original DataGridViewTextBoxColumn
class. We must implement our own derivation because the MaxInputLength
property is not valid in MaskedTextBox
instances. It contains the standard constructor and another constructor setting the Mask
string directly:
[System.Drawing.ToolboxBitmap(typeof(System.Windows.Forms.MaskedTextBox))]
public class DataGridViewMaskedTextColumn : DataGridViewColumn
{
public DataGridViewMaskedTextColumn() : this(String.Empty) {}
public DataGridViewMaskedTextColumn(string maskString)
: base(new DataGridViewMaskedTextCell())
{
SortMode = DataGridViewColumnSortMode.Automatic;
Mask = maskString;
}
Some methods and properties are derived like in DataGridViewTextBoxColumn
class:
public override string ToString() { ... }
public override DataGridViewCell CellTemplate { get; set; }
public new DataGridViewColumnSortMode SortMode { get; set; }
private DataGridViewMaskedTextCell MaskedTextCellTemplate { get; }
We must add the Clone
method for using the Mask
property:
public override object Clone()
{
DataGridViewMaskedTextColumn col = (DataGridViewMaskedTextColumn)base.Clone();
col.Mask = Mask;
col.CellTemplate = (DataGridViewMaskedTextCell)this.CellTemplate.Clone();
return col;
}
The main work is to use the Mask
string in each DataGridViewMaskedTextCell
of this column. Therefore we may not only define a simple getter and setter, but must store the value in each contained DataGridViewCell
like DataGridViewTextBoxColumn
does for its MaxInputLength
property.
[Category("Masking")]
[JThomas.Extensions.ReferencedDescription(typeof
(System.Windows.Forms.MaskedTextBox),"Mask")]
public string Mask
{
get { ... }
set {
if (Mask != value)
{
MaskedTextCellTemplate.Mask = value;
if (base.DataGridView != null)
{
DataGridViewRowCollection rows = base.DataGridView.Rows;
int count = rows.Count;
for (int i = 0; i < count; i++)
{
DataGridViewMaskedTextCell cell
= rows.SharedRow(i).Cells[base.Index]
as DataGridViewMaskedTextCell;
if (cell != null)
cell.Mask = value;
}
}
}
}
}
DataGridViewMaskedTextCell
This class can be derived from the DataGridViewTextBoxCell
class because the behaviour in an inactive cell doesn't differ. It's only necessary to override some methods and properties and adapt to the current classes and names. The main work is to add the Clone
method and the Mask
property in a simple way.
public override object Clone()
{
DataGridViewMaskedTextCell cell = base.Clone() as DataGridViewMaskedTextCell;
cell.Mask = this.Mask;
return cell;
}
private string mask;
public string Mask {
get { return mask == null ? String.Empty : mask; }
set { mask = value; }
}
DataGridViewMaskedTextControl
This class must be derived from the <code><code><code>MaskedTextBox
class implementing the <code><code><code>IDataGridViewEditingControl
interface. Most members are adapted in the same way like Microsoft's example does. It's only necessary to use string
values instead of object
and MaskedTextBox.Text
property instead of DateTimePicker.Value
property.
The most important definitions are the following ones:
public object EditingControlFormattedValue
{
get { return Text; }
set { if (value is string)
Text = (string)value;
}
}
public void ApplyCellStyleToEditingControl(
DataGridViewCellStyle dataGridViewCellStyle)
{
Font = dataGridViewCellStyle.Font;
DataGridViewMaskedTextCell cell
= dataGridView.CurrentCell as DataGridViewMaskedTextCell;
if (cell != null) {
Mask = cell.Mask;
}
}
public void PrepareEditingControlForEdit(bool selectAll)
{
if (selectAll)
SelectAll();
else {
SelectionStart = 0;
SelectionLength = 0;
}
}
protected override void OnTextChanged(System.EventArgs e)
{
base.OnTextChanged(e);
EditingControlValueChanged = true;
if (EditingControlDataGridView != null) {
EditingControlDataGridView.CurrentCell.Value = Text;
}
}
Points of Interest
TextBox.CharacterCasing
doesn't need a DataGridViewMaskedTextColumn
, but can be embedded as follows:
void DgvEditingControlShowing
(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if (e.Control is TextBox) {
TextBox box = e.Control as TextBox;
box.CharacterCasing
= dgv.CurrentCell.ColumnIndex == 1
? CharacterCasing.Upper
: CharacterCasing.Normal;
}
}
It would be possible to insert a DefaultValue
property into the DataGridViewMaskedText
solution. On the other side, it's useful to connect default values to data instances. You should better use DataColumn.DefaultValue
property, or DataTable.TableNewRow
event, or DataGridView.RowsAdded
event, or BindingNavigator.AddNewItem.Click
event, or something else like that.
History
- 05/04/2008: First version