例如:
函数过程定义如下:
Public Function f!(x!)
f=x+x
End Function
主调程序如下:
Privme Sub Commandl_Click()
Dim y%
y=3
Print f(y)
End Sub
上例形参x是单精度型、实参y是整型,程序运行时会显示"ByRef参数类型不符"的编译出错信息。
在值传递时,若是数值型,则实参按形参的类型将值传递给形参。例如:
函数过程定义如下:
Public Function f!(ByVal x%)
f=x+x
End Function
主调程序如下:
Priva~SubCommandI_Click()
Dim y!
Y=3.4
Print f(y)
End Sub
程序运行后显示的结果是6。
5.变量的作用域问题
局部变量,在对该过程调用,分配该变量的存储空间,当过程调用结束,回收分配的存储空间,也就是调用一次,初始化一次,变量不保值:窗体级变量,当窗体装入,分配该变量的存储空间,直到该窗体从内存卸掉,才回收该变量分配的存储空间。
例如,要通过文本框输入若于个值,每输入一个按Eenter键,直到输入的值为9999,箱入结束,求输入的数的平均值。
Private Sub Textl_Key Press(Key Ascii As Integer)
Dim sum!,n%
If KeyAscii=13 Then
If Val(Text1)=9999 Then
sum=sum/n
Print sum
Else
Sum=sum+Text1
n=n+1
Text1=""
End If
End If
End Sub
该过程没有语法错,运行程序可输入若干个数,但当输入9999时,程序显示"溢出"的错误。原因sum和n是局部变量,每按一个键,局部变量初始化为o,所以会有上述错误产生。
改进方法:将要保值的局部变量声明为Static静态变量或声明为窗体级变量。也可将要保值的变量在通用声明段进行声明为窗体级变量。
6.递归调用出现"栈溢出"
如下求阶乘的递归函数过程:
PublicFunctionfac(nAsInteger)Asinteger
If n=1 Then
fac=1
Else
fac=n*fac(n-1)
End If
End Function
Private Sub Commandl_Click() '调用递归函数,显示出fac(5)=120
Print "fac(5)":fac(5)
End Sub
当主调程序调用时,n的值为5时,显示120结果:当n的值为-5时,显示"溢出堆栈空间"的出错信息。
实际上每递归调用一次,系统将当前状态信息(形参、局部变量、调用结束时的返回地址)压栈,直到到达递归结束条件。上例当n=5时,每递归调用一次,参数n-l,直到n=l递归调用结束,然后不断从栈中弹出当前参数,直到栈空。而当n'-5时,参数n-1为-6、压栈,再递归调用、n-1永远到不了n=l的终止条件,直到栈满,产生栈益出的出错信息。
所以设计递归过程时,一定要考虑过程中有终止的条件和终止时的值或某种操作,而且每递归调用一次,其中的参数要向终止方向收敛,否则就会产生栈溢出。
第七章 常用控件(一)
7.1 知识要点
1.单选按钮和复选框
单选按钮和复选框的常用属性是Value,它的主要作用是用宋检查单选按钮或复选框是否被选定。
单选按钮和复选框能够响应Click事件,但通常不需要编写事件过程。
2.框架
框架的主要作用是将其他控件组合在一起,对一个窗体中的各种功能进行分类,以便于用户识别。当用框架将同一个字体上的单选按钮分组后,每一组单选按钮都是独立的,也就是说,在一组单选按钮中进行操作不会影响其他组单选按钮的选择。
框架的重要属性是Caption。
一般不需要编写事件过程。
3.列表框
列表框的主要属性有:Text、List、Listlndex、ListCount和So~ed。当MultiSelect属性为 1或2时允许多项选择,此时往往需要Selected属性判定哪些项目被选定。
列表框的常用方法是:Addltem、Removeltem和Clear。
在通常情况下,列表框不需要编写事件过程,其中的项目也不需要更改。
4.组合框
组合框是组合了文本框和列表框的特性而形成的一种控件。组合框的类型由其Style属性决定。下拉式组合框(Style为0)和简单组合框(Style为1)允许用户在文本框中输入不属于列表内的选项。
5.滚动条
滚动条特有的常用属性有:Max、Min、Value、SmallChange和LargeChange。
滚动条的重要事件是:Change和Scroll。
6.时钟控件
时钟控件特有的常用属性是Interval,它的值以0. 001秒为单位。
时钟控件的Enabled属性与其他控件是不同的,当时钟控件的Enabled属性为True时, Timer事件以Interval属性值的毫秒间隔发生。如果将时钟控件的Enabled属性设为False或 Interval属性设为0时计时器停止运行,则Timer事件不会发生。
Timer是时钟控件的唯一事件。
可以利用时钟连续播放图片达到动画效果。
7.3 常见错误和难点分析
1.遗漏对象名称
在VB程序设计时,初学者常犯的一个错误是遗漏对象名称,特别是在使用列表框时。例如,如果要引用列表框(List1)中当前选定的项目,Listl.list(Listlndex)是错误的。即使当前焦点在Listl上,VB也不是认为Listlndex是List l的属性,而是一个变量。所以正确的引用方式是:Listl. 1ist(Listl.Listlndex)。
2.列表框的Columns属性
列表框的Columns属性决定列表框是水平还是垂直滚动、以及如何显示列中的项目。如果水平滚动,则Columns属性决定显示多少列,如表2.7.2所示。图2.7.1是一个水平滚动两列显示的列表框。
在程序运行期间,该属性是只读的,也就是说,不能在运行时,将多列列表框变为单列列表框或将单列列表框变为多列列表框。
表2.7.2 列表框的Columns属性
列数 属 性
0(默认值)1到n 项目安排在一列中,且列表框竖直滚动项目安排在多个列中,先填第一列,再填第二列……列表框水平滚动并显示指定数目的列
3.域级验证
域级验证是指输入到某独立域的数据的验证,在域级验证的过程中一般不考虑窗体-般其他域的内容。进行域级验证的合适时间是:
(1)当往某域中输入某个键时,此时涉及键盘事件(KeyDown、KeyUp和KeyPress)。有关这方面的内容请参阅教程第4.7节。
(2)当用户企图离开某域时,此时涉及CansesValidate属性和Validate事件。
(3)当某域的内容发生变化时,此时涉及Change事件。
CausesValidation属性和Validate事件通常是协同工作的。
CausesValidate属性决定Validate事件是否发生。如果控件的CausesValidate属性为False,该控件的Validate事件永远不会发生。如果控件的CausesValidate属性为Tree,当焦点企图移到(还没有离开,也可以说离开之前)另一个CausesValidate属性为True的控件时,原控件的Validate事件发生:当焦点企图移到另一个CausesValidate属性为False的控件时,原控件的Validate事件暂时不发生,什么时候发生?直到焦点移到一个CausesValidate属性为True的控件上时才发生。
例如,假定有如图2.7.2所示的程序。当焦点企图从Textl移到Text2时,Textl的Validate事件发生:同样当焦点企图从Text2移到Textl时,Text2的Validate事件发生。当焦点企图从Textl移到Text3时,TextI的Validate事件暂时没有发生,因为Text3的CausesValidate为False,然后如果焦点企图继续移动到Text2时,Textl的Validate事件才发生。
图2.7.2 CausesValidate属性和Validate事件
CausesValidate属性和Validate事件的这一特性常常应用在如图2.7.3所示的程序中。当在文本框中输入了无效数据时而又不知道如何输入有效数据时,用户往往选择Help命令寻求帮助或选择Cancel命令结束数据输入,此时不希望执行验证程序。因为如果执行验证程序且发现了无效数据,则无法选择Help或Cancel了,用户就这样被套住了。现在只要将文本框的CausesValidate属性设为True,命令按钮的CausesValidate属性为False,验证程序放在 Validate过程中,问题就解决了。在默认情况下,所有控件的CausesValidation属性都为Tree。
图2.7.3 命令按钮的CausesValidate属性和Validate事件
并不是所有的控件都有Validate事件。只有那些能用于输入数据的控件(如文本框、复选框、滚动条等)才有这个事件。Validate事件过程如下:
Privme Sub object_Validate(Cancel As Boolean)
…
EndSub
其中,当参数Cancel被设为Tree后,焦点将不会离开对象。
如果要将实验七第1题改用Validate事件和CausesValidation属性实现数据验证,则应有如下的程序。
Sub txtMath_Validate(Cancel As Boolean)
If Val(txtMath.Text)<0 Or Val(txtMath.Text)>100 Then
Cancel=True
End If
End Sub
常用控件(二)
8.1 知识要点
1.菜单
每一个菜单项都是一个控件,有Click事件。在程序运行期间,如果用户单击菜单项,则运行该菜单项的Click事件过程。
利用菜单编辑器可以设计弹出菜单,在程序中使用PopupMenu方法显示。
利用控件数组可以实现实时菜单,即由应用程序根据需要动态创建的菜单。菜单项用 Load语句创建,用UnLoad清除。
2.对话框
在VB应用程序中,对话框有三种:预定义的对话框、通用对话框和用户自定义对话框。
预定义对话框是系统定义的对话框,可以调用如InputBox、MsgBox等函数直接显示。
通用对话框向用户提供了打开、另存为、颜色、字体,打印和帮助六种类型的对话框。使用它们可以减少设计程序的工作量。
自定义对话框是具有特殊的属性设置的窗体。作为对话框的窗体的BorderStyle、 ControlBox、MaxBu~on和MinButton应分别为1、False、False和False。
对话框分成模式的和无模式的。
3.多重窗体
窗体的常用方法和语句有:Load、Unload、Show和Hide。
在窗体的加载和卸载过程中涉及到多种事件,这些事件发生的时机和次序请参阅教程第 4.5节。
4.高级控件
在VB中,用户可以加载和使用VB提供的以及第三方开发的许多扩展的高级控件。它们的属性可以简单地在属性页中设置。
(1)SSTab控件可以用来制作选项卡。
(2)ProgressBar用来指示事务处理的进程。
(3)Slider控件是包含滑块和可选择性刻度标记的窗口,以一种可视的方式向用户提供刻度设置的功能。
(4)UpDown可以用来设置程序的某些参数,一般与文本框"捆绑"在一起。
(5)Animation用来描放AVI视频文件。Animation的四个重要方法是:Open、Play、Stop和Close方法,常用属性有Center和AutoPlay。如果Center为Tme,则动画在控件的中央播放;如果AutoPlay为True,则用Open打开文件时自动播放,否则需要用Play播放。
5.鼠标器
除了Click和DblClick之外,鼠标事件还有MouseDown、MouseUp和MouseMove。
这三个鼠标事件过程具有相同的参数。
(1)Button:可以使用vbLefiBu~on、vbRightButton和vbMiddleButton方便地检测是哪个鼠标按钮被按下了。
(2)Shift:可以使用vbAltMask、vbCtrlMask和vbShiftMask以及它们的逻辑组合来检测 Alt、Ctrl和Shift键的状态。如果要检测Ctrl和Shift键是否同时被按下,则应用表达式(Shift AndvbCtrlMask)And(ShifiAndvbShifiMask)。
(3)X,Y:表示当前鼠标指针的位置。
MouseDown和MouseUp的button'参数的意义与MouseMove是不同的。对于MouseDown和MouseUp来说,button参数指出哪个鼠标按钮触发了事件,而对于MouseMove来说,它指示当前所有的状态。
6.键盘
键盘事件有KeyPress与KeyUp和KeyDown。
KeyUp和KeyDown所接收到的信息与KeyPress接收到的不完全相同。
("KeyUp和KeyDown能检测到KeyPress不能检测到的功能键、编辑键和箭头键。
(2)KeyPress接收到的是用户通过键盘输入的ASCII码字符。例如,当键盘处于小写状态,用户在键盘按"A"健,则KeyAscii参数值为97:当键盘处于大写状态,用户在键盘按"A"健,则KeyAscii参数值为65。KeyUp和KeyDown接收到的是用户在键盘所按键的键盘扫描码。例如,不管键盘处于小写状态还是大写状态,KeyCode参数值都是65。
总之,如果需要检测用户在键盘输入的是什么字符,则应选用KeyPress事件:如果需要检测用户所按的物理键时,则选用KeyUp或KeyDown事件。
在默认情况下,单击窗体上的控件时窗体的KeyPress与KeyUp和KeyDown是不会发生的。为了启用这三个事件,心须将窗体的KeyPreview属性设为True,而默认值为False。
一旦将窗体的KeyPreview属性设为True,键盘信息要经过两个平台(窗体级键盘事件过程和控件的键盘事件过程)才能到达控件。例如,假定有下列两个过程:
SubForm_KeyPress(KeyAsciiAsInteger)
KeyAscii=KeyAscii+1
End Sub
Private Subtxt Test KeyPress(KeyAscii As Integer)
Key Ascii=KeyAscii+1
End Sub
则当用户在键盘上输入小写字符"a"时,文本框txtTest接收到的字符是"c"。
利用这个特性可以对输入的数据进行验证。例如,如果在窗体的KeyPress事件过程中将所有的字符都改成大写,则窗体上的所有控件都不能接收到小写字符。
用户应该根据需要决定数据验证放在窗体级还是放在控件级。
7.普通拖放
手工拖放与自动拖放的区别如表2.8.1所示。
在运行时拖动源对象并不能自动改变源对象位置,必须进行编程来重新放置控件。
拖动时的图标由源对象的Draglcon属性决定。
表2.8.1 手工拖放与自动拖放的区别
对象 手工拖放 自动拖放
源对象 DragMode置为0用Drag方法启动"拖"操作 DragMode置为1没有MouseDows事件
中间对象 发生DragOver事件 发生DragOver事件
目标对象 发生DragOver和DragDrop事件 发生DragOver和DragDrop事件
8.OLE拖放
当源对象的OLEDragMode属性为](Automatic)时,自动支持OLE"拖"操作;当目标对象的OLEDropMode属性设为2(Automatic),自动支持OLE"放"操作。
完全支持OLE拖放的控件有:TextBox、PictureBox和Image等控件。
支持自动"拖"操作,但不支持自动"放"操作的控件有:ListBox、ComboBox、DirListBox和FileListBox等控件。
只支持OLE拖放事件的控件有:Label、CommandButton、OptionButton、CheckBox、Frame和DriveListBox等控件。
8.3 常见错误和难点分析
1.窗体顶部菜单栏中的菜单项与子菜单中的菜单项的区别
窗体顶部菜单栏中的菜单项与子菜单中的菜单项都是在菜单编辑器中定义的,但是它们是有区别的。
(1)窗体顶部菜单栏中的菜单项不能定义快捷键,而子菜单中的菜单项可以有快捷键。
(2)当有热键字母(菜单标题中"&"后的字母)时,按Alt+热键字母选择窗体顶部菜单栏中的菜单项,按热键字母选择子菜单中的菜单项(当子菜单打开时)。子菜单没有打开时,按热键字母无法选择其中的菜单项。
(3)尽管所有的菜单项都能响应Click事件,但是窗体顶部菜单栏中的菜单项不需要编写事件过程。
2.在程序中对通用对话框的属性设置不起作用
在程序中对通用对话框的属性设置不起作用,多数情况是因为在弹出对话框后才进行属性设置。例如,下面的程序代码就存在这样的问题,改正方法是将弹出对话框语句放到最后,即把CommonDialogl.Action=l放在所有属性设置语句的后面。
CommonDialog1.Action=1
ConmaonDialog1.FileName="*.Bmp"
CommonDialogl.InitDir="C:\Windows"
CommonDialog1.Filter="Pictures(*.Bmp)|*.Bmp|All Files(*.*)|*.*"
CommonDialog1.FilterIndex=1
3.在工程中添加现有窗体时发生加载错误
在使用"工程"菜单中的"添加窗体"命令添加一个现存的窗体时经常发生加载错误,绝大多数是因为窗体名称冲突的缘故。例如,假定当前打开了一个含有名称为Forml的工程,如果想把属于另一个工程的Forml窗体装入则肯定会出错。
[注意]
窗体名与窗体文件名的区别。在一个工程中,可以有两个窗体文件名相同的窗体(分布在不同的文件夹中),但是绝对不能同时出现两个窗体名相同的窗体。
4.实时菜单的创建'
实时菜单是由应用程序根据需要动态创建的。在VB中,常见的实时菜单是"文件"菜单,该菜单显示了最近所使用的工程。
创建实时菜单必须结合控件数组,用Load语句创建菜单项,用UnLoad清除菜单项。
创建实时菜单的步骤;
(1)在菜单编辑器中建立样本菜单项。
样本菜单项的属性设置见表2.8.4所示。设置Index为o,表明样本菜单项是控件数组的一个元素,其下标为o。样本菜单项的Name属性是必须的,它将作为控件数组的名称。在-F面假定数组名为NameA~ay。Visible可以设为True,设为False表示初始时该菜单项不可见。
表2.8.4 实时菜单样本菜单项
属性 Name Caption Index Visible
设置值 必需的 可以没有 0 False
(2)在程序中用Load语句创建菜单项。
例如,Load NameArray(1)创建一个新的菜单项(在控件数组中的F标为1),然后将其 Visible属性设置True,同时设置Caption属性。
动态创建的菜单项继承了除了Index之外的绝大部分属性,所以要对Caption和Visible属性进行设置。另外,样本菜单项在菜单系统中的位置决定了新菜单项出现的位置。
(3)为实时菜单项编写代码。
每个实时菜单项都是控件数组的一个成员,具有相同的名称,并且共享事件过程。
下面是一个实时菜单项代码示例:
SubNameArray_Click(Index As Integer)
Select Case Index
Case 0
MsgBox("NameArmy(0)(样本菜单项) is clicked!")
Case 1
MsgBox("NameArray(1)(第一个实购菜单项)isclicked!")
Case 2
MsgBox("NameArray(2)(第二个实时菜单项)is clicked!")
End Select
End Sub
(4)删除实时菜单项。
尽管把Visible设为False,程序运行时实时菜单项不会显示,然而有时还是需要把实时菜单项从内存中销毁。