Click here to Skip to main content
Click here to Skip to main content

DataGridViewColumn Hosting MaskedTextBox

, 12 May 2008
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)

About the Author

J.Thomas

Germany Germany
No Biography provided

Comments and Discussions

 
Questionhow to force to startingposition of 0 when entering masktextbox Pinmembercoldfusiongod4-Feb-10 17:02 
GeneralToString vs Cast PinmemberDrew Loika17-Dec-09 13:17 
QuestionError occur Pinmemberdaqx772-Sep-09 22:13 
GeneralQuestion regarding MaskedTextBox properties Pinmemberalon198026-Aug-09 3:23 
GeneralRe: Question regarding MaskedTextBox properties PinmemberJ.Thomas26-Aug-09 3:55 
GeneralImplement in Web Pinmembermbaocha3-May-09 8:32 
GeneralRe: Implement in Web PinmemberJ.Thomas3-May-09 21:30 
GeneralBug found PinmemberDennis Betten9-Dec-08 23:11 
GeneralRe: Bug found PinmemberDennis Betten10-Dec-08 2:58 
GeneralRe: Bug found Pinmemberdebeysm12-Apr-11 1:31 
GeneralRe: Bug found Pinmemberdongvan21-Sep-11 9:25 
QuestionDifferences between this solution and MS MaskedTextBoxColumn? Pinmemberkaborka15-Jul-08 11:58 
AnswerRe: differences are not important PinmemberJ.Thomas16-Jul-08 4:21 
Generalterrible that theire still is no good inputmask in java and visual studio ... like theire was in clipper ... Pinmemberjckoenen20-Jun-08 13:59 
QuestionHow to apply mask to whole column and decimal column error PinmemberAndrusM6-Jun-08 6:42 
AnswerRe: How to apply mask to whole column and decimal column error PinmemberJ.Thomas6-Jun-08 7:22 
GeneralRe: How to apply mask to whole column and decimal column error PinmemberAndrusM6-Jun-08 8:30 
GeneralRe: Can't apply mask for output PinmemberJ.Thomas8-Jun-08 1:24 

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 | Mobile
Web03 | 2.8.140709.1 | Last Updated 12 May 2008
Article Copyright 2008 by J.Thomas
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid