本文仅针对MFC环境下的C++语言编写的基于对话框的程序的EditControl控件的有限位数的十进制的有理数的小数形式的数据的输入控制的鲁棒性进行讨论,如果没有特殊需求,请直接修改EditControl的Number属性。
首先来看看有理数。有理数分为正有理数、负有理数和0。
正有理数分为正整数、正小数
负有理数分为负整数、负小数
那么对有限位有理数的小数形式的输入控制基本可以从以下几个方面考虑:
1.输入的按键是否表示数字、负号、小数点;
2.负号与小数点对1个数据而言只能各存在1个;
3.负号只能在首位输入,而小数点能不能在首位输入;
4.-0的输入;
5.输入长度的控制。
EditControl的Number属性是一个非常鲁棒的属性,但有着很大的局限性,即这个属性会使EditControl只能输入0或正整数。所以这个属性对于一个有限位数的十进制的有理数的小数形式的数据的输入来说,其属性值必须是False。
接下来,是时候展现真正的技术了。
首先,你需要做一个MFC的基础界面,更改所需要控制的EditControl的ID号,这里以IDC_EDIT_IMPUTMONEY为例(因为当时做账本用的ID是这个),之后设置一个变量,这里以m_ImputMoney为例(因为当时做账本用的变量是这个),开启类视图,点击XXXDlg(这里以AccountBookDlg为例),重写PreTranslateMessage函数。
接下来要判断输入焦点是不是在所需要被控制的EditControl框内,如果不是则不控制,如果是,那么判断是否有按键输入,有输入再进行控制,具体代码是:
if( GetFocus() == GetDlgItem ( IDC_EDIT_IMPUTMONEY ) && pMsg->message == WM_CHAR )
//判断(输入焦点 是否是 ID为IDC_EDIT_IMPUTMONEY的控件)及(按键信息 是否是 字符)
然后是对上面5点的逐一解决:
1.如何判断按键是否表示数字、负号、小数点?简单粗暴直接判断。
if( ((pMsg->wParam) >='0' && (pMsg->wParam<='9')) || (pMsg->wParam) =='-' || (pMsg->wParam)==VK_BACK || (pMsg->wParam) =='.' )
{}
2.负号与小数点如何对1个数据而言只能各存在1个?
这个问题需要同后面的问题一同考虑。如果仅仅考虑数量的话,可以有以下几种做法(以小数点为例):
①设置BOOL型变量Flag,若其值为FLASE则无法输入小数点,若为TRUE则允许输入。但是这么做会产生一个新的问题,当输入小数点后,Flag值变为TRUE,然后按下退格键删去小数点,Flag若不变,则仍然无法重新输入小数点,这显然不够鲁棒。而如果令Flag对退格键的消息做出响应,则又需要改动步骤1中判断按键消息中加入是否为退格键,显得蛋疼,故弃之。
②读取已经输入的字符,查找并判断是否存在小数点,若无,则输;若有 ,则禁。相当简单粗暴,具体代码如下:
if(pMsg->wParam == '.')
{
CString str;
// 获取原来编辑框中的字符串
m_ImputMoney.GetWindowText(str);
//判断是否已经输入了小数点,若已经有输入,则不输入该小数点
if(str.Find('.')!=-1)
{
return TRUE;
// PreTranslateMessage函数的返回值为BOOL型
}
//否则就输入这个小数点
else
{
return CDialogEx::PreTranslateMessage(pMsg);
}
}
③见后文。
3.如何实现负号只能在首位输入?而小数点能不能在首位输入?小数点的首位输入其实司空见惯,比如计算器直接输入小数点时会默认整数位为0,校园卡的刷卡机也是同理,让小数点在首位可输入不仅鲁棒而且方便快捷,更重要的是,它不需要其他的任何代码,只需要将输入的字符改成double型的数据即可自动实现,也就是一个atof的事,故不做多余处理。而负号的首位输入则有以下2种考虑:
①判断输入的字符长度是否大于0,若大于0,则说明已经键入数值,不应输入负号。然而这样会导致一个BUG发生,就是在键入数值后,将光标移动到首位位置,继续输入负号时无法输入的问题。于是便有了第②种思路。
②判断光标选取首位置及末位置。这里需要引入一个函数GetSel用于判断光标选取的位置。详见代码:
if( pMsg->wParam == '-' )
{
CString strImput;
m_ImputMoney.GetWindowText(strImput);
// 还没有输入任何字符串
if(strImput.IsEmpty())
{
return CDialog::PreTranslateMessage(pMsg);
}
else
{
int nSource,nDestination;//用于记录光标位置
m_ImputMoney.GetSel(nSource,nDestination);//目测为引用传递
// 若此时选择了全部的内容,则可以输入负号
if(nSource==0)
{
return CDialog::PreTranslateMessage(pMsg);
}
//否则不输入
else
{
return TRUE;
}
}
}
同理,问题2中的小数点输入的控制也可以使用相同的方法,通过判断光标位置或选取位置的不同,来决定是否键入,具体代码不给出。
4.若输入-0将会如何?其实并没有什么卵用。−0代表负零,一个计算机科学中存在的数字。主要用来表达浮点数和在某些对整数进行有符号数处理。在数学中,一般没有正零或负零的概念,−0在逻辑上表示0的相反数,等于0。 所以如果输入的结果是-0,可以在判断后将其值改为0.0,其实不改也并没有什么关系,并不太可能会恶化鲁棒性。
5.如何进行数据输入的长度判断?double(双精度浮点数)使用 64 位(8字节) 来储存一个浮点数。 它可以表示十进制的15或16位有效数字,负值取值范围为 -1.7976E+308 到 -4.94065645841246544E-324,正值取值范围为 4.94065645841246544E-324 到 1.797693E+308。这个其实说简单就更简单了,输到15位之后不让输入就好了。但话说回来,毕竟取值范围的后几位不是0,具体怎么控制还有待开发,总之目前卡死在10位够用就行(反正不影响鲁棒性,只是限制了输入范围→_→)。