Click here to Skip to main content
11,721,385 members (82,336 online)
Click here to Skip to main content

DataGridViewColumn Hosting MaskedTextBox

, 12 May 2008 CPOL 122.9K 5.2K 67
Rate this:
Please Sign up or sign in to vote.
How to host a MaskedTextBox in a DataGridViewColumn

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 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;

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

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

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")]
//  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.

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 MaskedTextBox class implementing the 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;
    //    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:

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)

Share

About the Author

J.Thomas
Germany Germany
No Biography provided

You may also be interested in...

Comments and Discussions

 
BugError Pin
Member 119463963-Sep-15 19:52
memberMember 119463963-Sep-15 19:52 
QuestionText shows as blank even when there should be text Pin
SSi_Cincinnati11-Nov-14 8:28
memberSSi_Cincinnati11-Nov-14 8:28 
AnswerRe: Text shows as blank even when there should be text Pin
SSi_Cincinnati11-Nov-14 10:19
memberSSi_Cincinnati11-Nov-14 10:19 
QuestionThanks a lot. Pin
shprabin9-Feb-14 5:13
membershprabin9-Feb-14 5:13 
QuestionAutomatically move to next cell after correct input = NullReference Exception Pin
Raxxa_18-Sep-13 3:10
memberRaxxa_18-Sep-13 3:10 
QuestionMask DataGridView DateTime column Pin
Member 97107155-Jul-13 23:00
memberMember 97107155-Jul-13 23:00 
GeneralMy vote of 5 Pin
Member 926117512-Feb-13 21:31
memberMember 926117512-Feb-13 21:31 
QuestionAm I forced to enter x number of numerals before the decimal point? Pin
tzajicek8-Nov-12 5:19
membertzajicek8-Nov-12 5:19 
GeneralMy vote of 5 Pin
MTALASAZ28-Sep-12 8:26
memberMTALASAZ28-Sep-12 8:26 
QuestionNot working in a VB.NET client project. Pin
Brady Kelly26-Sep-12 21:53
memberBrady 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
membermrMagik38055-Aug-11 13:10 
GeneralThanks Pin
YZK30-Mar-11 18:53
groupYZK30-Mar-11 18:53 
GeneralMask Pin
Flicode4-Oct-10 2:52
memberFlicode4-Oct-10 2:52 
Questionhow to force to startingposition of 0 when entering masktextbox Pin
coldfusiongod4-Feb-10 17:02
membercoldfusiongod4-Feb-10 17:02 
GeneralToString vs Cast Pin
Drew Loika17-Dec-09 13:17
memberDrew Loika17-Dec-09 13:17 
QuestionError occur Pin
daqx772-Sep-09 22:13
memberdaqx772-Sep-09 22:13 
GeneralQuestion regarding MaskedTextBox properties Pin
alon198026-Aug-09 3:23
memberalon198026-Aug-09 3:23 
GeneralRe: Question regarding MaskedTextBox properties Pin
J.Thomas26-Aug-09 3:55
memberJ.Thomas26-Aug-09 3:55 
GeneralImplement in Web Pin
mbaocha3-May-09 8:32
membermbaocha3-May-09 8:32 
GeneralRe: Implement in Web Pin
J.Thomas3-May-09 21:30
memberJ.Thomas3-May-09 21:30 
GeneralBug found Pin
Dennis Betten9-Dec-08 23:11
memberDennis Betten9-Dec-08 23:11 
GeneralRe: Bug found Pin
Dennis Betten10-Dec-08 2:58
memberDennis Betten10-Dec-08 2:58 
GeneralRe: Bug found Pin
debeysm12-Apr-11 1:31
memberdebeysm12-Apr-11 1:31 
GeneralRe: Bug found Pin
dongvan21-Sep-11 9:25
memberdongvan21-Sep-11 9:25 
GeneralRe: Bug found Pin
StacyOW22-Aug-14 4:16
memberStacyOW22-Aug-14 4:16 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.150901.1 | Last Updated 12 May 2008
Article Copyright 2008 by J.Thomas
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid