在本任务中,我们要分别实现基本算术运算、累加和计算、阶乘计算、三角函数计算、排列组合计算和对数计算等多个子任务。 3.1.1 计算器的实验版本 1. 程序界面设计 (1)新建工程
打开VB开发环境,在工程浏览器窗口中(见图3-1左),将工程名称改为“计算器”(如图3-1中);再将窗体名称改为“frmCalculator”如图3-1右)。
图3-1 改变工程和窗体名称
将窗体文件保存为“frmCalculator.frm”,工程文件保存为“prjCalculator”。 (2)添加控件
在本工程中,我们需要用到下列控件:1个文本框用于输入运算数和输出结果;16个按钮构成计算器键盘,其中10个用于输入10个数字字符,1个用于输入小数点,一个用于触发计算的等号,另外4个用于选择加、减、乘、除运算符。
首先在窗体上部添加一个文本框,默认名称是Text1,调整好大小和位置(如图3-2),并将属性Text的值清空,再将对齐方式Alignment改成“1 -
Right Justify”右对齐;
图3-2 添加文本框控件
再来制作键盘,第1步,添加第1个按钮。在文本框下方添加一个按钮Command1,将它调整为一个按键般大小,并把Caption属性改成“1”。
第2步,添加第2个按钮。添加外形类似的按钮,用复制的方法即可。不过要注意,在粘贴时VB会询问“已经有一个控件为\"Command1 \"。创建一个控件数组吗?”(见图3-3),一定要回答“否”。将复制好的按钮Command2的Caption改成2,并移动到Command1的右边。
图3-3 创建控件数组询问对话框
第3步,重复进行粘贴操作,依次制作其它按钮,按图3-4的布局排列。前9个按钮的Caption改成与它们的顺序号相同,Command10的Caption改为“0”,Command11的Caption改为“.”,ommand12的Caption改为“=”,Command13~Command16的Caption依次改为“+”、“-”、“*”、“/”。
图3-4键盘制作 2. 程序代码编写 (1)数字按钮的处理
数字按钮的功能是,在单击按钮后,将对应的数字加入到文本框Text1中。
Private Sub Command1_Click() Text1.Text = \"1\" End Sub
测试一下,启动程序,连续单击按钮1,文本框中只会出现1个1。 (2)错误的发现与修正
赋值语句中新的数据总会替换掉原有的内容。 以下是解决办法:
Private Sub Command1_Click() Text1.Text = Text1.Text & \"1\" End Sub
下面,再来接着写Command2的事件过程代码: Private Sub Command2_Click() Text1.Text = Text1.Text & \"2\" End Sub
仿照上述做法,再写出其它8个数字按钮和小数点按钮的单击事件过程代码。
(3)Caption的利用
上述语句最后拼接的数据正好是被单击按钮的Caption。 Private Sub Command1_Click()
Text1.Text = Text1.Text & Command1.Caption '输入1
End Sub
Private Sub Command2_Click()
Text1.Text = Text1.Text & Command2.Caption '输入2 End Sub „„„„
Private Sub Command11_Click()
Text1.Text = Text1.Text & Command11.Caption '输入小数点 End Sub
(4) 运算符按钮的处理 1)运算数的暂存
Dim sglNumber As Single '用于暂存第1个运算数,前缀sgl是Single的缩写
sglNumber = Text1.Text '保存第1个运算数,字符串自动转换称为单精度型
如果单击加法运算符,则单击事件过程如下: Private Sub Command13_Click() '单击加法运算符按钮 Dim sglNumber As Single '用于暂存第1个运算数的变量 sglNumber = Text1.Text '保存第1个运算数 Text1.Text = \"\" '用空串赋值,即可清空文本框 End Sub 2)加法运算代码
在输入第2个运算数之后,需要单击等号按钮来完成运算。
Private Sub Command12_Click() '单击等号按钮
Text1.Text = sglNumber + Val(Text1.Text) '取出第2个数与第1个数做加法并输出结果 End Sub
试运行程序,依次单击按钮5—加号—按钮6—等号,结果是 6? 调用MsgBox来找一下原因:
Private Sub Command12_Click() '单击等号按钮 MsgBox sglNumber '弹出变量的值
Text1.Text = sglNumber + Val(Text1.Text) '做加法运算并输出 End Sub
对话框中弹出空串!
流程再往后退,检查一下运算符按钮的单击事件过程。 Private Sub Command13_Click() '单击加法运算符按钮 Dim sglNumber As Single sglNumber = Text1.Text MsgBox sglNumber '弹出变量的值 Text1.Text = \"\" End Sub
单击加号按钮后,对话框弹出了5,运行正确,错误不在本过程。 (5)程序调试方法
最基本的3种程序调试方法。
1)在适当的地方加上MsgBox调用,通过弹出变量的当前值来进行
观察。
2)在适当位置加上中断标记(如图3-5),让程序运行到指定的位置暂停,然后利用中断的环境来检查变量当前的值。
图3-5 添加调试断点
流程到达断点语句行时会暂时中断,将鼠标悬停在变量名上,下方就会显示“变量名=变量值”的提示(见图3-6)。
图3-6 程序中断状态下查看变量值
注意,当流程暂停在中断语句处时,该语句尚未被执行。 按下【F8】键,程序可以单步执行。
3)借助于专用调试对象Debug,在适当的位置加上Debug对象的打印方法:
Debug.Print <变量名列表>[;][,]
在立即窗体中打印出变量的值供观察分析。 3. 变量的作用范围
程序中的变量是有作用范围的,它们的作用范围与其生命周期有着密切的关系。
(1)变量的生命周期
如果变量声明语句写在一个过程之内,那么,只有当程序流程进入这个过程并遇到该声明语句时,变量才会被分配内存单元,而当程序执行到End
Sub语句,也就是过程结束的同时,变量就被系统从内存中清除。在过程中声明的变量,称为过程级变量。
变量声明语句还可以写在过程之外,通常是写在模块中所有过程前面。这种变量称为模块级变量。模块级变量从文件被加载时出生,当文件被卸载或程序被结束时消亡。对于窗体文件中的模块级变量来说,它们的生命与窗体的寿命一样长,只要窗体对象未被卸载,模块级变量就始终存活。 (2)变量的作用范围
因为过程级变量只能在自身所在的过程之中存活,所以它们的作用范围就仅限于自身所在的过程,所以过程级变量也称局部变量。 模块级变量可以被本模块中的任何过程所访问,它的作用范围是整个模块。对于一个模块中的所有过程而言,模块级变量就是一种全局变量。
如果模块级变量被声明为Public(公共变量),那么,在整个工程范围之内,变量都是可访问的,只是访问方法有所不同。 4. 程序代码的改进 (1)加法运算代码
根据变量作用范围,我们将第1个运算数的暂存变量sglNumber的声明语句放在了所有过程之外,让它成为模块级变量,这样所有过程中的语句就都可以访问这个变量了。 Option Explicit
Dim sglNumber As Single '模块级变量,用于暂存第1个运算数
Private Sub Command13_Click() '单击加法按钮 sglNumber = Text1.Text '保存第1个运算数 Text1.Text = \"\" '用空串赋值,即可清空文本框 End Sub
Private Sub Command12_Click() '单击等号按钮
Text1.Text = sglNumber + Val(Text1.Text) '做加法运算并输出 End Sub
Rem 以下省略了所有数字按钮和小数点按钮的单击事件过程代码 (2)通用运算代码
要能够进行四则运算,在单击等号按钮时,程序必须知道用户单击的是哪一个运算符,才能选用相应的计算公式。因此必须在用户单击运算符按钮时记下按钮的标记,到后续计算时才能够作出正确的选择。保存按钮标记的变量必须是模块级变量。 Option Explicit
Dim sglNumber As Single '模块级变量,用于暂存第1个运算数 Dim strOperator As String '模块级变量,用于保存运算符标记 Private Sub Command13_Click() '单击加法按钮 sglNumber = Text1.Text '保存第1个运算数
strOperator = Command13.Caption '保存运算符标记 Text1.Text = \"\" End Sub
Private Sub Command14_Click() '单击减法按钮
sglNumber = Text1.Text '保存第1个运算数
strOperator = Command14.Caption '保存运算符标记 Text1.Text = \"\" '清空文本框 End Sub
Private Sub Command15_Click() '单击乘法按钮 sglNumber = Text1.Text '保存第1个运算数
strOperator = Command15.Caption '保存运算符标记 Text1.Text = \"\" '清空文本框 End Sub
Private Sub Command16_Click() '单击除法按钮 sglNumber = Text1.Text '保存第1个运算数
strOperator = Command16.Caption '保存运算符标记 Text1.Text = \"\" '清空文本框 End Sub
Private Sub Command12_Click() '单击等号按钮
If strOperator = \"+\" Then Text1.Text = sglNumber + Val(Text1.Text) '加法
If strOperator = \"-\" Then Text1.Text = sglNumber - Val(Text1.Text) '减法
If strOperator = \"*\" Then Text1.Text = sglNumber * Val(Text1.Text) '乘法
If strOperator = \"/\" Then Text1.Text = sglNumber / Val(Text1.Text)
'除法
Text1.SetFocus Text1.SelStart = 0
Text1.SelLength = Len(Text1.Text) End Sub
Rem 以下省略了所有数字按钮和小数点按钮的单击事件过程代码 5. 多路选择语句
程序设计语言都提供了一种多路选择处理结构,或者称多路分支结构语句,在VB中多路选择语句叫Select Case语句,它的语法格式如下:
Select Case <匹配条件表达式> Case <情形1> <语句块1> [Case <情形2> <语句块2> ……
[Case <情形n> <语句块n>] [Case Else <语句块n+1>]] End Select
图3-7 多路选择语句流程
这是一种1个入口多个出口的结构(见图3-7),哪个条件匹配,就从哪个出口通行。
Case的数量是可选的,根据程序需要而定。若只有1个Case,就相当于If-Then-End If结构;如果只有1个Case和Case Else,就等价于If-Then-Else-End If结构。
匹配条件表达式可以是一个数值型、字符串型、日期型或逻辑型表达式。语句执行时,先将表达式的值计算出来,再将计算结果与下面各Case关键字后的数据进行比较,来检查是否匹配。
每个Case后列出满足该种情形的条件,可以是1个单一的数据,也可以是多个连续或不连续的数据,或者这些数据的组合表达式,例如: Case 2 Case 1, 3, 5
Case 1 To 10, 12 '1到12,不包含11
Case Is>1 And Is< 5 '1到5区间的数,不包含1和5 Case \"Saturday\" Or \"Sunday\" 6. 代码的优化
利用Select Case语句,对运算符选择部分进行优化,代码如下: Private Sub Command12_Click() '单击等号按钮 Select Case strOperator Case \"+\"
Text1.Text = sglNumber + Val(Text1.Text) '加法
Case \"-\"
Text1.Text = sglNumber - Val(Text1.Text) '减法 Case \"*\"
Text1.Text = sglNumber * Val(Text1.Text) '乘法 Case \"/\"
Text1.Text = sglNumber / Val(Text1.Text) '除法 End Select Text1.SetFocus Text1.SelStart = 0
Text1.SelLength = Len(Text1.Text) End Sub 6. 知识点小结 ◆
程序调试——程序调试最基本的基本方法,可以通过设置断点或者弹出对话框来观察变量的当前值,以便确定错误是在断点之前还是之后,断点可以设置多处。调试完成后,应该将断点删除。 ◆
变量的作用范围——变量是有作用范围的,它们的作用范围与其生命周期有着密切的关系。过程级变量的生命周期随过程的结束而终止,所以其作用范围仅限于过程之内;而模块级变量的作用范围可以是整个模块,只要模块没有被卸载,变量就存活着,因此可以被模块中所有的过程访问。
◆ 多路选择语句——这种语句结构可以在需要多选一的情况下使用,能够代替If选择语句结构,简化If的结构嵌套,提高程序执行速度。
3.1.2 四则运算计算器 1. 控件数组的引入 (1)数组的概念
◆ 数组——数组是一组同名变量。这组同名变量可以用一个索引(Index)来给它们编号,索引通常也称下标。
有了数组,在批量访问数组成员时,就不需要重复书写这些变量的名字,而只要改变它们的下标即可。数组与循环结构相结合,将使得程序代码大大简化。 (2)控件数组的概念
既然数组是一组变量,那么它也拥有各种数据类型,包括对象型。控件是一种对象,所以也可以被定义成数组。 ◆
控件数组——与数组定义相似,一组同名的控件就是控件数组,数组中的每一个控件都称为数组的成员。例如Command(1),Command(2),„就是控件数组Command的成员。
使用控件数组的好处是,数组中的所有控件可以共用同一个事件过程。根据它们的下标来确定当前引用的是哪个控件。 2. 界面的设计 (1)新建工程
工程名:“计算器”,窗体名:frmCalculator,窗体的MaxButton属性:False。 (2)添加控件
分别将表3-1中所列的控件添加到窗体。 表3-1 添加控件的类型与属性
控件类型数量NameCaptionAlignment用途 TextBox1txtShow 1 - Right Justify输入输出 CommandButton10cmdNumber0,1,2,„,9 输入数字 CommandButton1cmdDot. 输入小数点 CommandButton1cmdEquals= 执行运算 CommandButton4cmdOperator+, -, *, / 运算符
1)数字按钮的添加
首先添加第1个数字按钮,将它的名称改成“cmdNumber”。再将它的Caption改成“0”。
然后利用复制-粘贴的方法来添加另外9个数字按钮。
这次粘贴时,在弹出对话框询问“已经有一个控件为\"cmdNumber\"。创建一个控件数组吗?”的时候,需要回答“是”。这样粘贴好一个按钮后,会发现它的名称自动变成了“cmdNumber(1)”,而第1个按钮名称,已经变成了“cmdNumber(0)”。这表示VB为同名的按钮控件自动创建了一个按钮控件数组。现在将第2个按钮cmdNumber(1)的Caption改成“1”,然后移动到适当的位置„„
在VB中,数组的默认下标(索引)是从0开始的,因此控件数组第1个成员的下标为0,第2个成员的下标为1,„„,以此类推。 控件数组成员的下标是存放在Index属性中的。 2)运算符按钮的添加
接下来,添加第1个运算符按钮,将按钮的名称改成“cmdOperator”,然后进行复制并粘贴3次,产生了另外一个按钮数组cmdOperator(0) ~
cmdOperator(3)。最后将它们的Caption按次序分别改成“+”、“-”、“*”和“/”。 3)添加其它控件
最后再添加一个文本框,将它的名称改为“txtShow”;一个小数点按钮,名称改为“cmdDot”;和一个等号按钮,名称改为“cmdEquals”。并把它们安排在图3-8所示的位置。
图3-8 控件位置布局 3. 数字按钮代码的编写 (1)最基本的代码
双击任意一个数字按钮,进入到代码编辑窗口,看到新的事件过程框架变成了这样:
Private Sub cmdNumber_Click(Index As Integer) End Sub
其中参数Index是VB自动获取的按钮控件数组的下标,当单击
cmdNumber(n)的时候, Index值就是n。
通过Index的值可以知道了用户单击的是哪个按钮:
Private Sub cmdNumber_Click(Index As Integer) '单击任一个数字按钮
txtShow.Text = txtShow.Text & Index '将按钮的Caption插入文本框 End Sub
如果先单击按钮0,再单击其它数字按钮时,文本框中会出现前导0,不符合书写惯例,所以应该设法把它解决掉。 (2)前导0问题的处理
当单击按钮0的时候,先判断一下它是否为文本框中的第一个数字,如果是,就不将这个0写入文本框。
If txtShow.Text <> \"\" Then txtShow.Text = txtShow.Text & Index
试运行一下,现在所有按钮都不起作用了!
这个事件过程是所有数字按钮所共用的,本想屏蔽前导0的代码把所有数字按钮都给屏蔽了!
必须首先判断单击的是否为按钮0,如果是0同时又是第1个字符,才需要屏蔽。
Private Sub cmdNumber_Click(Index As Integer)
If Index = 0 Then '当单击的是按钮0时就执行下面的语句 If txtShow.Text <> \"\" Then txtShow.Text = txtShow.Text &
Index End If End Sub
再测试一下,还是所有按钮都不起作用!
我们所给出的逻辑是,当单击的是按钮0时,才可能进入If结构。因此单击其它数字按钮时根本没有执行任何语句,过程就结束了。 正确代码如下:
Private Sub cmdNumber_Click(Index As Integer)
If Index = 0 Then '当单击的是按钮0时就执行下面的语句 If txtShow.Text <> \"\" Then txtShow.Text = txtShow.Text & Index
Else '当单击其它数字按钮时应执行的语句 txtShow.Text = txtShow.Text & Index End If End Sub
4. 小数点按钮代码的编写 Private Sub cmdDot_Click()
txtShow.Text = txtShow.Text & \".\" End Sub
(1)小数点代码查错
改变小数点与数字按钮的输入次序,并且多单击几次小数点按钮,就可以发现2个问题:第1个,如果小数点作为最先输入的符号时,左
边应该自动附加一个0,而现在不能做到;第2个,小数点按钮可以单击多次并会在文本框中插入多个小数点符号。 (2)小数点前自动加0
先来处理小数点前自动加0的问题。小数点按钮的处理代码原本是这样写的:
Private Sub cmdDot_Click()
txtShow.Text = txtShow.Text & \".\" End Sub
要实现自动加0,可以先判断一下文本框是否为空,如果是,就在小数点前面添加一个0,否则就直接加入一个小数点。 Private Sub cmdDot_Click() If txtShow.Text = \"\" Then
txtShow.Text = \"0.\" '如果小数点是首字符,就先写入\"0.\" Else
txtShow.Text = txtShow.Text & \".\" '如果小数点不是首字符,则写入\".\" End If End Sub
想一想,还有没有其它更好的写法? Private Sub cmdDot_Click()
If txtShow.Text = \"\" Then txtShow.Text = \"0\" '如果小数点是首字符,则先写入0
txtShow.Text = txtShow.Text & \".\" '无论是否首字符,都要加入小数点 End Sub
这样看起来是不是更简练? (3)避免小数点多次输入
先判断文本框中是否已经存在一个小数点,如果存在,则不再将小数点插入文本框。
◆ 取整函数Int (x)——返回不大于x的最大整数。 例如:Int (12.3) = 12;而Int (-12.3) = -13。
可以使用这个函数来判断文本框中的数据是否为整数,如果不是,说明含有小数点;如果 Int(txtShow.Text) <>
Val(txtShow.Text),说明txtShow.Text为小数表达式。不过当小数点位于文本框最右边的时候,上述判断就会失灵。 ◆ 字符串搜索函数InStr(i ,s1, s2)
——本函数常用参数有3个,i是一个正整数表达式,s1和s2,都是字符串表达式。函数的功能是:在一个字符串s1中,从第i个字符开始,搜索是否存在另一个字符串s2,如果存在,就返回s2在s1中第一次出现的位置值。例如: InStr (1, \"ABCDABC\返回1; InStr (1, \"ABCDABC\返回4。
如果在第一个字符串s1中没有搜索到第二个字符串s2,则函数返回0,例如:
InStr (5, \"ABCDABC\返回0。
第1个参数i可以省略,如果省略,则i的默认值为1。 利用Instr函数处理后的代码如下: Private Sub cmdDot_Click()
If InStr(txtShow.Text, \".\") = 0 Then '如果小数点不存在
If txtShow.Text = \"\" Then txtShow.Text = \"0\" '如果小数点是首字符,则先写入0
txtShow.Text = txtShow.Text & \".\" '添加一个小数点 End If End Sub
5. 运算符按钮的处理 (1)运算数的暂存 先声明2个模块级变量:
Dim sglNumber As Single '用于暂存第1个运算数 Dim intOperator As Integer '用于存放运算符的下标
按钮数组,,和的Index分别是0,1,2,3,所以根据下标就可以知道单击的是哪一个按钮。
Private Sub cmdOperator_Click(Index As Integer) intOperator = Index
sglNumber = Val(txtShow.Text) End Sub
(2)第2个运算数的问题
最简单的做法是,在当单击运算符按钮之后,先清空文本框,再输入第2个运算数。
可是常见的计算器在单击运算符之后,第1个运算数还是被保留在文本框当中。
在单击数字按钮之前,可以根据运算符按钮的状态来决定下面该做什么,如果运算符按钮被单击过,在输入数据前就先清空文本框,否则就直接做字符拼接操作。
可以使用一个模块级的布尔变量来记忆运算符按钮的状态。如果单击过,就给它赋值True,否则赋值False。
Dim blnOperatorClicked As Boolean '用于记忆运算符是否被单击过 然后在运算符按钮单击事件过程开始插入一行代码: blnOperatorClicked = True '记录本按钮是否被单击过 最后在数字按钮单击事件过程的第1行插入下列语句:
If blnOperatorClicked = True Then txtShow.Text = \"\" '如果单击过运算符,则清空文本框
现在来调试一下程序,发现在单击过运算符按钮后文本框中只能输入一位数字。
由于变量blnOperatorClicked在声明时已经被初始化为False,所以输入第1个运算数时没有遇到清空文本框的动作,而当单击过运算符按钮后,其的值变成了True,因此之后每输入一个字符之前,都会做文本框清空操作。
应该在适当的时候让blnOperatorClicked 还原成False。
Private Sub cmdNumber_Click(Index As Integer) '单击数字按钮事件过程
If blnOperatorClicked = True Then
txtShow.Text = \"\" '如果单击过运算符,则在输入第2个数前清空文本框
blnOperatorClicked = False '然后取消运算符被单击过的状态 End If
If Index = 0 Then '当单击的是按钮0时就执行下面的语句 If txtShow.Text <> \"\" Then txtShow.Text = txtShow.Text & Index
Else '否则执行下面的语句
txtShow.Text = txtShow.Text & Index End If End Sub
在小数点按钮单击事件过程中也要加入同样的处理,因为第2个运算数也可能会含有小数点的:
Private Sub cmdDot_Click() '单击小数点按钮事件过程 If blnOperatorClicked = True Then
txtShow.Text = \"\" '如果单击过运算符,则在输入第2个数前清空文本框
blnOperatorClicked = False '然后取消按过运算符的状态 End If
If InStr(txtShow.Text, \".\") = 0 Then '如果小数点不存在
If txtShow.Text = \"\" Then txtShow.Text = \"0\" '如果小数点是首字符,则先写入0
txtShow.Text = txtShow.Text & \".\" '添加一个小数点 End If End Sub
6. 运算代码的编写
单击等号后要做的事情有3件:根据最近单击的运算符状态来选定一种运算公式;然后将暂存的第1个运算数与文本框中的第2个运算数做相应的运算;最后在文本框中输出运算结果。 Private Sub cmdEquals_Click()
Select Case intOperator '需匹配的条件表达式为 intOperator Case 0 ' intOperator=0, 做加法
txtShow.Text = sglNumber + Val(txtShow.Text) Case 1 ' intOperator=1, 做减法
txtShow.Text = sglNumber - Val(txtShow.Text) Case 2 ' intOperator=2, 做乘法
txtShow.Text = sglNumber * Val(txtShow.Text) Case Else ' intOperator=3, 做除法
txtShow.Text = sglNumber / Val(txtShow.Text) End Select End Sub
7. 过程的调用方法
模块中已存在的过程或函数,都是可以重复使用的,称为代码重用。在一个过程中利用另一个过程或者函数,称为过程或函数的调用。过程调用的方法与函数调用有所不同。过程调用的一般语法格式是: [Call] <过程名称[ (参数列表) ]>
其中调用关键字Call可以省略,如果没有参数,括号也可以省略。 程序执行到过程调用语句时,转到指定的过程并将过程中的语句完整地执行一遍,然后返回到调用语句的下一行继续执行。调用语句所在的过程称为主调过程,而被调用的过程称为被调过程。 8. 连续运算的处理
在处理运算符的代码之前加上等号按钮的处理代码即可。
Private Sub cmdOperator_Click(Index As Integer) '单击运算符按钮事件过程
If blnOperatorClicked = False Then cmdEquals_Click '调用等号按钮单击事件过程 intOperator = Index
sglNumber = Val(txtShow.Text) blnOperatorClicked = True End Sub 9. 增加删除功能 (1)按钮的添加
按表3-2的描述在窗体添加3个按钮。
表3-2 新增按钮描述
控件类型NameCaption用途
CommandButtoncmdBackSpace←退格删除一个字符 CommandButtoncmdClearC清除文本框 CommandButtoncmdMinus+/-输入负号
变更后的界面如图3-9所示。
图3-9 新增按钮后的界面 (2)删除代码的编写 1)清空文本框:
Private Sub cmdClear_Click() '单击 C 按钮事件过程 Me.txtShow.Text = \"\" '清空文本框 End Sub 2)新函数学习:
◆ Left(s, n)——从字符串s中取出左边n个字符。例:Left(\"Welcome \。
◆ Right(s, n)——从字符串s中取出右边边n个字符。例:Right(\"holiday\。
◆ Mid(s, i [, n])——从字符串s第i个字符开始取出n个字符。例:Mid(\"1234\
\"23\"。其中参数n可以省略,如果省略n,取出的字串就从第i个字
符开始直到末尾。 3)删除错误输入:
txtShow.Text = Left (txtShow.Text, Len (txtShow.Text) - 1) 退格按钮的代码:
Private Sub cmdBackSpace_Click() '退格按钮单击事件过程 txtShow.Text = Left(txtShow.Text, Len(txtShow.Text) - 1) End Sub
试运行程序,当文本框有字符的情况下,删除操作正确,可是当文本框为空的时候,单击退格按钮就会出现运行错误(见图3-10)。
图3-10 无效参数错误
这是因为取子串函数中的参数n必须是正整数,因为不可能取出-n个字符。
要修正这个错误,还需要加一个判断: Private Sub cmdBackSpace_Click()
If txtShow.Text <> \"\" Then '判断文本框是否为空,不为空才做删除操作
txtShow.Text = Left(txtShow.Text, Len(txtShow.Text) - 1) End If End Sub 10.负号输入功能
这个功能比较简单,处理代码与小数点处理代码类似,不同的是,负
号只能在文本框最左边出现,而且不需要加前导0。
只要文本框为空,单击按钮+/-,就可以输入一个负号,当文本框中有数字的时候,单击该按钮,可以给数据变号。 Private Sub cmdMinus_Click() '单击+/-按钮事件过程 If blnOperatorClicked = True Then
txtShow.Text = \"\" '如果单击过运算符,则先清空文本框 blnOperatorClicked = False '然后取消按过运算符的状态 End If
If txtShow.Text = \"\" Then '如果文本框为空 txtShow.Text = \"-\" '添加一个负号 Else
If If Val(txtShow.Text) <> 0 Then '如果文本框中有数据 txtShow.Text = -Val(txtShow.Text) '让文本框中的数据变号 End If End If End Sub 11.知识点小结
在本节四则运算计算器的设计过程中,我们新学到了以下知识点: ◆ 数组——数组是一组同名变量,用下标来编号来区别它们。数组可以极大地方便运算,提高编程效率。
◆ 控件数组——控件数组是一组同名的控件,控件数组成员的同一事件可以共用一个事件过程,因而可以简化代码和提高编程效率。
◆ 取子串函数——Left(s,n)从字符串s中取出左边n个字符;Right(s,n) 从s中取出右边n个字符;Mid(s,i,n) 从s第i个字符开始取出n个字符。
◆ 求字符串长度函数——Len(s),计算字符串s的字符个数。 ◆ 字符串搜索函数——InStr(String1,
String2)在一个字符串中搜索另一个字符串的内置函数,返回值是String2在String1中首次出现的位置序号。 3.1.2 自然数累加与阶乘功能 1. 添加界面元素
在窗体中再添加2个按钮(如图3-11)。
图3-11 添加新按钮控件
然后按表3-3的描述分别修改新增按钮的属性。 表3-3 新增按钮描述
控件类型NameCaption用途
CommandButtoncmdSigmaΣ自然数累加 CommandButtoncmdFactorialn!求阶乘
2. 循环结构程序设计
重复劳动是计算机的最擅长的。为了适应重复计算的编程需要,程序设计语言都提供了一类语句,叫做循环语句。VB中最常用的循环语句有2种,Do(While)循环语句和For循环语句。前者又有2种语
法结构。
(1)Do While-Loop 和 While-Wend 循环结构
Do While-Loop 和 While-Wend循环语句的运行逻辑如图3-12(左)所示。
图3-12 循环语句流程图
这种循环结构的运行流程是,首先判断是否满足循环条件,如果满足,就执行一遍循环体中的语句,然后回过头来再作判断,如果条件还是满足,就再执行一次循环体,如此反复„„,直到某一次判断条件不满足了,就结束循环,执行后续语句;如果一开始判断条件就不满足,则直接绕过循环体,执行后续语句。 这种循环语句的结构也叫前测循环结构。
非常重要的是,循环语句中必须有一个能让循环趋向结束的语句,否则循环永远不会结束,而造成所谓的死循环。 前测循环语句的语法格式为: Do While <循环条件> <循环体> Loop 或者:
While <循环条件> <循环体> Wend
(2)Do-While Loop循环语句
Do-While Loop循环语句的运行逻辑如图3-12(右)所示。 这种循环结构的运行流程是,先无条件执行一遍循环体,然后再来判断条件是否满足,若满足,就再次执行循环体,如此反复„„,直至条件不能满足为止,于是结束循环,执行后续语句。 这种循环语句的结构也叫后测循环结构。 后测循环语句的语法格式为: Do <循环体>
Loop While <循环条件>
这2种逻辑结构的区别就在于,后测循环结构至少会执行一次循环体,而前测循环结构则可能一次都不执行循环体。 (3)累加和编程
用循环语句来编写100个5相加的程序。 1)循环条件是什么? 2)怎么知道是加了100次? 3)循环体应该做什么事?
Dim intCount As Integer '计数变量 Dim lngSum As Long '求和结果变量
Do While intCount < 100 '只要计数器的值<100就执行循环体 lngSum = lngSum + 5 '循环体,将结果变量的值取出来+5再写回去
Loop
txtShow.Text = lngSum '输出求和结果 看看这段代码正确吗?好像死循环了!
前面讲过,循环结构中必须要有一个使循环趋于结束的语句,这段代码中忘记计数了!循环体中还应该加一个语句: intCount = intCount + 1 正确的代码如下:
Private Sub cmdSigma_Click() 'Σ按钮单击事件过程 Dim intCount As Integer '计数变量 Dim lngSum As Long '求和结果变量
Do While intCount < 100 '只要计数器的值<100就执行循环体 intCount = intCount + 1 '计数器+1
lngSum = lngSum + 5 '做+5运算,结果保存到lngSum Loop
txtShow.Text = lngSum '将求和结果输出到文本框 End Sub
也可以将While改成Until,不过改成Until之后,循环条件则需要做反向的判断。例如:While x<5 等价于Until x>=5。 3. 自然数累加编程 (1)For循环语句
再来学习另外一种循环语句,它可以自行维护计数器和控制循环次数,它的语法是:
For <循环变量名> = <初值> To <终值> [ Step <步长>] <循环体> Next [<计数变量名>]
其中步长是非0的任意实数,如果省略Step短语,则默认步长为1。 这种循环语句的执行逻辑是:首先判断循环变量是否在初值和终值的范围之内,如果是,就执行循环体,遇到Next后,将循环变量的值增加一个步长值,回头再次进行判断,如果还在范围内,继续循环,如果超出范围,就结束循环。 (2)自然数累加编程
每次要加的数正好等于当次循环变量的值。第1次循环+1,„„,第100次循环+100。
Private Sub cmdSigma_Click() 'Σ按钮单击事件过程 Dim i As Integer '计数变量
Dim lngSum As Long '求和结果变量
For i = 1 To 100 '只要i的值在1~100范围内就执行循环体 lngSum = lngSum + i '循环体 Next i
txtShow.Text = lngSum '将求和结果输出到文本框 End Sub
启动程序,单击Σ按钮,文本框输出5050,完全正确。
要求1~n的累加和只要将循环语句中的100改成n就可以了,n由用户输入。
Private Sub cmdSigma_Click() 'Σ按钮单击事件过程 Dim i As Integer '计数变量
Dim lngSum As Long '求和结果变量 Dim n As Integer '求和范围 n = Val (txtShow.Text) '读取n的值
For i = 1 To n '只要i的值在1~n范围内就执行循环体 lngSum = lngSum + i '循环体 Next ' 此处的i可以省略
txtShow.Text = lngSum '将求和结果输出到文本框 End Sub 4. 求阶乘编程
会做自然数累加,再来做阶乘,那就很容易了。先来看以下代码: Private Sub cmdFctorial_Click() '单击n!按钮事件 Dim i As Integer '计数变量
Dim lngProduct As Long '求阶乘结果变量 Dim n As Integer '求阶乘范围 n = Val (txtShow.Text) '读取n的值
For i = 1 To n '只要i的值在1~n范围内就执行循环体 lngProduct = lngProduct * i '循环体 Next
txtShow.Text = lngProduct '将求阶乘结果输出到文本框 End Sub
在文本框中输入一个5,单击n!按钮,结果是0。 来看看循环体的计算表达式: lngProduct = lngProduct * i
由于lngProduct的初始值为0。不论循环多少次,lngProduct的值始终都是0。
在进入循环结构之前,需要手动初始化lngProduct: lngProduct = 1 '初始化为1
将这行语句插入到For语句之前,程序就能够正常计算阶乘了。 5. 知识点小结
本节我们学习的主要知识点就是循环结构的程序设计。循环结构是简化重复计算的一种编程逻辑,通过循环语句来实现。现在将VB的循环语句总结一下。
◆ 前测循环——凡是在循环体执行前需要判断循环条件的,都称为前测循环。循环体可能一次都不执行;
◆ 后测循环——凡是在循环体执行后才需要判断循环条件的,都称为后测循环。循环体至少执行一次;
◆ While <循环条件>短语——表明当<循环条件> =True的情况下才执行循环体,直至<循环条件> =False,结束循环;
◆ Until <循环条件>短语——表明当<循环条件> =False的情况下才执行循环体,直至<循环条件> =True,结束循环;
◆ For <循环变量名>=x To y短语——表明当循环变量在x~y范围之内时才执行循环体,否则结束循环;
◆ 使用Do-Loop循环和While-Wend循环结构需要程序员维护循环变量,而For-Next循环语句能自动维护循环变量;
◆ 除了For-Next循环语句外,其它循环语句的循环体中都至少要有一条能够使得循环趋于结束的语句。 3.1.3 函数计算功能 1. 求三角函数 (1)添加界面元素
在窗体上添加表3-4所列的3个命令按钮和2个单选按钮,并设置它们的属性。
表3-4 求三角函数所需添加的控件 控件类型NameCaption用途
CommandButtoncmdTriFunc(0)Sin求正弦函数 CommandButtoncmdTriFunc(1)Cos求余弦函数 CommandButtoncmdTriFunc(2)Tan求正切函数 OptionButtonoptDegree角度角度选项 OptionButtonOptRadian弧度弧度选项
添加新按钮后的界面如图3-13所示。
图3-13 新增三角函数控件
将新增的3个命令按钮做成控件数组,命名为cmdTriFunc。 ◆
单选按钮OptionButton——顾名思义,它是用户与程序交互中做选择用的。在某一时刻,放在同一个容器之中的多个单选按钮只能选择其一。单选按钮有一个Value属性,存放布尔类型的数据,当某一个单选按钮被选中时,它的Value属性值就等于True,此时同组的另外单选按钮的Value值就都自动变成False,而且外观也都呈现出未被选中的样式。OptionButton的Caption设置它右边的说明文本。 ◆ 三角函数——包括Sin(x),Cos(x)和Tan(x),它们都是VB的内置函数。参数x要求是弧度值,返回值是相应的三角函数的值。 ◆
自定义常量——自定义常量是命名常量的一种,需要进行常量声明后才可以使用,声明后在内存中有它的存储单元,在它的作用范围之内,可以随时以常量名对它进行引用。与变量声明不同的是,常量在声明时必须同时赋值,而且在它的生存期内不许再次给它赋值。常量的声明语句格式如下:
Const <常量名> As <数据类型> = <常量值> '实例:
Const PI As Single = 3.141593 (2)求三角函数编程
由于求三角函数常用的参数是角度,所以在设计时可以先将optDegree的Value属性选为True,或者在Form_Load事件过程最后插入一行:
Me.optDegree.Value = True '选中角度选项
这样窗体启动时就会默认选中角度单选按钮。
求三角函数的编程思路:首先从文本框取出用户输入的角度数据;然后根据单选按钮的选择情况来判断输入的是角度值还是弧度值,如果是角度值,则要将角度值转换为弧度值;最后根据单击的是Sin,Cos还是Tan,分别用内置函数来求出函数值并输出到文本框。 三角函数按钮单击事件过程:
Private Sub cmdTriFunc_Click(Index As Integer) '三角函数按钮单击事件过程
Const PI As Double = 3.14159265358979 '声明一个常量存放圆周率
Dim x As Single '声明角度存放变量 x = Val (txtShow.Text) '读取角度值
If optDegree.Value = True Then x = x * PI / 180 '如果单选了角度,则转换为弧度值 Select Case Index
Case 0 '如果单击了Sin按钮
txtShow.Text = Sin(x) '输出Sin(x)的函数值 Case 1 '如果单击了Cos按钮
txtShow.Text = Cos(x) '输出Cos(x)的函数值 Case 2 '如果单击了Tan按钮
txtShow.Text = Tan(x) '输出Tan(x)的函数值 End Select
End Sub
◆ 格式化函数Format(x,
f)——该函数可以将数值型、日期型和字符串型数据转换成某种指定的格式。它有2个参数,用逗号分隔,第1个是需要转换的数据表达式,第2个是指定的格式模板,用双引号括起,例如: txtShow.Text = Format(Sin(x), \"0.000000\")
这样,Sin(x)的函数值将会被转换成带有前导0的并且保留6位小数的数字格式,6位以后被截去的小数按四舍五入规则进行处理。 用Format函数修改了输出语句后,再计算Sin(90)的输出结果就等于1.000000了。这种格式似乎不合常理,拖尾0也是不必显示的。那么再来改用Format的另一种格式: txtShow.Text = Format(Sin(x), \"0.######\")
这种格式的意思是:保留前导0,最多保留6位小数,如果小数位有拖尾0,则不予显示。因此最少可能只有0位小数。更多Format ()函数的格式可参见附录B。 2. 求对数
VB提供了求对数的内置函数Log(x)。Log(x)函数的返回值为x的自然对数,即以e为底的对数,即数学中的log e x或ln
x(x是任何有效的大于0的数值表达式)。如果要求以n为底的对数,则可以根据对数的换底公式log e x = ln x / ln n,得到等价的VB表达式为:
Log(x) / Log(n)。由此可得以10为底(常用对数)的对数的运算表达
式则为:Log(x) / Log(10)。 (1)添加求对数控件
按表3-5的描述在原来窗体中添加2个按钮ln和lg,做成按钮数组cmdLog,分别用于求自然对数和常用对数。 表3-5 求对数所需添加的控件 控件类型NameCaption用途
CommandButtoncmdLog(0)Ln求自然对数 CommandButtoncmdLog(1)Lg求常用对数
添加新按钮后的界面如图3-14所示。
图3-14 新增求对数控件 (2)求对数编程
首先判断单击的是那个按钮,如果是ln,求自然对数,那么直接引用Log(x)函数即可得到结果,若是lg,要求常用对数,则要进行换底运算后才能得到结果:
Private Sub cmdLog_Click(Index As Integer) '对数按钮的单击事件过程
Dim x As Single x = Val (txtShow.Text)
If Index = 0 Then '如果单击ln按钮 txtShow.Text = Log(x)
Else '如果单击lg按钮
txtShow.Text = Log(x) / Log(10) End If End Sub 3. 自定义函数
(1)自定义函数的格式
自定义函数的框架跟过程类似,最大的区别在于函数需要有一个返回值,而过程没有。自定义函数的基本框架结构如下:
[Private] Function <函数名>([<参数1声明> [, <参数2声明> ]„]) [As <类型>] <语句块>
<函数名>=<表达式> [Exit Function] [<语句块>] End Function 几点说明:
1)如果加上Private关键字,那么该函数只能被本模块中的程序调用,如果未加,默认为Public(也可以显式地加上Public),那么在整个工程中任何一个模块中的程序都可以调用该函数;
2)函数名命名规则与变量名相同,参数的声明与变量声明相同,可以有0到多个参数;
3)函数体中至少要有一个给函数名赋值的语句,函数名就是该函数
的返回值,返回值的数据类型在函数头部最后声明; 4)使用Exit Function语句可以无条件跳出函数结构; 5)结束函数语句为End Function。
6)在参数声明部分目前暂时隐藏了一些关键字,有待以后逐步补充。 先试着写一个最简单的函数,求1个数的平方: Function Power(x As Single) As Single
Power = x * x '为函数名赋值作为函数的返回值 End Function 函数调用也很简单:
Label1.Caption = \"5的平方 = \" & Power(5) x * x 可以写成x ^ 2。
再来写一个求2个数中最大数的函数: Function Max(x As Single, y As Single) As Single If x > y Then Max = x Else Max = y End Function
这次有2个参数,调用的时候按次序对应地传递2个参数给函数就可以了。例如:
Print \"5和6中最大数为:\"; Max(5, 6) (2)求阶乘的函数
现在来自定义一个求阶乘的函数:
Function Factorial(n As Integer) As Double '求阶乘函数 Dim i As Integer
Factorial = 1 '初始化函数值,0和1的阶乘都等于1 If n > 1 Then For i = 2 To n
Factorial = Factorial * I '计算阶乘,并给函数名赋值作为返回值 Next End If End Function
将这个函数写在窗体模块的最后,或任意2个过程之间,备用。 现在在窗体中添加一个临时按钮Command1,用来测试上述函数: Private Sub Command1_Click() MsgBox
txtShow.Text
&
\"!=\"
&
Factorial(Val(txtShow.Text)),vbInformation,\"阶乘函数\" End Sub
运行程序,在文本框中输入5,单击Command1,得到输出如图3-15的效果。
图3-15 阶乘函数测试 (3)求e的代码
自然对数ln就是以e为底的对数,那么e是怎么求出来的呢?e的近似值是通过一个级数前n项之和求出来的近似值,n越大,精度越高,e≈2.718281828。
这个级数的规律:第i项的值是i!的倒数(i = 0, 1, 2, „)。 只要把0~n每个数的阶乘求倒数,然后逐一累加,就能求得e的近似值。
Private Sub Command1_Click() Dim e As Double, i As Integer For i = 0 To 20
e = e + 1 / Factorial(i) '分母调用了自定义的求阶乘函数 Next
MsgBox \"e≈\" & e, vbInformation, \"求e\" End Sub
单击Command1,可以看到e≈2.71828182845905。
由于e的近似公式前2项之和等于2,所以代码也可以写成这样: Private Sub Command1_Click() Dim e As Double, i As Integer e = 2
For i = 2 To 20 e = e + 1 / Factorial(i) Next
MsgBox \"e≈\" & e, vbInformation, \"求e\" End Sub
由于求的是近似值,因此还有一个精度问题要考虑,如何来控制精度?精度与循环次数之间有什么关系呢?
可以通过判断最后一项大小的办法来决定精度,比如最后一项的值小于10-6,我们就认为精度够了,那么便可以结束循环。 Private Sub Command1_Click() Dim e As Double, i As Integer e = 2 i = 1 Do i = i + 1
e = e + 1 / Factorial(i)
Loop Until 1 / Factorial(i) < 1 / 10 ^ 16 '直至第i项的值<10-16,就结束循环
MsgBox \"e≈\" & e, vbInformation, \"求e\" End Sub
由于预先不知道循环次数,我们改用了后测循环结构。此时需要自己来维护循环变量i,i从2开始,何时结束则由表达式:1 / Factorial(i) < 1 / 10 ^ 16决定。
这个表达式还可以有以下多种等价的变化形式 : Loop Until 1 / Factorial(i) < 10 ^ (-16) Loop Until Factorial(i) > 10 ^ 16 Loop While 1 / Factorial(i) >= 1 / 10 ^ 16 Loop While 1 / Factorial(i) >= 10 ^ (-16)
Loop While Factorial(i) <= 10 ^ 16 4. 多重循环
通常将2重以上的循环结构称为多重循环结构,所谓2重循环,就是在一个循环结构中嵌套了另一个循环结构。当单重循环结构不能解决算法问题时,可以采用多重循环结构编程。
如果没有求阶乘的函数,求e也可以使用下列代码来实现: Private Sub Command1_Click()
Dim e As Double, i As Integer, j As Integer ' j作为内循环的循环变量
Dim dblFact As Double '存放阶乘结果的变量 e = 2 i = 1 Do i = i + 1
dblFact = 1 '初始化阶乘结果变量 For j = 1 To i '内嵌套循环,求阶乘 dblFact = dblFact * j Next
e = e + 1 / dblFact
Loop Until 1 / dblFact < 1 / 10 ^ 16 '直至第i项的值<10-16,就结束循环
MsgBox \"e≈\" & e, vbInformation, \"求e\"
End Sub
其中Do-Loop结构是外循环,For-Next结构是内循环。当外循环第1周时,i = 2,内循环就循环2周,求出2的阶乘;„„;当外循环第n周时,i = n
+ 1,内循环则循环n + 1周,求出n+1的阶乘。 5. 求幂与求根编程 (1)第1种求幂过程
在窗体添加1个按钮,Name为cmdPower,Caption为x^y(如图3-16)。
图3-16 添加求幂与求平方根按钮 求幂按钮单击事件过程代码如下: Private Sub cmdPower_Click() Dim x As Integer, y As Integer x = Val (txtShow.Text) On Error GoTo 1
y = InputBox(\"请输入指数\求幂\ txtShow.Text = x ^ y 1: End Sub
如果在输入对话框中单击“取消”按钮,则GoTo到行标签1:,直接结束过程。
(2)第2种求幂过程
1)先将求幂按钮的名称cmdPower改成cmdOperator,回车后,按钮已经加上了索引:cmdOperator(4),成为了运算符按钮数组的成员。 2)修改等号按钮的单击事件过程,在Select结构中加1个Case,来处理求幂运算:
Private Sub cmdEquals_Click() '单击=按钮事件过程
Select Case intOperator '需匹配的条件表达式为 intOperator Case 0 'intOperator为0-做加法
txtShow.Text = sglNumber + Val(txtShow.Text) …………
Case Else 'intOperator为4-求幂
txtShow.Text = sglNumber ^ Val(txtShow.Text) End Select
sglNumber = 0 '暂存器清0,避免连按等号时重复计算 intOperator = 0 '避免连按等号时做减法或除法 End Sub
这样,求幂的操作过程就跟四则运算完全一样了。 (3)求平方根编程
求平方根有2种方法,第1种方法是使用x^y按钮来求,令y=1/2或0.5即可。第2种方法,利用求平方根的内置函数Sqr(Square Root的缩写)。
再来添加1个按钮,Name为cmdSqrt,Caption为2个特殊符号:
√ ̄,在中文输入法的软键盘中可以找到(如图3-16)。 求平方根按钮的单击事件过程代码: Private Sub cmdSqrt_Click()
txtShow.Text = Sqr(Val(txtShow.Text)) End Sub 6. 知识点小结
在本节中,我们学习了一下知识点: ◆
单选按钮——它是用户与程序间作选择交互的控件。在某一时刻,同一容器中的多个单选按钮只能选中其一。程序通过访问单选按钮的Value值,来确定哪个单选按钮被选中。
◆ 三角函数——Sin(x),Cos(x)和Tan(x)都是VB的内置函数。参数x必须是弧度值,如果用户输入的是角度值,则必须进行角度到弧度的转换。
◆ 自定义常量——命名常量与变量的不同点在于,常量在声明时必须同时赋值,而且在它的生存期内不许再次给它赋值,常量是只读的。 ◆ 格式化函数——Format()函数可以将数值型、日期型和字符串型数据转换成某种指定的格式。它关键是指定格式模板的表达式。 ◆
自定义函数——自定义函数是可以被重复使用的独立代码结构体。它的框架结构一定要严格按照规定来书写。如果函数的计算需要参数,那么必须按顺序、按数据类型来声明相应的接收参数的变量。函数名
就是函数的返回值,因此函数体中必须至少要有一个为函数名赋值的语句来保存返回值。 ◆
多重循环——多重循环,就是在一个循环结构中嵌套另一个或多个循环结构。当一个循环结构不能解决算法问题时,可以采用多重循环结构编程。需要注意的是,内嵌循环体必须完全嵌入外循环体之内,不能有任何所谓的“跨骑”结构。 3.1.4 键盘操作支持 1. 键盘事件
VB对象的键盘事件有3种:
KeyDown——按下按键时触发KeyDown事件; KeyUp——抬起按键时触发KeyUp事件;
KeyPress——一个完整的按键动作触发KeyPress事件。
如果做一个完整的按键动作,这3个事件都会被触发,三者的触发顺序是先KeyDown,然后KeyPress,最后KeyUp。
在本工程中,我们只需用到KeyPress事件,另外2个在以后的工程中会用到。 2. 键盘事件处理
(1)文本框键盘事件框架
选择KeyPress事件,该事件的过程框架如下: Private Sub txtShow_KeyPress(KeyAscii As Integer) End Sub
该过程可以接收参数KeyAscii,它是按键的Ascii码(简称Asc码),当触发KeyPress事件时由VB自动获得。常用按键的Asc码见表3-6。更多按键的Asc码可以参见附录C。 表3-6常用按键的Asc码表 按键Asc码(10进制值) 0~948~57 A~Z65~90 a~z97~122 +43 -45 *42 /47 .46 退格8 Enter13
字符的ASC码也可以通过VB的内置函数Asc获得。 ◆
Asc(c)——计算字符ASC码函数。返回字符c的Asc码。例如:Asc(\"A\")的返回值为65;Asc(\"a\")的返回值为97;Asc(\"0\")的返回值为48。
与之对应的还有一个反向转换函数Chr,它可以获得一个ASC码所
对应字符。
◆ Chr(i)——返回整数i所对应的字符。i必须在ASC码的范围之内,即0 ~ 255。例如:Chr(66)的返回值为\"B\";Chr(49)的返回值为\"1\"。 (2)文本框键盘事件编程
如果焦点位于文本框内,键盘上所有的可见字符都能键入文本框,这就给输入错误带来了隐患。我们要做的就是只允许数字、负号和小数点和退格键等13个字符进入文本框。
根据表3-6,我们可以写出文本框允许键入的13个字符的判断表达式:
KeyAscii >= 45 And KeyAscii <= 57 And KeyAscii <> 47 Or KeyAscii = 8
表示允许的是KeyAscii的值在45~57范围内、但不包括47,或者是8,一共13个字符。 而不允许字符进入的表达式为:
Not (KeyAscii >= 45 And KeyAscii <= 57 And KeyAscii <> 47 Or KeyAscii = 8) 或者:
KeyAscii < 45 And KeyAscii <> 8 Or KeyAscii > 57 Or KeyAscii = 47
VB对文本框控件KeyPress事件过程的处理,次序是这样的:按下键盘→处理KeyPress事件过程→显示字符。如果在处理过程中,改变了字符的KeyAscii值,就可以使得出现在文本框中的字符作相应
地改变。
如果键入非法字符,应该将它改成什么呢?Asc码表中有一个特殊字符Null,意思是空、无效的。这个空不是空串,也不是空格,更不是0,而是虚无、什么都没有。Null的Asc码是0。 根据上述的概念,就可以给出完整的处理过程了: Private Sub txtShow_KeyPress(KeyAscii As Integer)
If KeyAscii < 45 And KeyAscii <> 8 Or KeyAscii > 57 Or KeyAscii = 47 Then
'如果是非法字符,那么
KeyAscii = 0 'txtShow中不显示字符 Else '如果是合法字符 Select Case KeyAscii Case 45 '如果是负号
If txtShow.Text <> \"\" Then KeyAscii = 0 '负号只能在第1位
Case 46 '如果是小数点
If InStr(txtShow.Text, \".\") > 0 Then KeyAscii = 0 '小数点只能出现一次 End Select
If blnOperatorClicked = True Then sglNumber = Val(txtShow.Text)
txtShow.Text = \"\" '如果按过运算符,清空文本框
blnOperatorClicked = False '取消按过运算符的状态 End If End If End Sub
如果在键盘上按下运算符按键,是不允许进入文本框的(减号当负号时除外),那么如何处理运算符按键呢? 3. 窗体键盘事件处理 (1)窗体键盘事件编程思路
当焦点在窗体中某一个控件之上的时候,该窗体同时也一定拥有焦点。如果窗体对象收到这个事件消息,窗体对象也会触发KeyPress事件。默认情况下,窗体是不处理这个事件的,如果需要它来处理,那么必须设置它的KeyPreview的属性。如果将它的值设置为True,就可以让窗体先于控件收到键盘事件消息。可以在Form_Load事件过程中加上一行代码来设置这个属性: Me.KeyPreview = True
如果窗体能够收到键盘事件,窗体的按键事件过程就可以供我们进行编程。
(2)运算符按键编程代码
Private Sub Form_KeyPress(KeyAscii As Integer) '窗体按键事件过程
Select Case KeyAscii Case 43 '按下加号键
cmdOperator_Click (0) '调用运算符单击事件过程 Case 45 '按下减号键
If txtShow.Text <> \"\" Then '若文本框不空,看作减号,否则看作负号
cmdOperator_Click (1) '调用减号按钮单击事件过程 KeyAscii = 0 '按键不再影响文本框 End If
Case 42 '按下乘号键 cmdOperator_Click (2) Case 47 '按下除号键 cmdOperator_Click (3) End Select End Sub
当按下运算符按键时,调用了运算符单击事件过程。将相应运算符按钮的下标作为个参数传递给过程。这样就模拟了按钮的单击动作。 窗体收到的事件消息,文本框随后也会收到,不过除了减号之外,其它按键都被文本框拒之门外。
如果已经输入过第1个运算数,那么文本框不为空,此时按下减号键应作为减号对待,调用减号按钮的单击事件过程,同时将减号的KeyAscii置0,让文本框收不到按键事件消息;如果文本框为空,那么就当作负号看待,不去调用减号按钮单击事件过程,而会由文本框的KeyPress事件接收并按负号处理。
4. 回车键的处理代码
前面我们已将等号按钮的Default属性设置为True,按下回车键就相当于单击了等号按钮。也可以通过窗体的按键事件过程来进行编程。 在Form_KeyPress过程中,加上一个Case来处理回车事件: Private Sub Form_KeyPress(KeyAscii As Integer) '窗体按键事件过程
Select Case KeyAscii Case 13 '按下回车键
cmdEquals_Click '调用等号按钮单击事件过程 …… End Select End Sub 5. 知识点小结
◆ 键盘事件——用户操作键盘时触发的事件。利用键盘事件可以代替许多鼠标事件,从而提高操作速度。键盘事件共有3个,本节只介绍了KeyPress事件的应用。 ◆
用好键盘事件,需要知道各个按键的Asc码,通过与事件所传递的参数KeyAscii进行对比,便可以确定用户按下的是哪个键,从而编程控制程序的流程和处理相关的运算。 ◆
KeyPreview属性——将窗体的KeyPreview属性设置为True,窗体
可以优先于控件接收到键盘事件消息。利用它可以处理其它控件不能接收或不必接收的消息。
◆ 活用KeyAscii = 0。用好它可以屏蔽键盘按键消息,达到意想不到的效果。 3.2 扩展技能训练 3.2.1 数制转换
10进制数到n进制数的转换采用的是除n取余法。假设一个10进制整数x要转换成2进制数,就可以用2不断地去除x,将每一步得到的余数从右到左排列,直至商数为0,并将最后的余数排在最左边,这样构成的数就是x的等值二进制数。
转换为8进制和16进制数也是同理,只要将除数换成相应的进制基数即可。 1. 程序界面设计
图3-17 进制转换程序界面
程序界面如图3-17所示,其中的控件名称参照表3-7。 表3-7 进制转换程序界面的控件 控件类型NameCaption用途 LabellblDigital十进制说明文本 lblOthersx进制
TextBoxtxtDigital 输入输出10进制数 txtOthers 输入输出其它进制数
OptionButtonoptSystem(0)2进制数制选项 optSystem(1)8进制
FramefraFrame 为单选按钮分组
OptionButtonOptConvert(0)正转换10进制转其它进制 OptConvert(1)逆转换其它进制转10进制
其中用到了一个新的控件-Frame,称为框架控件,就是工具箱中的图标所表示的控件,它是一个容器,可以用来为控件分组。由于窗体中用到了4个单选按钮,分为2组,如果将它们都放置在同一个容器中,那么同一时刻就只能选中4个中的1个,若需要选中2个,就必须将它们分为2组。注意,放置在框架容器中的控件,必须直接画在框架之中,而不能先画在窗体中,然后再拖进去。 2. 程序实现代码 (1)窗体加载过程
在窗体启动时,默认选中“正转换”单选按钮: Private Sub Form_Load()
optConvert(0).Value = True '选中“正转换”单选按钮 End Sub (2)正转换过程
单击“2进制”或“8进制”单选按钮时,根据所选择的数制选项来确定数制的基数,然后读取上边文本框中的10进制数,再用除2取余法或除8取余法来进行数制转换:
Private Sub optSystem_Click(Index As Integer) Dim lngDigital As Long '存放10进制数 Dim intBase As Integer '存放进制的基数
Dim i As Integer, intTemp As Integer '存放每一次的余数
lblOthers.Caption = optSystem(Index).Caption '将第2行的说明文本改为相应的进制
Select Case Index '判断选中的是哪种进制,然后确定数制的基数 Case 0 '选中2进制 intBase = 2 Case 1 '选中8进制 intBase = 8 End Select
If optConvert(0).Value = True Then '选中正转换 txtOthers.Text = \"\" '清空下面的文本框 lngDigital = Val(txtDigital.Text) '读取10进制数 Do While lngDigital > 0
'如果输入的10进制数是数字且不为0,反复取余,直至商数为0 intTemp = lngDigital Mod intBase '除基数取余
txtOthers.Text = intTemp & txtOthers.Text '将余数从右到左排列
lngDigital = lngDigital \\ intBase '获得最新的商数 Loop
txtDigital.SetFocus '选中上边文本框以便输入下一个数 Else '选中逆转换 …… End If End Sub
转换结果如图3-18所示。
图3-18 转换结果 (3)逆转换过程
将一个n进制的数转换为10进制数,可以用按权展开法来进行。n进制数第i位数的等值10进制数为:该位的数值×该位的权;而某一位的权等于ni (i=0, 1,„)。
例如,将2进制的101转换成10进制数,最右边的数字1的等值10进制数为1×20;中间的数字0的等值10进制数为0×21;最左边边的数字1的等值10进制数为1×22。三者相加就等于10进制的5。 这个算法,可以用以下代码实现: txtDigital.Text = \"\" '清空上面的文本框
For i = 1 To Len(txtOthers.Text) '按字符的位数进行循环 intTemp = Mid(txtOthers.Text, Len(txtOthers.Text) - i + 1, 1) '从右往左依次读取每一位数字
If intTemp >= intBase Then '如果某一位数字大于进制的基数,则报错
MsgBox \"该数不是\" & Str(intBase) & \"进制数!\错误!\" Exit Sub End If
lngDigital = lngDigital + intTemp * intBase ^ (i - 1) '将每一位数字展开为10进制数并全部相加 Next
txtDigital.Text = lngDigital '在上边文本框显示10进制数 txtOthers.SetFocus '选中下边文本框以便输入下一个数
用本段取代前面小节代码行的省略号,就完成了逆转换部分的全部编程。
3.2.2 九九乘法表
九九乘法表一共有9行9列,每行的第1个数字分别是1~9,每列的第1个数字也分别是1~9,而其它位置上的数字,是所在列的第1个数字与所在行的第1个数字的乘积,也就是行号和列号的乘积。使用2重循环,很容易写出打印乘法表的代码: Private Sub Command1_Click()
Dim i As Byte, j As Byte ' i代表行号,j代表列号 For i = 1 To 9 '从第1行到第9行循环 For j = 1 To 9 '从第1列到第9列循环 Print i * j; '打印行列下标的乘积,不换行 Next j
Print '打印完1行9个数字之后强制换行
Next i End Sub
在这段代码中,初学者不容易理解的是何时应该换行,何时又不能换行的问题,关键取决于对Print方法的语法理解的程度。
运行这段程序,乘法表虽然打印出来了,但是排列却不太整齐(如图3-19),而且上边和左边都顶着窗体的边缘,很不好看。
图3-19 最初的乘法表
上一章将Print语法时,介绍过2个配套函数Tab和Spc,它们可以在打印时控制打印的起始位置。以下代码就解决了上面的3个问题: Private Sub Command1_Click() Dim i As Byte, j As Byte
Print: Print: Print '表格上边空3行 For i = 1 To 9
Print Tab(4); '每行从左边第4列开始打印 For j = 1 To 9 Print i * j; Next j
Print: Print '每行多加1个换行 Next i End Sub
但是这仍然没有解决列对齐的问题,原因是有些数字是1位数,有些
是2位数。在打印数字前先判断一下,要打印的是1位数还是2位数,若是1位数,就在它前面加上1个空格。 For j = 1 To 9
If i * j < 10 Then Print \" \"; '如果数字小于10,就添加一个空格
Print i * j; Next j
这样打印出来的乘法表布局就比较理想了(见图3-20)。
图3-20 修正过个乘法表布局
最规范的方法是使用Tab函数来控制每列的打印起始位置: Private Sub Command1_Click() Dim i As Byte, j As Byte Print: Print: Print For i = 1 To 9 For j = 1 To 9
Print Tab(j * 4); i * j; Next j Print: Print Next i End Sub
每一列都从4的倍数的列开始打印。
3.2.3 排序
排序是日常生活中常见的一种工作。排序的算法又是程序设计中比较典型的范例之一,因此几乎所有程序设计语言课程都要涉及排序的编程问题。
1.简单排序算法描述
最基本的排序算法我们把它称为简单排序算法。以下通过一个具体的实例,对照表3-8来对简单排序算法进行描述。 表3-8 简单排序算法描述 位置序号(1)(2)(3)(4)说 明 第1轮
比较3次853 6(1)>(2),交换 5836(1)>(3),交换 3856(1)<=(4),不交换 第2轮
比较2次 58 6(2)<=(3),不交换 586(2)<=(4),不交换 第3轮 8 6(3)>(4),交换 排序结果356 8
2.简单排序算法编程 (1)排序界面设计
界面设计如图3-21所示。在界面中我们采用了10个文本框,做成了
名为txtNumbers的控件数组,用来显示要排序的数;还使用了2个按钮,一个名为cmdRandom,用于产生10个随机数,另一个名为cmdSort,用于执行排序过程;此外还用了2个标签,来显示一些说明文本。
图3-21 简单排序算法演示界面 (2)自动产生随机数
随机数函数Rnd,可以在每次调用它的时候,会返回一个范围在[0,1)的随机浮点数。
若需要使用较大的随机数,可以将Rnd函数的返回值乘以一个放大因子即可。例如通过Rnd * 100,即可得到一个介于0到100之间的数(不包括100)。
若要获得一个整数随机数,则可以将随机数截去小数部分即可。截去一个浮点数的小数部分可以用整除运算符来实现,例如12.3 \\ 1 = 12。还可以采用VB的内置函数Int (x)来实现。
若要改变整型随机数的下界和上届,可以使用以下表达式来实现: Int (Rnd * <上界> - <下界> + 1) + <下界>
例如,要产生一个2位数的整数(10 ~ 99),就可以用下列表达式来实现:
Int (Rnd * 99 - 10 + 1) + 10 = Int (Rnd * 90) + 10
其中Int (Rnd * 90)可以产生0 ~ 89范围内的数,再加上10,就得到10 ~ 99范围内的数。
每次调用Rnd函数产生的随机数序列是有规律的,例如每次开始运行程序时,产生的第1个随机数总是相同的,而此后调用Rnd时,总是用前一个随机数来生成下一个随机数,因此所产生的随机数系列是相同的。要打破这个规律,可以先执行一个语句: Randmize
单击“产生随机数”按钮后,自动产生随机数的过程如下: Private Sub cmdRandom_Click() '“产生随机数”按钮单击事件过程 lblNote.Visible = False '排序开始前,隐藏“排序结束”字样 Randomize '打乱随机数序列
For i = 0 To 9 '以文本框数组的下标循环,遍历10个文本框 txtNumbers(i).Text = Int(Rnd * 99) + 1 '产生十个1~99的随机数 Next
cmdSort.Enabled = True '启用“升序排序”按钮(设计时已禁用) End Sub
每次单击“产生随机数”按钮后,都会在10个文本框中自动产生不同的随机数序列。 (3)排序过程编程
按照前面介绍的排序算法,我们先取第1个文本框中的数,将它与第2、3、„„10个文本框中的9个数依次相比较,如果交换条件成立,就进行交换,完成第1轮的9次比较后,最小的数就被排到了第1个文本框中;然后再取第2个文本框中的数,将它与第3、4、„„
10个文本框中的8个数依次相比较,如果交换条件成立,就进行交换,完成第2轮的8次比较后,第2小的数就被排到了第2个文本框中;„„;如此比较下去,直到第9轮,取第9个文本框中的数,将它与第10个文本框中的数相比较,如果交换条件成立,就进行交换,完成第9轮的1次比较后,最大的数就被排到了第10个文本框中;排序完成。
以下是用2重循环实现的排序代码:
Private Sub cmdSort_Click() '“升序排序”按钮单击事件过程 cmdSort.Enabled = False '禁用“升序排序”按钮,产生新的随机数后再启用
For i = 0 To 8 '比较表达式左边的文本框下标,下标从0起算! For j = i + 1 To 9 '比较表达式右边的文本框下标
If Val(txtNumbers(i).Text) > Val(txtNumbers(j).Text) Then '如果交换条件成立
Call Exchange '调用交换子程序 End If Next Next
lblNote.Visible = True '显示“排序结束”字样 End Sub
由于两数的交换过程相对独立,因此将它写成一个单独的过程Exchange如下:
Sub Exchange()
Dim strTemp As String '交换过程使用的临时变量
txtNumbers(i).BackColor = vbYellow '突出显示要交换的左文本框 txtNumbers(j).BackColor = vbYellow '突出显示要交换的右文本框 MsgBox txtNumbers(i).Text & \" 将与 \" & txtNumbers(j).Text & \" 交换\数据交换\"
txtNumbers(i).BackColor = vbWhite '左文本框背景颜色还原 txtNumbers(j).BackColor = vbWhite '右文本框背景颜色还原 Rem 以下3行交换左右文本框中的数 strTemp = txtNumbers(i).Text
txtNumbers(i).Text = txtNumbers(j).Text txtNumbers(j).Text = strTemp End Sub
当遇到需要交换的数据对时,将即将进行交换数据的文本框的背景色改变为黄色,以突出显示,同时弹出对话框进行提示(见图3-22)。对于无需交换的比较过程,流程不作停留,运行时看不见比较的过程。
图3-22 排序程序运行过程
当排序全部结束后,在写有“排序结束”字样的标签控件lblNote显示出来。
【第一章】【上一章】【最末章】
Private Sub Form_KeyPress(KeyAscii As Integer) Text1.SetFocus Text1.Text = \"\" Dim i As Integer
If KeyAscii = Asc(\"0\") Then i = 0
Command1_Click (i) Beep
ElseIf KeyAscii = Asc(\"1\") Then i = 1
Command1_Click (i) Beep
ElseIf KeyAscii = Asc(\"2\") Then i = 2
Command1_Click (i) Beep
ElseIf KeyAscii = Asc(\"3\") Then i = 3
Command1_Click (i) Beep
ElseIf KeyAscii = Asc(\"4\") Then i = 4
Command1_Click (i) Beep
ElseIf KeyAscii = Asc(\"5\") Then i = 5
Command1_Click (i) Beep
ElseIf KeyAscii = Asc(\"6\") Then i = 6
Command1_Click (i) Beep
ElseIf KeyAscii = Asc(\"7\") Then i = 7
Command1_Click (i) Beep
ElseIf KeyAscii = Asc(\"8\") Then i = 8
Command1_Click (i) Beep
ElseIf KeyAscii = Asc(\"9\") Then i = 9
Command1_Click (i) Beep
ElseIf KeyAscii = Asc(\"/\") Then i = 0
Command6_Click (i) Beep
ElseIf KeyAscii = Asc(\"*\") Then i = 1
Command6_Click (i) Beep
ElseIf KeyAscii = Asc(\"-\") Then i = 2
Command6_Click (i) Beep
ElseIf KeyAscii = Asc(\"+\") Then i = 3
Command6_Click (i) Beep
ElseIf KeyAscii = Asc(\"=\") Then Command7_Click Beep
ElseIf KeyAscii = Asc(\".\") Then
Command8_Click Beep End If End Sub
因篇幅问题不能全部显示,请点此查看更多更全内容