herose  
     
     
 

  Visual C#中实现约束文本编辑框

黄猛 唐琳 孙明珠

摘要:介绍了如何在Visual C#中实现带约束功能的文本编辑框,以输入日期和时间格式为例,通过处理TextBox控件的KeyPress事件,来限制键盘输入,保证用户输入符合要求。
关键词:C# TextBox控件 KeyPress事件

一、 引言
    .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();
//
// textBox1
//
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下编译通过。

参考文献:
1 MSDN Library for Visual Studio 2005

(电脑编程技巧与维护杂志社版权所有。未经许可不得转载。)

 
 
 
     
     
 
2008 Copyright.《电脑编程技巧与维护》杂志社 版权所有