Click here to Skip to main content
15,881,204 members
Articles / Programming Languages / C#
Article

Masked C# TextBox Control

Rate me:
Please Sign up or sign in to vote.
4.10/5 (38 votes)
21 Jan 20042 min read 721.1K   8.5K   127   76
A control that can be used for date, IP address, phone number, SSN, digit and decimal format input

Introduction

The TextBox control is the most used control in a Windows program. It also cause a lot of problem either from QA or the user because of invalid data that was entered. Using a masked control will save a lot of time for the developer and reduce the complaints and bugs.

This masked intelligent user control enhances the function of the TextBox control, which can mask the Date, IP Address, SSN, Phone numbers, digits, decimal and checks the validation, and automatically set the delimiter location.

The property Masked is set to None by default and the control works like a normal TextBox control.
If setting the property to DateOnly, the control is masked to Date format.

It is intelligent.

What the user enters

What is Displayed

12 12/

124

12/04/

13

01/3

3

03/

34

03/04/

14

01/04/

1/

01/

Using the ErrorProvider to handle the invalidate input:

Creating Control:

  1. Start the Visual Studio.NET Windows Forms designer.
  2. Select a new C# project by clicking New from the File menu.
  3. Click Windows control library template on the templates.
  4. Set the Name MaskedTextBox

Then open the maskedTextBox.cs to Change the base class to the System.Windows.Forms.TextBox.

Adding a Property:

C#
public enum Mask {None, DateOnly, PhoneWithArea, IpAddress, 
                  SSN, Decimal, Digit };
private Mask m_mask;
public Mask Maked
{
    get { return m_mask;}
    set {
        m_mask = value;
        this.Text="";
    }
}

Override the OnKeyPress function

C#
this.KeyPress += new
              KeyPressEventHandler(this.OnKeyPress);

private void OnKeyPress(object sender, KeyPressEventArgs e)
{
      MaskedTextBox sd = (MaskedTextBox) sender;
      if (sd.m_IPAddrOnly)
            sd.MaskIpAddr(e);
      if (sd.m_digitOnly)
            sd.MaskDigit(e);
      if(sd.m_ssn)
            sd.MaskPhoneSSN(e, 3, 2);
      if(sd.m_phoneOnly)
            sd.MaskPhoneSSN(e, 3, 3);
      if(sd.m_dateOnly)
            sd.MaskDate(e);
      if(sd.m_decimalOnly)
            sd.MaskDecimal(e);
}

Unboxing the sender and using it to call these function is very important for multiple properties and when more than one control is used in one form, otherwise different controls may share the same variable with another control, which will cause unexpected results.

Double check the masked format:

C#
private void OnLeave(object sender, EventArgs e)            
{                                                           
    MaskedTextBox sd = (MaskedTextBox) sender;            
    Regex regStr;                                         
    switch(m_mask)                                        
    {                                                     
        case Mask.DateOnly:                             
            regStr = new Regex(@"\d{2}/\d{2}/\d{4}"); 
            if(!regStr.IsMatch(sd.Text))              
                errorProvider1.SetError(this, "*"); 
            break;                                    

        case Mask.PhoneWithArea:                        
            regStr = new Regex(@"\d{3}-\d{3}-\d{4}"); 
            if(!regStr.IsMatch(sd.Text))              
                errorProvider1.SetError(this,"**"); 
            break;                                    

        case Mask.IpAddress:                            
            short cnt=0;                              
            int len = sd.Text.Length;                 
            for(short i=0; i<len;i++)                 
                if(sd.Text[i] == '.')               
                {                                   
                    cnt++;                        
                    if(i+1 < len)                 
                        if(sd.Text[i+1] == '.') 
                        {                       
                            errorProvider1.SetError(this, "*"); 
                            break;            
                        }                       
                    }                                   
                    if(cnt < 3 || sd.Text[len-1] == '.')      
                        errorProvider1.SetError(this, "*"); 
                    break;        
                                                
        case Mask.SSN:                                  
            regStr = new Regex(@"\d{3}-\d{2}-\d{4}"); 
            if(!regStr.IsMatch(sd.Text))              
                errorProvider1.SetError(this, "*"); 
            break;     
                                           
        case Mask.Decimal:                              
            break;                                    

        case Mask.Digit:                                
            break;                                    
    }                                                     
}   

Usage

After creating the DLL program, you should add the component into ToolBox. By:

  1. Right click the mouse in the ToolBox, and then select Customize Toolbox to open a dialog.
  2. Select the tab .Net framework component,
  3. Use the browser to find your DLL, check it, then click Ok.

The control should be in the ToolBox and it is ready to use.

That is all. I hope you enjoy it.

History:

Update at 03/04/2002.
Using enum to set the properties.
Add Onleave function to check the invalidation of the controls.
Add Leap year check for Date mask in the CheckDayOfMonth funcrion.

12 July 2002 - updated source download

9 Nov 2003 - updated source download

If you have any comments or find some bugs, I would love to hear about it and make it better. You can reach me at Jibin Pan.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


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

Comments and Discussions

 
QuestionLicense Pin
hagop_cg24-Feb-22 0:25
hagop_cg24-Feb-22 0:25 
Questionabout new features Pin
glovkesh11-Sep-14 22:06
glovkesh11-Sep-14 22:06 
Questioncan someone help me Pin
babarkhalid24-Jun-14 21:52
babarkhalid24-Jun-14 21:52 
Questionversion mask Pin
Chris Anders28-May-14 20:18
Chris Anders28-May-14 20:18 
GeneralMy vote of 4 Pin
Feb1925-Feb-13 22:08
Feb1925-Feb-13 22:08 
Generalshamsi date(yyyy/mm/dd) [modified] Pin
ss_hellhound12-Nov-07 3:43
ss_hellhound12-Nov-07 3:43 
QuestionCrystal Report for particullar where condition Pin
bagurtheen15-Feb-07 1:56
bagurtheen15-Feb-07 1:56 
GeneralMask for Email address Pin
ASysSolvers11-Dec-06 0:23
ASysSolvers11-Dec-06 0:23 
GeneralRe: Mask for Email address Pin
Expert Coming16-Jun-07 13:01
Expert Coming16-Jun-07 13:01 
GeneralHuge Bug Pin
subhash_c14-Apr-06 14:53
subhash_c14-Apr-06 14:53 
QuestionHow to use with simple regular expression? Pin
Perry211-Apr-06 12:52
Perry211-Apr-06 12:52 
GeneralCopy/paste problem Pin
jfloviou20-Jul-05 7:43
jfloviou20-Jul-05 7:43 
Generalip problem Pin
RanjithLogics11-May-05 19:04
RanjithLogics11-May-05 19:04 
GeneralMaskedTextBox in SharpDevelop Pin
fuese27-Apr-05 4:27
fuese27-Apr-05 4:27 
GeneralMask Ip... Pin
Rothariger18-Feb-05 7:31
Rothariger18-Feb-05 7:31 
GeneralRe: Mask Ip... [modified] Pin
PandaPKH14-Jun-06 0:09
PandaPKH14-Jun-06 0:09 
GeneralError Provider help Pin
betterc11-Jul-04 3:27
betterc11-Jul-04 3:27 
GeneralRe: Error Provider help Pin
betterc15-Aug-04 9:22
betterc15-Aug-04 9:22 
GeneralRe: Error Provider help Pin
RogerDodge6-Nov-05 1:02
RogerDodge6-Nov-05 1:02 
GeneralRe: Error Provider help Pin
SmartGuest17-Jan-11 4:32
SmartGuest17-Jan-11 4:32 
GeneralGerman date Pin
sventek7-Jun-04 20:06
sventek7-Jun-04 20:06 
GeneralRe: German date Pin
haezeban17-Jun-04 10:37
haezeban17-Jun-04 10:37 
Hello,

I hope my code will fit in this reply. This is code I found on the codeproject site but I changed it to dd/mm/yyyy format.
There are two porblems with it :
- you can not go in override mode, to change the day you need to use backspace till the beginning and then put in the data completely again.
- If you use backspace to far, you will get a runtime error.

Hope it helps, any comments are apreciated
Nicky

public class DateTextBox : System.Windows.Forms.TextBox
{
public enum Mask {None, DateOnly, PhoneWithArea, IpAddress, SSN, Decimal, Digit };
private Mask m_mask;

public Mask Masked
{
get { return m_mask;}
set
{
m_mask = value;
this.Text="";
}
}
private int digitPos=0;
private int DelimitNumber=0;
private bool CtrlV = false;

private System.ComponentModel.Container components = null;

public DateTextBox()
{
InitializeComponent();
if(Masked != Mask.None)
m_mask = Masked;

digitPos=0; // Positie van het cijfer binnen de groep
DelimitNumber=0; // Aantal scheidingtekens
this.Text=null;

this.ContextMenu = new ContextMenu();
}

protected override void Dispose( bool disposing )
{
if( disposing )
{
if( components != null )
components.Dispose();
}
base.Dispose( disposing );
}

#region Component Designer generated code
private void InitializeComponent()
{
this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.OnKeyPress);
this.Leave += new System.EventHandler(this.OnLeave);
}
#endregion

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
// trap Ctrl-V paste and prevent invalid values
// return false to allow further processing
CtrlV = false;
if(keyData == (Keys)Shortcut.CtrlV || keyData == (Keys)Shortcut.ShiftIns)
{
IDataObject iData = Clipboard.GetDataObject();
CtrlV = true;

// assemble new string and check IsValid
string newText;
newText = base.Text.Substring(0, base.SelectionStart)
+ (string)iData.GetData(DataFormats.Text)
+ base.Text.Substring(base.SelectionStart + base.SelectionLength);

// check if data to be pasted is convertable to inputType
if(!IsValid(newText))
return true;
}
if(keyData == (Keys)Shortcut.Del)
{ // OPvangen delete
this.Text = "";
digitPos = 0;
DelimitNumber = 0;
}
if(keyData == (Keys)Shortcut.CtrlC)
{ // Kopieer in clipbord, verder gebeurt er niets
CtrlV = true;
Clipboard.SetDataObject(this.Text,true);
}

return base.ProcessCmdKey(ref msg, keyData);
}

private bool IsValid(string val)
{
// testen van een volledige string, meestal via copy verkregen

bool ret = true;

if(val.Equals("") || val.Equals(string.Empty))
return ret;

// Klopt de structuurweergave
Regex regStr = new Regex(@"\d{2}/\d{2}/\d{4}");
if(!regStr.IsMatch(val))
return false;

// Test maand
int m = Int32.Parse(val.Substring(3,2));
if (m <= 0 || m > 12) return false;

// Test dagen
int d = Int32.Parse(val.Substring(0,2));
if(!CheckDayOfMonth(m,d)) return false;

// Test jaar
int j = Int32.Parse(val.Substring(6,4));
if (j < 1900 || j > 2200) return false;

return ret;
}


private void OnKeyPress(object sender, KeyPressEventArgs e)
{

DateTextBox sd = (DateTextBox) sender;
if (!CtrlV) // Geen formatering doen als we via de paste komen of indien we Ctrl-C gedrukt hebbenh
sd.MaskDate(e);

}

private void OnLeave(object sender, EventArgs e)
{
DateTextBox sd = (DateTextBox) sender;
Regex regStr;

regStr = new Regex(@"\d{2}/\d{2}/\d{4}");
if (sd.Text != "")
{
if(!regStr.IsMatch(sd.Text))
sd.Focus();
}
}


private void MaskDate(KeyPressEventArgs e)
{
int len = this.Text.Length;
int indx = this.Text.LastIndexOf("/");


if(Char.IsDigit(e.KeyChar) || e.KeyChar == '/' || e.KeyChar == 8)
{
if (e.KeyChar != 8)
{
if (e.KeyChar != '/' )
{
// Bepaal positie in de groep
if(indx > 0) // Er is al een scheidingteken
digitPos = len-indx; // Bepaal positie binnen de groep (dag/Maand/jaar)
else
digitPos++; // Poistie binnen de eerste groep

if (digitPos == 3 && DelimitNumber < 2)
{
// Maximaal 2 posities voor dag of maand
if (e.KeyChar != '/')
{
DelimitNumber++;
this.AppendText("/");
}
}


// 2e positie in dag of maand gedeelte of
if (digitPos == 2)
{

if(DelimitNumber < 2) // daggedeelte of maandgedeelte
{
if(digitPos==1) this.AppendText("0"); // zet 0 voor
this.AppendText(e.KeyChar.ToString()); // Daarna ingegeven cijfer

if(indx < 0) // Eerste deel = dag
{
if(Int32.Parse(this.Text)> 31) // check validation
{
string str;
str = this.Text.Insert(0, "0");
this.Text =str.Insert(2, "/0");
DelimitNumber++;

// Controle dagen
int d = Int32.Parse(this.Text.Substring(0,2));
if(!CheckDayOfMonth(Int32.Parse(this.Text.Substring(3,2)),d))
{
this.Text = "";
this.Select(0,0); // Om juiste positie te bepalen
digitPos = 0;
DelimitNumber = 0;
}
else
{
this.AppendText("/");
DelimitNumber++;
}
}
else // dag <= 31
{
if (Int32.Parse(this.Text) != 0)
{
// Voeg scheidingsteken toe
this.AppendText("/");
DelimitNumber++;
}
else
{
this.Text = this.Text.Substring(0,this.Text.Length - 1);
this.Select(2,0);
digitPos=1;
}
}
e.Handled=true;
}
else // Tweede (maand) deel of derde (jaar) deel
{
if( DelimitNumber == 1) // maand gedeelte
{
if(Int32.Parse(this.Text.Substring(3,2))> 12) // check validation
{
this.Text = this.Text.Substring(0,this.Text.Length - 1);
this.Select(5,0); // Om juiste positie te bepalen
digitPos=1;
e.Handled=true;
}
else
{
// Bepaal max. aantal dagen
int d = Int32.Parse(this.Text.Substring(0,indx));
if (Int32.Parse(this.Text.Substring(3,2)) == 0)
{
this.Text = this.Text.Substring(0,this.Text.Length - 1);
this.Select(4,0); // Om juiste positie te bepalen
digitPos=0;
}
else if(!CheckDayOfMonth(Int32.Parse(this.Text.Substring(3,2)),d))
{
this.Text = "";
this.Select(0,0); // Om juiste positie te bepalen
digitPos = 0;
DelimitNumber = 0;
}
else
{
this.AppendText("/");
DelimitNumber++;
}
}
e.Handled = true;
}
}
}
}
else if(digitPos == 1 && Int32.Parse(e.KeyChar.ToString())>3 && DelimitNumber==0)
{
// Eerste poistie is > 3, dan zet men er een 0 voor, aantal dagen niet > 31
if(digitPos==1) this.AppendText("0");
this.AppendText(e.KeyChar.ToString());
this.AppendText("/");
DelimitNumber++;
e.Handled = true;
}
else if(digitPos == 1 && Int32.Parse(e.KeyChar.ToString())>1 && DelimitNumber==1)
{
// Eerste poistie is > 1, dan zet men er een 0 voor, aantal maanden niet > 20
if(digitPos==1) this.AppendText("0");
this.AppendText(e.KeyChar.ToString());
// Bepaal max. aantal dagen
int d = Int32.Parse(this.Text.Substring(0,indx));
if(!CheckDayOfMonth(Int32.Parse(this.Text.Substring(3,2)),d))
{
this.Text = "";
this.Select(0,0); // Om juiste positie te bepalen
digitPos = 0;
DelimitNumber = 0;
}
else
{
this.AppendText("/");
DelimitNumber++;
}
e.Handled = true;
}
else
{
// Jaar moet in 1900 of 2000 liggen
if(digitPos == 1 && DelimitNumber==2 && e.KeyChar > '2')
e.Handled = true;
}
if( digitPos > 4)
e.Handled = true;
}
else
{ // Ingave slash
DelimitNumber++;
if (DelimitNumber == 3)
{ // de laatste verwijderen
//this.Text = this.Text.Substring(0,this.Text.Length - 1);
//this.Select(this.Text.Length,0);
DelimitNumber--;
e.Handled = true;
}
else
{
if ((DelimitNumber == 2) && (this.Text.Substring(this.Text.Length - 1,1) == "/")) // 2 na elkaar
{
//this.Text = this.Text.Substring(0,this.Text.Length - 1);
//this.Select(this.Text.Length,0);
DelimitNumber--;
e.Handled = true;
}
else
{
string tmp3;
if(indx == -1)
tmp3 = this.Text.Substring(indx+1);
else
tmp3 = this.Text;
if(digitPos == 1)
{
this.Text = tmp3.Insert(indx+1,"0");;
this.AppendText("/");
e.Handled = true;
}
}
}
}
}
else // BackSpace ingegeven char(8)
{
e.Handled = true;
if((len-indx) == 1)
{
DelimitNumber--;
if (indx > -1 )
digitPos = 2;
else
digitPos--;
}
else
{
if(indx > -1)
digitPos=len-indx-1;
else
digitPos=len-1;
}

this.Text = this.Text.Substring(0,this.Text.Length - 1);
this.Select(this.Text.Length,0);
}
}
else
{
e.Handled = true;
// opvangen return
//if (e.KeyChar == 13)
// this.OnLeave(sender, EventArgs);

}
}

private bool CheckDayOfMonth(int mon, int day)
{
bool ret=true;
if(day==0) ret=false;
switch(mon)
{
case 1:
if(day > 31 )
ret=false;
break;
case 2:
System.DateTime moment = DateTime.Now;
int year = moment.Year;
int d = ((year % 4 == 0) && ( (!(year % 100 == 0)) || (year % 400 == 0) ) ) ? 29 : 28 ;
if(day > d)
ret=false;
break;
case 3:
if(day > 31 )
ret=false;
break;
case 4:
if(day > 30 )
ret=false;
break;
case 5:
if(day > 31 )
ret=false;
break;
case 6:
if(day > 30 )
ret=false;
break;
case 7:
if(day > 31 )
ret=false;
break;
case 8:
if(day > 31 )
ret=false;
break;
case 9:
if(day > 30 )
ret=false;
break;
case 10:
if(day > 31 )
ret=false;
break;
case 11:
if(day > 30 )
ret=false;
break;
case 12:
if(day > 31 )
ret=false;
break;
default:
ret=false;
break;
}
return ret;
}
}
GeneralRe: German date Pin
haezeban17-Jun-04 11:07
haezeban17-Jun-04 11:07 
GeneralRe: German date Pin
Anonymous16-Mar-05 11:48
Anonymous16-Mar-05 11:48 
GeneralA simple example for regular expression Pin
jian-ping18-Dec-03 14:30
jian-ping18-Dec-03 14:30 

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.