一、 引言
.NET提供了功能强大的Windows窗体设计器,让开发人员快速的建立所想要的用户界面,在开发过程中经常会使用到文本编辑控件TextBox,让用户进行输入。但在实际应用中,可能需要对用户的输入进行限制,例如:一个要求输入日期的文本框,在不加限制的情况下,用户输入的月份或天数可能超出实际的范围,或者输入了其它非数字的字符,这样就容易导致程序处理出现错误。虽然TextBox控件提供了validated和validating事件来验证文本框的内容,但是这种验证方式只有在用户输入完成后才能触发验证事件,来提示用户输入错误,操作性不友好。如果事先给文本框设置一个输入格式,在用户输入每个字符的过程中就对内容进行验证,不符合格式的字符拒绝输入,这样就可以保证输入内容的正确。本文以输入日期和时间为例介绍实现带约束功能的文本框。
二、 TextBox控件的KeyPress事件
1. KeyPress事件
要实现对输入的每个字符进行检查,必须获取用户键入的每一个字符,获取键盘的输入可以使用TextBox控件的KeyPress事件,该事件在控件有焦点的情况下按键时发生,该事件传递KeyPressEventArgs类型的参数,KeyPressEventArgs类有两个关键参数:
bool Handled;
char KeyChar;
Handled用来指示是否处理过KeyPress事件,处理过为true,否则为false。如果只是想处理键盘事件而不想控件接收键盘字符,则应将Handled设为true。
KeyChar用来获取或设置与按下的键对应的字符。
2. 使用事件
要在应用程序中使用事件,必须提供一个事件处理程序,该处理程序执行程序逻辑以响应事件并向事件源注册事件处理程序。例如定义一个处理KeyPress事件的程序: Void MyDateTimeEdit_KeyPress(object sender, KeyPressEventArgs e) { }
然后将事件处理程序添加到TextBox的KeyPress事件。 KeyPress += new KeyPressEventHandler(MyEdit_KeyPress);
三、 实现原理
本文以输入固定格式的日期和时间字符串为例,输入的格式必须为“2007-11-24 12:12:12”,输入的月份数值不能超过12,天数不能超过31,输入小时的数值不能超过23,分钟和秒的值不能超过59。掩码字符串:掩码为输入字符提供基本的格式,包括字符的取值、长度。本文中掩码设为“0000-00-00 00:00:00”。它代表的意义为:掩码中为0的字符是允许用户更改的,且必须为数字,其它字符固定不可更改,用户输入的字符串长度不能大于掩码的长度。
参照字符串:参照字符串为输入的字符提供位置参考,格式为“____-__-__ __:__:__”,当输入的字符处于参照字符串的‘_’位置时允许用户继续输入,否则系统直接输出该字符,不需用户键入。
输入字符串:用来记录用户输入的合法字符,格式与掩码相同,但是随用户的输入而更改。
实现步骤:
(1)初始化掩码字符串、参照字符串和输入字符串,输入字符串初始值等于掩码字符串。
(2)等待用户输入。
(3)触发KeyPress事件,获得用户在文本框中输入的位置x和字符c。
(4)在掩码中找到第x个字符m,如果字符m为0,则判断字符c是否为数字,如果不是数字,则返回到(2)重新执行,否则用字符c替换输入字符串的第x个字符。
(5)将输入位置x向后移动一个位置,获得新位置y=x+1,然后判断参照字符串中第y个字符p是否为“_”,如果是执行步骤(2),否则将字符p作为参数触发KeyPress事件,执行步骤(3)。
(6)循环执行上述过程,直到用户输入字符串的长度等于掩码长度。
四、 实现代码
1.在Visual C#开发环境中创建一个基于Windows的应用程序,工程名为“EditMask”。
2.添加一个新类,命名为“MyDateTimeEdit”,然后将基类改为“System.Windows.Forms.TextBox”,这样新类可以具有TextBox的所有功能,新类的定义如下: using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Media; namespace EditMask { class MyDateTimeEdit : System.Windows.Forms.TextBox { } }
3. 为MyDateTimeEdit类添加KeyPress事件: void MyEdit_KeyPress(object sender, KeyPressEventArgs e);
然后添加KeyPress事件: KeyPress += new KeyPressEventHandler(MyEdit_KeyPress);
为了使控件只显示输入字符串,不自动接受字符处理,将Handle变量设为true。
MyDateTimeEdit类的实现代码如下: using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Media; namespace EditMask { //该类从TextBox继承 class MyDateTimeEdit : System.Windows.Forms.TextBox { public bool m_bMaskKeyInProgress; public bool m_bUseMask; //是否启用约束编辑功能 public bool m_bIsDatetime; //按照日期时间格式约束 public String m_strMask; //掩码字符串 public String m_strLiteral; //参照字符串 public String m_strMaskLiteral; public String m_strHours; //小时的参考变量 public String m_strMins; //分钟的参考变量 public String m_strSeconds; //秒的参考变量 public StringBuilder m_str; //输入字符串 public MyDateTimeEdit() { KeyPress += new KeyPressEventHandler(MyEdit_KeyPress);//添加KeyPress事件 //初始化变量 m_bMaskKeyInProgress = false; m_bUseMask = true; m_bIsDatetime = true; m_strHours = "23"; m_strMins = "59"; m_strSeconds = "59"; m_strMask = "0000-00-00 00:00:00"; //初始化掩码字符串格式 m_strLiteral = "____-__-__ __:__:__"; //初始化参照字符串格式 m_str = new StringBuilder("0000-00-00 00:00:00", 19);//初始化输入字符串 m_strMaskLiteral = m_str.ToString(); Text = m_str.ToString(); //显示输入字符串 this.Select(0, 0); } //KeyPress事件处理方法 private void MyEdit_KeyPress(object sender, KeyPressEventArgs e) { e.Handled = true; //将Handled设为true,不处理键入的字符 char nChar = e.KeyChar; //获取键入的字符 char c; if (!m_bMaskKeyInProgress) { if (!CheckChar(nChar)) //判断输入的字符是否符合格式 return; } if (m_bUseMask) { if (!Char.IsControl(nChar))//判断输入字符是否控制键 { //获取当前选择文本的起始和结束位置 int startPos, endPos; startPos = SelectionStart; endPos = SelectionStart + SelectionLength; Select(startPos, endPos + 1); m_str[endPos] = nChar; //更新输入字符串 } else if (nChar == (char)Keys.Back)//处理BackSpace键 { int startPos, endPos; startPos = SelectionStart; endPos = SelectionStart + SelectionLength; //如果按下回退键,则显示掩码字符串的相应字符 if ((startPos == endPos) && (startPos >= 1) && (startPos <= m_str.Length)) { if (m_strMaskLiteral != "") { c = m_strMaskLiteral[startPos - 1]; SelectionStart = startPos - 1; SendChar(c); SelectionStart -= 1; } } else MessageBeep();//播放警告音 return; } } int selectionStart = SelectionStart; Text = m_str.ToString();//显示输入字符串 SelectionStart = selectionStart + 1;//光标后移一位 Select(SelectionStart, 0); if (!m_bMaskKeyInProgress && m_bUseMask && m_strLiteral != "") { int startPos, endPos; startPos = SelectionStart; endPos = SelectionStart + SelectionLength; // 判断输入位置是否在掩码位,否则直接输出该字符 if (endPos < m_strLiteral.Length) { c = m_strLiteral[endPos]; if (c != '_') SendChar(c); } } } //检测输入字符是否符合要求 private bool CheckChar(char nChar) { char c; if (!m_bUseMask) return true; // 是否控制键 if (Char.IsControl(nChar)) return true; // 取消选择 int startPos, endPos; startPos = SelectionStart; endPos = SelectionStart + SelectionLength; Select(startPos, 0); // 输入位置大于掩码长度,播放警告音 if (endPos >= m_strMask.Length) { MessageBeep(); return false; } c = '_'; if (m_strLiteral != "") c = m_strLiteral[endPos]; if (c != '_') { SendChar(c); startPos = SelectionStart; endPos = SelectionStart + SelectionLength; } c = m_strMask[endPos]; bool doit = true; switch (c) { case '0'//只接受数字键输入 { doit = true; if (Char.IsDigit(nChar))//判断输入字符是否为数字 { if (m_bIsDatetime) { //如果输入月份值,则判断是否在正确范围内,输入范围不能大于12 if (endPos == 5) { if (nChar > '1') doit = false; } if (endPos == 6) { if (m_str[5] == '1') { if (nChar > '2') doit = false; } } //如果是输入天数,范围不能大于31 if (endPos == 8) { if (nChar > '3') doit = false; } if (endPos == 9) { if (m_str[8] == '3') { if (nChar > '1') doit = false; } } //输入的小时范围不能超过23 if (endPos == 11) { if (nChar > m_strHours[0]) doit = false; } if (endPos == 12) { if (m_str[11] == m_strHours[0]) { if (nChar > m_strHours[1]) doit = false; } } //分钟的范围不能超过59 if (endPos == 14) { if (nChar > m_strMins[0]) doit = false; } if (endPos == 15) { if (m_str[14] == m_strMins[0]) { if (nChar > m_strMins[1]) doit = false; } } //秒的范围不能超过59 if (endPos == 17) { if (nChar > m_strSeconds[0]) doit = false; } if (endPos == 18) { if (m_str[17] == m_strSeconds[0]) { if (nChar > m_strSeconds[1]) doit = false; } } } return doit; } break; } } //不正确输入,警告音提示 MessageBeep(); return false; } //以字符c触发KeyPress事件 private void SendChar(char c) { m_bMaskKeyInProgress = true; KeyPressEventArgs e = new KeyPressEventArgs(c); MyEdit_KeyPress(this, e); m_bMaskKeyInProgress = false; } //播放警告音 private void MessageBeep() { SystemSounds.Beep.Play(); } } }
4.添加一个“TextBox”控件,命名为“textBox1”,在Form1类中打开“InitializeComponent()”函数,将控件类型修改为“MyDateTimeEdit”,并初始化文本,修改结果见代码粗体部分: private void InitializeComponent() { this.textBox1 = new EditMask.MyDateTimeEdit(); this.SuspendLayout(); this.textBox1.Location = new System.Drawing.Point(66, 68); this.textBox1.Name = "textBox1"; this.textBox1.Size = new System.Drawing.Size(155, 21); this.textBox1.TabIndex = 0; this.textBox1.Text = "0000-00-00 00:00:00"; … }
5.程序运行结果见图1。
图1 带约束功能的文本框
本文代码在Visual Studio 2005下编译通过。
|