Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / Windows Forms
Article

DataGridViewColumn Hosting MaskedTextBox

Rate me:
Please Sign up or sign in to vote.
4.55/5 (21 votes)
12 May 2008CPOL3 min read 220.8K   7.9K   68   39
How to host a MaskedTextBox in a DataGridViewColumn
Image 1

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) MaskedTextColumns 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:

C#
using JThomas.Controls;

//  create a new column and set a mask using a minimum of 4 and a maximum of 7 digits
DataGridViewMaskedTextColumn column = new DataGridViewMaskedTextColumn("0000999");
column.DataPropertyName = "Number";
column.HeaderText = "Nummer";     //  German text
column.Name = "column";
column.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
column.Width = 70;

//  add this column to the DataGridView dgv
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:

C#
//  use the Bitmap of MaskedTextBox
[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:

C#
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:

C#
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.

C#
[Category("Masking")]
//  use the MaskedTextBox.Mask description
[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.

C#
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:

C#
public object EditingControlFormattedValue
{
    get    {    return Text;        }
    set    {    if (value is string)
            Text = (string)value;
    }
}

public void ApplyCellStyleToEditingControl(
    DataGridViewCellStyle dataGridViewCellStyle)
{
    Font = dataGridViewCellStyle.Font;
    //    get the current cell to use the specific mask string
    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;
    }
}

// MaskedTextBox event
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:

C#
void DgvEditingControlShowing
    (object sender, DataGridViewEditingControlShowingEventArgs e)
{
        if (e.Control is TextBox) {
                TextBox box = e.Control as TextBox;
                // example: 2<sup>nd</sup> column requires upper case
                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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
PraiseVery useful Pin
JeDi Wang20-Dec-22 17:06
JeDi Wang20-Dec-22 17:06 
PraiseEXCELLENT!! Pin
Grets121926-Mar-21 6:57
Grets121926-Mar-21 6:57 
NewsBye-bye Pin
J.Thomas4-Jun-20 22:30
J.Thomas4-Jun-20 22:30 
PraiseGood job Pin
Member 886595225-Apr-20 11:42
Member 886595225-Apr-20 11:42 
QuestionMask is not working in an inherited datagridview Pin
Shouvik2627-Nov-15 4:25
Shouvik2627-Nov-15 4:25 
GeneralMy vote of 5 Pin
Member 119866575-Nov-15 4:54
Member 119866575-Nov-15 4:54 
BugError Pin
Member 119463963-Sep-15 19:52
Member 119463963-Sep-15 19:52 
QuestionText shows as blank even when there should be text Pin
SSi_Cincinnati11-Nov-14 8:28
SSi_Cincinnati11-Nov-14 8:28 
AnswerRe: Text shows as blank even when there should be text Pin
SSi_Cincinnati11-Nov-14 10:19
SSi_Cincinnati11-Nov-14 10:19 
QuestionThanks a lot. Pin
shprabin9-Feb-14 5:13
shprabin9-Feb-14 5:13 
You have saved my day. Thanks a lot Thomas.
QuestionAutomatically move to next cell after correct input = NullReference Exception Pin
Raxxa_18-Sep-13 3:10
Raxxa_18-Sep-13 3:10 
QuestionMask DataGridView DateTime column Pin
Member 97107155-Jul-13 23:00
Member 97107155-Jul-13 23:00 
GeneralMy vote of 5 Pin
Member 926117512-Feb-13 21:31
Member 926117512-Feb-13 21:31 
QuestionAm I forced to enter x number of numerals before the decimal point? Pin
tzajicek8-Nov-12 5:19
tzajicek8-Nov-12 5:19 
GeneralMy vote of 5 Pin
MTALASAZ28-Sep-12 8:26
MTALASAZ28-Sep-12 8:26 
QuestionNot working in a VB.NET client project. Pin
Brady Kelly26-Sep-12 21:53
Brady Kelly26-Sep-12 21:53 
QuestionSolved my need, but...how do I tell it to exclude literials and promp char in text output Pin
mrMagik38055-Aug-11 13:10
mrMagik38055-Aug-11 13:10 
AnswerRe: Solved my need, but...how do I tell it to exclude literials and promp char in text output Pin
k1attila118-Dec-16 4:32
k1attila118-Dec-16 4:32 
GeneralThanks Pin
YZK30-Mar-11 18:53
YZK30-Mar-11 18:53 
GeneralMask Pin
Flicode4-Oct-10 2:52
Flicode4-Oct-10 2:52 
Questionhow to force to startingposition of 0 when entering masktextbox Pin
coldfusiongod4-Feb-10 17:02
coldfusiongod4-Feb-10 17:02 
GeneralToString vs Cast Pin
Drew Loika17-Dec-09 13:17
Drew Loika17-Dec-09 13:17 
QuestionError occur Pin
daqx772-Sep-09 22:13
daqx772-Sep-09 22:13 
GeneralQuestion regarding MaskedTextBox properties Pin
alon198026-Aug-09 3:23
alon198026-Aug-09 3:23 
GeneralRe: Question regarding MaskedTextBox properties Pin
J.Thomas26-Aug-09 3:55
J.Thomas26-Aug-09 3:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.