李颂慈哪里人:VB应用程序结构

来源:百度文库 编辑:中财网 时间:2024/04/29 04:57:01

VB应用程序结构

一个应用程序实际上是使计算机完成特定任务的指令集。应用程序的结构就是指组织指令的方法,也就是指令存放的位置以及它们的执行顺序。应用程序越复杂,对结构的要求也就越明显。而且如何在一个大的应用程序中快速查找特定指令,程序的指令也起着重要的作用。

一、模块

VB的代码存储在模块中。在VB中提供了三种类型的模块:窗体模块、标准模块和类模块。

简单的应用程序可以只有一个窗体,所用的程序都驻留在窗体模块中,而当应用程序庞大复杂时,就要另外附加窗体。最终可能有几个窗体中有一些共同都要执行的代码,为了在两个窗体中不产生重复代码,可创建一个独立的模块,用它实现代码公用。该独立模块即是标准模块。此外还可以建立包含共享代码与数据的类模块。

每个标准模块、类模块和窗体模块可包含:

  • 声明。 可将常数、类型、变量和动态链接库(DLL)过程的声明放在窗体、类或标准模块的声明部分。每个模块只有一个声明部分。
  • 过程。每个模块可有多个过程,过程是划分VB代码的最小单元,每个过程是一个可执行的代码片段。VB中主要有事件过程、子过程、函数过程或者属性过程等。

1、各种模块类型

(1)窗体模块

由于VB是面向对象的应用程序开发工具,所以应用程序的代码结构就是该程序在屏幕上表示的对应模型。根据定义,对象包含数据和代码。应用程序中的每个窗体都有一个相对应的窗体模块(文件扩展名为.frm)

窗体模块是VB应用程序的基础。窗体模块可以包含处理事件的过程、通用过程以及变量、常数、自定义类型和外部过程的窗体级声明。写入窗体模块的代码是该窗体所属的具体应用程序专用的;也可以引用该程序内的其它窗体和对象

每个窗体模块都包含事件过程,在事件过程中有为响应该事件而执行的程序段。窗体可包含控件。在窗体模块中,对窗体上的每个控件都有一个对应的事件过程集。除了事件过程,窗体模块还可包含通用过程,它对来自该窗体中任何事件过程的调用都作出响应。

(2)标准模块

可将那些与特定窗体或控件无关的代码放入标准模块中。标准模块中包含应用程序内的允许其它模块访问的过程和声明。它们可以包含变量、常数、类型、外部过程和全局声明或模块级声明。写入标准模块的代码不必固定在特定的应用程序上。

使用标准模块

标准模块或代码模块是具有文件扩展名.bas,并包含能够在程序任何地方使用的变量和过程的特殊文件。

在编写程序时,很可能会遇到一些使用相同变量和例程的窗体和事件过程。在缺省状态下,变量对于事件过程来说是局部的,就是说仅能在创建这些变量的事件过程中读取或者修改变量。与之相似,事件过程对于创建它们的窗体来说也是局部。为了在工程中的所有窗体和事件中共享变量和过程,需要在该工程的一个或多个标准模块中对它们进行声明和定义。

标准模块让你能够在整个程序中共享变量和过程。

正如窗体一样,标准模块被单独列在Project(工程)窗口内,并可通过使用File(文件)菜单中的SaveModulel As菜单项存盘。但是,与窗体不同,标准模块不包含对象或属性设置而只包含可在代码窗口中显示和编辑的代码。

创建并保存标准模块

创建标准模块:

如要在程序中创建新的标准模块,那么单击工具条“Add Form(添加窗体)”按钮上的下箭头并单击“Mode(添加模块)”,或者单击“Project(工程)”菜单中的“Add Module(添加模块)”菜单项,可以在工程中创建一个空的标准模块。

VB在工程中增加一个名为“Module1”的标准模块。该模块对应的代码窗口被打开,在这个代码窗口中的“对象”和“过程”列表框的内容表明该标准模块的通用声明已被打开。在此所声明的变量与过程在整个程序都可以使用。

这时打开“工程资源管理器”窗口,便能看到整个工程资源管理器窗口的一个新文件夹中列出了你添加到程序中的标准模块。括弧中的文件名Module1是该模块的缺省文件名。该模块的对象名(该模块在程序中的名称)显示在括弧的左侧。

从“属性窗口”可见,由于该模块不包含对象,因此它惟一的属性就是Name属性。可以修改Name属性,如改为modVariables,然后按ENTER键。

你也可以通过使用“Project(工程)”菜单中的“Add File(添加文件)”菜单项将此文件通过其文件名加载到另一个工程中。

保存标准模块:

在“File(文件)”菜单中,单击“Save Module1 As(Module1另存为)”菜单项将这个空的标准模块存盘。该标准模块作为.bas文件保存到磁盘,并且“工程资源管理器”窗口中该模块的文件名也同时更新。

删除标准模块

如果要从工程中删除标准模块,那么在“工程资源管理器”窗口单击选中该模块,然后单击“Project(工程)”菜单中的“Remove(删除)”菜单项(或右击选择)。Remove(删除)操作并未将该模块从你的硬盘中删除,但它删除了该模块与当前工程之间的链接。

声明公用变量

在标准模块中声明全局变量十分简单,键入关键字Public,后跟该变量名。

缺省状态下,公用变量在模块中被声明为变体类型,但是可以通过使用As关键字来指定相应类型,可以把公用变量声明为某个指定的基本类型。

例题:设计一个模拟幸运数字机游戏的程序“幸运7”,即出现数字7时,系统发出响声。

没有使用标准模块时:

界面:

2个命令按钮,4个标签,一个图像框(Stretch属性属性设置为True,并加载一副图元文件图片—VB自带)。

没有数字7时的界面 有数字7时的界面

代码:

Private Sub Command1_Click()
    Randomize
    Image1.Visible = False    '隐藏硬币图像
    Label1.Caption = Int(Rnd * 10)    '得到随机数
    Label2.Caption = Int(Rnd * 10)
    Label3.Caption = Int(Rnd * 10)    '当某个标题为7时,显示一堆硬币并响铃
    If (Label1.Caption = 7) Or (Label2.Caption = 7) Or (Label3.Caption = 7) Then
        Image1.Visible = True
        Beep    '发出声音
    End If
End Sub

[程序演示]

使用标准模块时:

再添加一个标签控件Label5,Caption设置为"Wins:0",Name属性为"lblWins"。

界面:

代码:

  • 添加一个新的标准模块,并在标准模块中键入:
        Public Wins
    该程序语句在程序中声明了一个变体类型的公用变量。当你的程序运行时,程序中的每一事件过程都能访问这个变量。
  • 在事件过程中的Beep语句下面一行键入下列语句:
        Wins = Wins + 1
       
    LblWins.Caption = "Wins:" & Wins
  • 保存工程,会将该模块存盘。

[程序演示]       [程序下载]

公用变量Wins在此十分有用,因为在每次调用Command1_Click事件过程中它的值一直保持不变,即可以累加。

如果在Command1_Click事件过程中以局部变量方式声明了Wins,则每次调用Command1_Click事件过程时都会重新设置该变量的值,这样的结果不能累加(可以试一试)。

(3)类模块

在VB中类模块是面向对象编程的基础。可以在类模块中编写代码建立新对象。这些新对象可以包含自定义的属性和方法。实际上,窗体正是这样一种类模块,在其上可安放控件,可显示窗体窗口。类模块在一定程度上与普通控件有一些类似,例如它们都有自己的属性,可以响应的事件,可以执行方法等等。但是,普通的控件或者窗体都是有其图形界面的,而类模块是没有的。

用类模块创建对象,这些对象可被应用程序内的过程调用。标准模块只包含代码,而类模块包含代码又包含数据,可视为没有物理表示的控件。

2、模块设计的考虑

在VB中创建应用程序的最重要的部分是设计阶段。首先需要为应用程序设计一个用户界面,这个界面的设计比较容易确定。但设计代码的结构就是一项较复杂的工作。构造应用程序的方法不同,可能会造成应用程序的性能以及代码的可维护性、可使用性不同。

VB应用程序呈层次结构。典型应用程序包括若干个模块:应用程序中每个窗体的窗体模块、共享代码的标准模块和类模块。每个模块包含若干含有代码的过程:Event(事件)过程,Sub过程或Function过程及Property(属性)过程。

在很多情况中都存在着多个对象共享的重复过程。这种情况最好创建一个共享的子过程,使大家都可以调用它。即使将来修改这些子程序,则只在一个地方修改就可进行全部修改,这将大大提高程序的可读性和可维护性。可以把所有的共享操作放在标准的模块中

3、代码编辑器

VB代码编辑器是一个窗口,大多数代码都在此窗口上编写,它有许多便于编写VB代码的功能(如下面介绍的“自动”功能)。并将模块中的代码再细分出与对象对应的独立部分用"对象列表框"实现各对象间的切换。

对于窗体模块,该列表包括一个“通用段”,一个窗体所包含的“每一个控件的段”;对于类模块,列表包含一个“通用段”和一个“类段”;对于标准模块,只有一个“通用段”被显示,因为标准模块不支持事件。。

VB代码编辑器的一些功能和用法:

(1)自动完成编码

VB能自动填充语句、属性和参数,这些性能使编写代码更加方便。在输入代码时,编辑器列举适当的选择。通过"工具"菜单上的"选项"命令访问"选项"对话框,在"选项"对话框的"编辑器"选项卡上可以选择"自动列出成员特性",由它们决定是允许还禁止代各码的设置值。在代码中输入一控件名时,"自动列出成员特性"会显示出这个控件的下拉式属性表。键入属性名的前几个字母,就会从表中选中该名字,按Tab键将完成输入。当不能确认给定的控件有什么样的属性时,这个选项是非常有帮助的。即使选择了禁止"自动列出成员特性",仍可使用CTRL+J组合键得到这种性能。

"自动快速信息"功能可显示语句和函数的语法。

(2)书签

在代码编辑器中书签可用来标记代码的某些行,以便以后可以很容易地返回这些行。书签开关的切换以及定位到已有书签的命令可以在“编辑”菜单中书签选项中得到。

二、过程

一个应用程序是由若干模块组成的,而每个模块又是由若干个更小的代码片段组成,将组成这些模块的代码片段称为过程。通过过程,可以将整个程序按功能进行分块,每个过程用来完成一项特定的功能。使用过程来组织代码,不仅可以使程序的结构更加清晰,而且便于查找和修改代码。当在其它工程中使用某个相同功能时,可以将过程作为可重用的代码,直接拿到该工程中使用。

(1)VB中过程基本分类:

  • 事件过程:当发生某个事件时,对该事件作出响应的程序段。
  • 函数过程(Functi9on Procedure):函数过程用来完成特定的功能并返回相应的结果。在事件或其它过程中可按名称调用函数。函数过程能够接收参数,并且总是以该函数名返回一个值。这类过程一般用于完成计算任务。
  • 子过程:子过程是没有返回值的函数。在事件过程或其它过程中可按名称调用子过程。子过程能够接收到参数,并可用于完成过程中的任务并返回一些数值。但是,与函数过程不同,子过程不返回与其特定子过程名相关联的值。子过程一般用于接收或处理输入数据、显示输出或者设置属性。
  • 属性过程:属性过程用来创建和操作程序中用户定义的属性。这是一种有用的,在某种程度上相当高级的特性,它使能够定制现有的VB控件并通过创建新的对象,属性和方法来扩展VB语言。
  • 通用过程:允许把一个经常用到的过程(如子过程、函数过程)以熟悉的名称写入某个标准模块中。

在标准模块中可以创建三种类型的通用过程:函数过程、子过程和属性过程。

(2)使用过程编程的优点

  • 消除了重复语句行。
  • 使程序更易阅读。
  • 简化了程序开发
  • 其它程序可重复使用该通用过程
  • 扩展VB语言

(3)过程的作用范围

和变量一样,函数、子过程和属性过程也有一定的作用域。一个函数、子过程和属性过程的作用域决定了“过程”对于程序的其他部分或者对象的访问能力。这些过程可以被用于窗体、标准模块或者类模块中。

如果窗体或模块中的过程定义为Public,他们就可以在其他窗体和模块中使用。这时的过程已经不再是某个模块中的私有过程,它可以在程序的任何地方进行调用。

如果过程定义为Private,则这些过程只能在他们所属的模块中使用,而不能使用于其他的模块中。

如果定义过程为Static,表示在调用该过程后保留过程的局部变量的值。Static属性对在Sub外声明的变量不会产生影响。

在类模块中过程还可以定义为Friend,表示该过程在类模块中都是可见的,但对于使用该类对象的实例是不可见的。

子过程是在响应事件时执行的程序段。将模块中的代码分成子过程后,在应用程序中查找和修改代码变得更容易了,子过程类似于用户自定义函数,不同之处是子过程不返回与其名称相关联的值,子过程一般用来从用户那里得到输入数据、显示或打印信息,或者操纵与某一条件相关的几种属性。子过程也用来在过程调用中处理和返回若干个变量。大多数函数只能返回唯一一个值,但子过程却能够返回多个值。

(1)定义Sub子过程

定义子过程的语法格式:

[Private|Public|Friend][Static]Sub过程名[(参数列表)]

    [程序段]

[Exit sub]

    [程序段]

End Sub

例如:

Private Sub Subtest()
    Print"This is a Sub procedure"
End Sub

说明:

1)Sub过程以Sub开头,以End Sub结束,在Sub和EndSub之间是描述过程操作的语句块,称为“过程体”或“子程序体”。

格式中个参量的含义如下:

Public(可选的):表示Sub过程是公有过程,可以在程序的任何地方调用它。各窗体通用的过程通常在标准模块中用Public定义,在窗体层定义的通用过程一般在本窗体中使用,也可以在其它窗体模块中使用。

Private(可选的):表示Sub过程是私有的,只能被本模块中的其它过程访问,不能被其它模块中的过程访问。

Friend(可选的):只能在类模块中使用。表示该Sub过程在整个工程中都可见的,但对对象实例的控件者是不可见的。

Static(可选的):表示在每次调用过程时,局部变量的值保持不变。如果省略Static,则局部变量默认为”自动“的,即在每次调用过程时,局部变量被初始化为0或空字符串。Static属性对在Sub外声明的变量不会产生影响,即使过程中也使用了这些变量。

注:如果没有使用Private,Public或Friend等,Sub过程在缺省情况是公用的。

过程名(必需的):Sub的名称遵循标准的变量命名约定。在同一个模块中,同一个变量名不能既做Sub过程名,又做Function过程名。

参数列表(可选的):代表在调用时要传递给Sub过程的参数的变量列表。如果不止一个参数,则由逗号分开。

“参数列表”指明了调用时传送给过程的参数的类型和个数,每个参数的格式为:

    [ByVal]变量名[()][As数据类型]

这里的“变量名”是合法的VB变量名或数组名,如果是数组,则要在数组名后面加一对括号;

“数据类型”指的是变量的类型,如果省略,则默认为Variant;

变量名前面的“ByVal”是可选的。如果加上ByVal,则表明该参数是“传值”参数,否则为“引用”参数。关于这方面内容将在后面详细介绍。

2)在过程体内可以用一个或多个Exit Sub语句,执行从一个Sub过程中退出。

3)Sub过程不能嵌套。也就是说,在Sub过程内,不能定义Sub过程或Function过程;不能使用GoSub,GoTo或Return来进入或退出Sub过程,只能通过调用执行Sub过程,而且可以嵌套调用。

4)Sub过程可以是递归的,也就是说,该过程可以调用自己来完成某个特定的任务。不过,递归可能会导致堆栈上溢。通常Static关键字和递归的Sub过程不在一起使用。

5)在过程调用中,发送到子过程的参数值和类型必须与子过程声明语句中参数值和类型相符。

下面是一个Sub过程的例子:

    Sub Tryout(x AsInteger,ByVal y As Integer)
   
     x=x+100
        y=y*6
        print x,y
    End Sub

上面的过程有两个形式参数,其中第二个前面有ByVal,表明该参数是一个传值参数。

过程可以有参数,也可以不带任何参数——称无参过程。如下例:

    Sub ContinueQuery()
        Do
            Response$=InputBox$("Continue(Yor N)?")
            IfResponse$="N" Or Response$="n" Then End
            IfResponse$="Y" Or Response$="y" Then Exit Do
        Loop
    End Sub

对于无参过程,调用时只写过程名即可。

(2)建立Sub过程

Sub过程可以在标准模块中建立,也可以在窗体模块中建立。

标准模块中建立过程的方法有两种。

第一种方法操作步骤为:

1)“添加模块”,同时打开模块“代码窗口”;

2)“工具”菜单/“添加过程”命令,打开“添加过程”对话框;

3)在“名称”框中输入要建立的过程的名字(例如Tryout);

4)在类型栏内选择要建立的过程的类型。如果建立子过程,则选择“子过程”,同样,如果建立的是函数过程,则选择“函数过程”;

5)在范围栏内选择过程的适用范围。选择“公有的”,则建立的过程可用于本工程内所有窗体模块,选择“私有的”,只能用于本标准模块。

6)“确定”。

第二种方法:直接在模块“代码窗口”中键入过程的名字,如键入“SubTryout()”后,按回车键。

窗体模块中建立过程很简单,和上面的第二种方法类似,只是在窗体模块的“代码窗口”中输入。

双击窗体进入“代码窗口”,在对象框中选择“通用”,“过程”框中选择“声明”,然后直接在窗口内键入“SubTryout()”后,按回车键。

建立了过程后,在模块“代码窗口”中的“过程”框中,将显示该过程名。

(3)查看其它模块中的过程

要查看其它模块中的过程,请按照以下步骤:

1)"视图"菜单中选取"对象浏览器"

2)在"工程/库"框中选择工程;

3)在"类/模块"列表中选择模块,并在"成员"列表中选择过程;

4)单击右键,在弹出菜单中选取"查看定义",查看选中的过程。

(4)调用子过程

要执行一个过程,必须调用该过程。

调用Sub过程有两种方法:使用Call语句和直接把过程名作为一个语句使用。

1)用Call语句调用Sub过程

格式:Call过程名[(实际参数)]

如果过程没有参数,则“实际参数”和括号可以省略,否则应给出相应的实际参数,并放在括号内。如:

    CallTryout(a,b)

2)把过程名作为一个语句来使用

这时,省略关键字Call。例如:

    Tryoura,b

说明:当使用Call语法时,参数必须在括号内。若省略Call关键字,则也必须去掉参数两边的括号。

例如,先定义一个名为Display的子过程:

Sub Display(disString As String)
    Cls
    Print disString
End Sub

调用这个子过程(在窗体上添加一个命令按钮):

Private Sub Command1_Click()
    Call Display("请显示字符串")     ' 或者改为:Display"请显示字符串" 
End Sub

(5)例题

下面将通过一些例子,理解过程的使用。

例1:编写一个计算矩形面积的Sub过程,然后调用该过程计算矩形面积。

程序如下:

    Sub Recarea(Rlen,Rwid)
        Dim Area 
        Area=Rlen*Rwid
        MsgBox"Total Area is"&Area
    End sub

    Private Sub Command1_Click()
        Dim A, B
        A = InputBox("what is the length?")
        A = Val(A)
        B = InputBox("what is the width?")
        B = Val(B)
        Recarea A, B
    End Sub

在上面的例子中,Rlen、Rwid称为窗体参数。除窗体参数外,控件也可以作为通用过程的参数,即在一个通用过程中设置相同性质控件所需要的属性,然后用不同的控件调用此过程。

例2:编写一个通用过程,在过程中设置字体属性,并调用该过程显示指定的信息。

界面:

在窗体上添加两个文本框,大小适中。

代码:

Sub Fontout(TextCtrl1 As Control, TextCtrl2 As Control)
    With TextCtrl1
        .FontName = "隶书"
        .FontSize = 24
        .FontItalic = True
        .FontUnderline = True
    End With
    With TextCtrl2
        .FontName = "华文彩云"
        .FontSize = 36
        .FontItalic = False
        .FontUnderline = False
    End With
End Sub

Private Sub Form_Click()
    Fontout Text1, Text2
End Sub

Private Sub Form_Load()
    Text1.Text = "欢迎使用"
    Text2.Text = "Visual Basic 6.0"
End Sub

[程序演示]

例3:实现字体闪烁效果。

界面:

在Form1上配置一个计时器控制Timer1、标签控件Label1、命令控件Command1。Timer1的Enabled设置为True;Label1的Caption设置为需闪烁的文字,如“蚌埠电大”;Command1中的Caption设置为“退出”。

代码:

Option Explicit


Private Sub Form_Load()
    Timer1.Interval = 555   '设置字体闪烁间隔时间
End Sub


Private Sub Timer1_Timer()    '利用计时器控件Timer有规律地改变字体颜色(ForeColor)
    Chc Label1, 3, 12, 13  '调用过程,Chc过程可循环地改变字体颜色 (ForeColor)
End Sub


Sub Chc(Cont As Control, Color1 As Integer, Color2 As Integer, Color3 As Integer)
    If Val(Cont.Tag) = Color1 Then
            Cont.Tag = Color2
        ElseIf Val(Cont.Tag) = Color2 Then
            Cont.Tag = Color3
        ElseIf Val(Cont.Tag) = Color3 Then
               Cont.Tag = Color1
    Else
        Cont.Tag = Color1
    End If
    Cont.ForeColor = QBColor(Cont.Tag)
End Sub


Private Sub Command1_Click()
    End
End Sub


[程序演示]       [程序下载]

例3:见教材P182自测题五的第4题,设计一个程序,可以对Image控件中的图像进行放大和缩小。

如果不用过程,代码为:

Option Explicit

Private Sub Command1_Click()
    Image1.Stretch = True
    Image1.Left = Image1.Left + Image1.Width * 0.5 / 2
    Image1.Top = Image1.Top + Image1.Height * 0.5 / 2
    Image1.Width = Image1.Width - Image1.Width * 0.5
    Image1.Height = Image1.Height - Image1.Height * 0.5
End Sub

Private Sub Command2_Click()
    Image1.Stretch = True
    Image1.Left = Image1.Left + Image1.Width * (-1) / 2
    Image1.Top = Image1.Top + Image1.Height * (-1) / 2
    Image1.Width = Image1.Width - Image1.Width * (-1)
    Image1.Height = Image1.Height - Image1.Height * (-1)
End Sub

本例也可以使用过程,代码为:

Option Explicit

Private Const ratio_small As Single = 0.5
Private Const ratio_large As Single = -1#

Private Sub bianhuan(ByVal img As Image, ByVal ratio As Single)
    img.Stretch = True
    img.Left = img.Left + img.Width * ratio / 2#
    img.Top = img.Top + img.Height * ratio / 2#
    img.Width = img.Width - img.Width * ratio
    img.Height = img.Height - img.Height * ratio
End Sub

Private Sub Command1_Click()
    bianhuan Image1, ratio_small
End Sub

Private Sub Command2_Click()
    bianhuan Image1, ratio_large
End Sub

[程序演示]

3、Function过程

VB包含内置的或内部的函数,还可用Function语句编写自己的Function过程。在定义了一个函数之后,可以像调用任何一个VB的内部函数一样使用它,即可以在任何表达式,语句或函数中引用它。

函数过程是标准模块中位中Function语句与EndFunction语句之间的一系列语句。函数中的这些语句完成某些有意义的工作,一般是处理文本,进行输入或计算一个值。

通过将函数名与任何所需的参数一起置于一条程序语句中,可以调用该函数。换句话说,使用函数过程与使用内置函数(比如Time,Int或Str等)的方法完全相同。

每个函数完成一种服务,比如进行计算并返回一个值。

在标准模块中声明的函数在缺省的状态下是公用函数,它们可在任何事件中过程中使用。

(1)Function(函数)过程的定义

Function过程的定义格式:

[Public|Private|Friend|Static]Function过程名[(参数列表)][As数据类型]

    [程序段]

    [过程名=表达式]

    [Exit Function]

    [程序段]

    [过程名=表达式]

End Function

说明:

1)Function过程以Function开头,EndFunction结束,之间是描述过程操作的语句块,即“过程体”或“函数体”。格式中的“过程名”、“参数列表”、Public、Private、Friend、Static、ExitFunction的含义与Sub过程相同。“As数据类型”是由Function过程返回的值的数据类型,如省略,则为Variant。

如:已知直角三角形两直角边的值,计算斜边,可定义一个Function函数。

Function hypotenuse(A as integer,B as integer) as string

    hypotenuse=sqr(A^2+B^2)

End function

再例如:

Function BinarySearch(Lower,Upper AsInteger) As Boolean

    ……
    If Lower= Upper Then
        BinarySearch = True
       Exit Function
    Else
       BinarySearch = False         '过程名=表达式
    End If
    ……

End Function

2)调用Sub过程相当于执行一个语句,不返回值;而调用Function过程要返回一个值,因此可以像内部函数一样在表达式中使用。

在VB中调用Function过程的方法和调用任何内部函数的方法是一样的,如调用上面的“hypotenuse”函数:

  label1.caption=hypotenuse(Cint(text1.text),cint(text2.text))

  strX=hypotenuse(width,height)

函数总是以该函数的名称返回给调用过程一个值。因此,函数中的最后一行语句往往是将函数的最终计算结果放入“过程名”中的赋值语句。例如,下文中的函数过程total1用来为某个工程计算,然后将结果赋值给total1名。

  Function total1(cost)

   t1=cost*0.05

   t2=cost*0.015

   total1=t1+t2 '将结果赋值给Function函数total1。

  end function

Function过程返回的值放在上述格式中的“表达式”中,并通过“过程名=表达式”把它的值赋给“过程名”,如果在Function过程中省略“过程名=表达式,则该过程返回一个默认值:数值函数过程返回0值;字符串函数过程返回空字符串;variat函数则返回empty。如果在返回对象引用的Function过程中没有将对象引用赋给name(通过set),则函数返回nothing。一般地,为了能使一个Function过程完成所指定的操作,通常要在过程体中为“过程名”赋值。

3)前面讲过,过程不能嵌套。因此不能在事件过程中定义通用过程(包括Sub过程和Function过程),只能在事件的过程内调用通用过程。

(2)建立Function过程

前面提到的建立Sub过程的三种方法也可用来建立Function过程,只是当用第一种方法建立时,在对话框的“类型”栏内选择“函数”。

(3)调用函数过程

例如:编写一个求最大公约数(GCD)的函数过程。

程序如下:

Function gcd(ByVal x As Integer, ByVal y As Integer) As Integer
    Do While y <> 0
        reminder = x Mod y
        x = y
        y = reminder
    Loop
    gcd = x
End Function

求最大公约数的方法:
两个数a、b,如果b能除尽a,则是这两个数的最大公约数(如6和3的最大公约数是3),否则:
        GCD(a,b)=GCD(b,a Mod b)
例如:GCD(126,12)=GCD(12,126 Mod 12)=GCD(12,6)=6

下面介绍如何调用这个过程。

调用Function过程比较简单,可以像使用VB内部函数一样调用。因为Function过程返回一个值,所以完全可以将它看成一个函数,与内部函数(如Sqr,Str,Chr等)没什么区别,只不过内部函数由语言系统提供,而Function过程由用户自己定义。

如下面的事件过程调用了上面的“gcd”函数:

Private Sub Form_Click()
    Dim a As Integer, b As Integer
    a = 96: b = 64
    x = gcd(a, b)      '注意这里的x与上面的x无关。
    Print "G.C.D="; x
End Sub

最后运行的结果为:

G.C.D = 32

三、过程的使用

1、再谈Sub过程与Function过程的调用及使用

调用过程有很多方法,它们与过程的类型、位置以及在应用程序中的使用方式有关。调用Sub过程与Function过程不同,在表达式中,调用Sub过程的是一个独立的语句,而调用函数过程只是表达式的一部分。Sub过程还有一点与函数不一样,它不会用名字返回一个值。而与Function过程一样,Sub过程也可以修改传递给它们的任何变量的值。

(1)调用Sub过程

有两种方法:

CallMyProc(FirstArgument,SecondArgument)

MyProc FirstArgument,SecondArgument

说明 当使用Call语法时,参数必须在括号内。若省略Call关键字,则也必须去掉参数两边的括号

(2)调用函数过程

方法一:通常,调用自定义的函数过程的方法和调用VB内部函数过程的方法一样,即在表达式中写上名字。

方法二:也可以像调用Sub过程一样调用Function过程,如调用函数过程Year:

    Call Year(Now)

    Year Now

当用这种方法调用函数时,放弃返回值。

一般来说,函数都赋值给某些变量或属性。

(3)调用其它模块中的过程

在工程中的任何地方都能调用其它模块中的公用过程。调用其它模块中的过程的各种方法,取决于该过程是在窗体模块中、类模块中还是标准模块中。

调用窗体模块中的过程:

所有窗体模块的外部调用过程必须指向包含此过程的窗体模块。如在窗体模块Form1中包含SomeSub过程,则可使用语句:“CallForm1.SomeSub(参数列表)”来调用Form1中的SomeSub过程。

调用类模块中的过程:

类模块中的过程与窗体中调用过程类似,在类模块中调用过程要调用与过程一致并且指向类实例的变量。例如DemoClass是类Class1的实例:

    DimDemoClass as New Class1
    DemoClass.SomeSub

但是不同于窗体的是,在引用一个类的实例时,不能用类名作限定符(如不能像窗体中用Form1.SomeSub那样使用Class1.SomeSub),必须首先声明类的实例为对象变量(如上面的DemoClass)并用该变量名引用它。

使用标准模块中的过程:

如果过程名是惟一的,则不必在调用时加模块名。无论是在模块内,还是在模块外调用,结果总会引用这个惟一过程。在整个应用程序中,如果过程仅出现在一个地方,这个过程就是惟一的。

如果两个以上的模块都包含同名的过程,例如:对于在Module1和Module2中都有名为“CommonName”的过程,在调用该过程时,分两种情况:

  • 如果在同一模块内调用一个公共过程就会直接运行该模块内的过程。如在Module2中直接调用“CommonName”则运行Module2中的“CommonName”过程,而不是Module1中的“CommonName”过程。
  • 如果在Module1中调用Module2中的“CommonName”过程,那就必须使用模块名进行限定,要用语句:Module2.CommonName(参数列表)。

在下面的练习中,将在前面的“幸运7”程序中添加一个函数,用来计算游戏中的获胜率(Spin中出现一个或多个7的百分比)。为此,可在标准模块中增加一个名为Rate的函数和一个名为Spins的公用变量。然后每次单击“开始”按钮时都调用Rate函数。还要在窗体上新创建的标签中显示计算结果。

在窗体上再添加一个标签,Name属性为“lblRate”、Caption属性为“0.0%”、ForeColor是“红色”,图像框的“Stretch”属性仍设置为True。

界面如下:

标准模块窗口中键入如下代码(增加一个名为Rate的函数和一个名为Spins的公用变量):

Option Explicit
Public Wins
Public Spins     '再定义一个公用变量,用来记录产生随机数的总数,即单击按钮的次数。


Function Rate(Hits, Attempts) As String   '建立一个名为Rate的函数
    Dim Percent
    Percent = Hits / Attempts
    Rate = Format(Percent, "0.0%")
End Function

说明:Rate函数通过将Hits参数除以Attempts参数来确定获胜率,然后通过使用Format(格式)函数调整显示结果。Rate函数被声明为字符串类型,原因在于Format(格式)函数的返回值是字符串。Hits参数和Attempts参数是这两个变量的占位符,在函数调用过程中,它们将被传递给函数。Rate函数是通用的,可与任何数字或变量一同使用,而不仅限于Wins和Spins。

调出窗体代码的Command1_Click事件过程。,在其中添加调用函数语句,如下:

Private Sub Command1_Click()
Randomize
Image1.Visible = False   '隐藏硬币图像
Label1.Caption = Int(Rnd * 10)   '得到随机
Label2.Caption = Int(Rnd * 10)
Label3.Caption = Int(Rnd * 10)   
Spins = Spins + 1
If (Label1.Caption = 7) Or (Label2.Caption = 7) _
Or (Label3.Caption = 7) Then
Image1.Visible = True
Beep       '当某个标题为7时,显示一堆硬币并响铃
Wins = Wins + 1   '记录产生7的次数。
lblWins.Caption = "Wins:" & Wins
End If
lblRate.Caption = Rate(Wins, Spins)
End Sub

其中红色部分为新加内容。

[程序演示]       [程序下载]

上例中的过程名Rate是惟一的,可以在任何模块中直接引用。

(4)子过程和函数过程的嵌套使用

例:见教材P239自测题七的第一题,将华氏温度换算为摄氏温度。

添加一个命令按钮。

代码如下:

Option Explicit
Dim temp

Sub ConvertTemp()
    temp = InputBox("请输入华氏温度。", 1)
    MsgBox "温度为 " & Celsius(temp) & " 摄氏度。"
End Sub

Function Celsius(fDegrees)
    Celsius = (fDegrees - 32) * 5 / 9  '将华氏温度换算为摄氏温度
End Function

Private Sub Command1_Click()
    Call ConvertTemp
End Sub

(5)Go Sub...Return语句

在一个过程中利用Go Sub...Return语句可以调用该子过程中的某段子程序,执行完毕后再返回。其语法规则如下:

GoSub line

line

Return

可以在过程中的任何地方使用GoSub和Return,但是GoSub和与之相应的Return语句必须放在同一个过程中。一个子程序中可以包含一个以上的Return语句,但是当碰到第一个Return语句时,程序就会返回到紧接在刚刚执行的GoSub语句之后的语句继续执行。

注意:不能使用 Go Sub...Return来进入或退出Sub过程。

下面的示例使用gosub来调用子过程里的一段子程序。Return语句则将执行返回到Gosub的下一个语句。

sub gosubdemo()

    dim num
    num=inputbox("enter a positive number to be divided by 2.") '请求用户输入一个数
    if num>0 thenGosub MyRoutine
        debug.print num
        exit sub
    MyRoutine:
        num=num/2
        Return   '将控制返回Gosub之后的语句

End Sub

2、过程参数传递

过程通常需要某些程序当前状态的信息才能完成它的工作。程序当前状态信息也包含在调用过程时传递到过程内的变量。当将变量传递到过程时(完成形式参数与实际参数的结合),称变量为参数。

(1)参数的数据类型

过程的参数类型缺省为Variant数据类型。不过,也可以声明参数为其它数据类型。例如:函数接受一个字符串变量和一个整型变量:

Function WhatForLunch(WeekDay As String,Hour AsInteger) AsString

If WeekDay="Saturday" then
    WhatForLunch="Fish"
ELse
    WhatForLunch="Chicken"
End If
If Hour>2 Then WhatForLunch="Sorry,It's too late!"

End Function

(2)按地址传递参数

按地址传递参数过程(也称“引用”)使得过程通过变量的内存地址去访问实际变量的内容。将变量传递给过程时,在过程中可改变变量的值。按地址传递参数在VB中是缺省的。

如果给按地址传递的参数指定数据类型,就必须将这种类型的值传递给参数,也可以给参数传递一个表达式,VB计算表达式,并按要求的类型将值传递给参数。

当变量通过地址传递时,对变量的任何修改都被传递给调用过程。传址参数调用在修改调用过程中的变量方面具有显著的优点,只是不要在过程中无意地修改变量。

例如,下面的子过程声明和调用:

Sub CostPlusInterest(Cost,Total)

    Cost=Cost*1.05 'add 5% toCost
   
Total=Int(Cost) 'then make integer and return

End Sub

调用:

……

 Price=100
   
Total=0
   
CostplusinterestPrice,Total
   
Print Price; "at 5% interest is ";Total

本例中,程序将两个传址变量传递给CostPlusInterest过程:Price和Total。程序员计划在随后的Print方法中使用更新的Total变量,但不慎忘掉了Price变量在过程的中间步骤中被更新,所以程序运行时,产生结果如下:

  105 at 5% interest is 105

没有达到预期的效果。

(3)按值传递参数

避免上述问题的一种好的做法是按值传递参数。按值传递参数传递的只是变量的副本。如果过程执行过程中改变了这个值,则所有变动只影响副本而不会影响变量本身。使用ByVal关键字指出参数是按值来传递的。

例如:对上例加以修改,就不会发生上面出现的错误,改动如下:

Sub CostPlusInterest(ByVal Cost,Total)

当使用ByVal声明Cost参数时,就会产生正确的结果:

    100 at 5% interest is 105

如果不想依赖于ByVal关键字,也可以换一种方法防止所传递的变量被修改:将变量置于括弧内而把它转换为文字值,如上例中,调用costplusinterest过程的语句为:

    costplusinterest(price),total,

同样可以得到正确结果。

再例如,有下面代码:

Option Explicit

Function Multiply(ByVal n As Integer) As Integer
    Multiply = 1
    Do While n > 0
        Multiply = Multiply * n  '求n!
        n = n - 1
    Loop
End Function

Private Sub Form_Click()
    Dim sum As Integer, i As Integer
    For i = 5 To 1 Step -1
        sum = sum + Multiply(i)
    Next
    Print "sum="; sum
End Sub

运行后结果为153(5!+4!+3!+2!+1!)。

若将函数的参数n前的“ByVal”去掉,结果为120(5!),这显然不是编者的初衷,是错误的。

究竟什么时候用传值方式,什么时候用传地址方式,没有硬性规定。下面几条规则可供参考:

1)对于整形、长整形或单精度参数,如果不希望过程修改实参的值,则应加上关键字ByVal(按值传送)。而为了提高效率,字符串和数组应通过地址传送。此外,用户定义的类型(记录类型)和控件只能通过地址传送。

2)对于其它数据类型,包括双精度型、货币型和变体数据类型,可以用两种方式传送。但经验证明,此类参数最好用传值方式传送,这样可以避免错用参数。

3)如果没有把握,最好用传值方式传送所有变量(字符串、数组和记录类型变量除外),在编写完程序并能正确运行后,再把部分参数改为传地址,以加快运行速度。这样,即使在删除一些ByVal后,程序不能正确运行,也很容易查出错在什么地方。

(4)使用可选的参数

VB提供了十分灵活和安全的参数传送方式,允许使用可选参数和可变参数。在调用一个过程时,可以向过程传送可选的参数或者任意数量的参数。

在前面的例子中,一个过程的形式参数(形参)是固定的,调用时提供的实际参数(实参)也是固定的。也就是说,如果一个过程有三个形参,则调用时必须按相同的顺序和类型提供三个实参。

在VB中,可以指定一个或多个参数作为可选参数。例如,我们建立一个计算两个数的乘积的过程,它能够有选择的乘以第三个数。在调用时,既可以给它传送两个参数,也可以给它传送三个参数。

为了定义带可选参数的过程,必须在“参数列表”中使用Optional关键字,并在过程体中通过IsMissing函数测试调用时是否传送可选参数。例如:

Sub Multi(fis As Integer, sec As Integer, Optional third) '第三个参数是可选的
    Dim n
    n = fis * sec
    If Not IsMissing(third) Then  
'第三个参数是否存在测试
        n = n * third
    End If
    Print n 
'结果可以是两个数或三个数的乘积
End Sub

如果用下面的事件过程调用:

Private Sub Form_Click()
    Multi 10, 20
End Sub

结果是200;如果用下面的事件过程调用:

Private Sub Form_Click()
    Multi 10, 20, 30
End Sub

结果是6000。

注意:

1)上面的过程只有一个可选参数,也可以有多个,但可选参数必须放在参数列表的最后也就是说,如果指定了可选参数,则参数列表中此参数后面的其它参数也必是可选的。

2)IsMissing函数有一个参数,它就是由Optional指定的形参的名字,其返回值为Boolean类型,如果没有向可选参数传送实参,则IsMissing函数的返回值为True,否则返回值为False。

3)如果实参数目和形参数目相同,可以省去IsMissing函数。例如:

Dim strname As String
Dim steaddress As String

Sub listtext(Optional x As String, Optional y As String)
    List1.AddItem x
    List1.AddItem y
End Sub

Private Sub command1_click()
    strname = "yourname"   '提供了两个参数
    steaddress = 12345
    Call listtext(strname, steaddress)
End Sub

4)在未提供某个可选参数时,实际将该参数作为具有empty值的变体来赋值。可以用IsMissing函数来测试丢失的可选参数。如:

Dim strname As String
Dim steaddress As String

Sub listtext(Optional x As String, Optional y As String)
    List1.AddItem x
    If Not IsMissing(y) Then
    List1.AddItem y
    End If
End Sub

Private Sub command1_click()
    strname = "yourname"  
    Call listtext(strname)  '未提供第二个参数
End Sub

5)提供可选参数的值也可以为可选参数指定缺省值。下例中,如果未将可选参数传递到函数过程,则返回一个缺省值。

Sub listtext(x As String, Optional y As Integer =12345)
    List1.AddItem x
    List1.AddItem y
End Sub

Private Sub Command1_Click()
    Dim strname As String
    strname = "yourname"    
        Call listtext(strname)   '未提供第二个参数
End Sub

(5)使用不定量参数(可变参数)

一般说来,过程调用中的参数个数应等于过程说明的参数个数。但用ParamArray(译:预测的)关键字指明时,过程将接受任意个数的参数。

可变参数过程通过ParamArray命令来定义,一般格式为:

    Sub 过程名(ParamArray数组名())

这里的“数组名”是一个形式参数,只有名字和括号,没有上下界。由于省略了变量类型,“数组”的类型默认为Variant

例1:前面例子中建立的Multi过程可以求两个或三个数的乘积,下面定义的是一个可变参数过程,用这个过程可以求任意多个数的乘积。

Sub Multi(ParamArray Numbers())
    n = 1
    For Each x In Numbers
        n = n * x
    Next x
    Print n
End Sub

可以用任意个参数调用上述过程。例如:

Private Sub Command1_Click()
    Multi 2, 3, 4, 5, 6
End Sub

结果是720。

由于可变参数过程中的参数是Variant类型,因此,可以把任何类型的实参传送给该过程。例如,也不可用下面的代码调用上面个过程:

Private Sub Command1_Click()
    Dim a As Integer, b As Long, c AsVariant, d As Integer
    a = 6: b = 8: c = 12: d = 2
    Multi a, b, c, d
End Sub

结果是1152。

例2:编写计算总和的sum函数(见教材P227)。

Dim x As Variant
Dim y As Integer
Dim intsum As Integer

Sub sum(ParamArray intnums())
    For Each x In intnums
        y = y + x
    Next x
    intsum = y
End Sub

Private Sub command1_click()
    sum 1, 3, 5, 7, 8
    List1.AddItem intsum  '注意这里的变量intsum与“形参”数组名intnums()是两个完全不同的概念,你可以将变量intsum用其它名字代替,如兰色的intsum用z来代替。
End Sub

本例中,将子过程换为函数过程也一样。如将Sub换为Function。

四、程序的调试

程序员无论如何小心地规划自已的工程,如何仔细地绘制流程图,然后再编写代码也无法保证应用程序一次就能运行成功。事实上很少有大的应用程序是一次就能运行成功而得到正确结果的。

VB6.0为程序的调试提供了强大的程序调试工具。

1、一个示例

首先通过一个出现错误的应用程序示例,了解程序出错时的发生的情况:

创建一个新的工程,双击屏幕上的窗体,在窗体的Form_Load()事件中添加下列代码:

Private Sub Form_Load()
    Dim i, j As Integer
    j = 1
    For i = 1 To 10 Step 1
        j = j * i
        Print j:
    Next
End Sub

运行后弹出错误对话框,在这个对话框中,显示了程序的运行有错误,错误的代码为6,错误类型为数值运算结果溢出,单击Debug(调试)按钮,查看具体的错误代码(以高亮的形式显示了具体的错误)。

2、VB6.0中的错误类型

在VB6.0中的错误分为三种类型:语法错误;执行错误,逻辑错误。

下面就分别的介绍这三种错误类型的详细情况

(1)语法错误

语法错误是指由于指令或者程序没有遵守VB6.0的语法规则时发生的一类错误,通常是由于录入错误引起的。语法错误的示例:

Private Sub Form_Load()

   dim i ,j as double

   i=2.71828

   j=3.14159

   MsgBo"i*j="&i*j,vbDefaultButton3,"VB6.0语法错误"

End Sub

错误原因是msgbox中掉了一个x。

(2)执行错误

当VB6.0执行一条不能够执行的语句时,就会产生一个执行错误,然后自动地进入中断模式,加亮错误语句,等待用户的反应。下面就是一个产生执行错误的示例:

Private Sub Form_Load()
    Dim i As Integer
    Form1.AutoRedraw = True
    i = 100
    Do While i > 0
        i = i - 21
        Print Sqr(i)
    Loop
End Sub

由于程序段中产生一个为负数开平方根的非法错误,所以要使变量i的值始终为正数。修改后的程序段如下:

Private Sub Form_Load()
    Dim i As Integer
    Form1.AutoRedraw = True
    i = 100
    Do While i >= 21
        i = i - 21
        Print Sqr(i)
    Loop
End Sub

(3)逻辑错误

同大多数错误一样,逻辑错误通常也是由于编程中的逻辑错误引起的,不过逻辑错误仍然会输出一个结果,只不过这个输出的结果与预期的结果不符,由于程序会输出一个结果,所以这种错误比较难于查找。这种错误只能通过测试及分析输出结果来发现。下面就是一个逻辑错误的示例。

Private Sub Form_Load()
    Dim i As Double
    Dim j As Double
    Form1.AutoRedraw = True
    j = 1
    For i = 1 To 20
        j = j * i
        i = i + 1
        If Int(i) Mod 2 = 0 Then
            Print
        End If
        Print j;
    Next
End Sub

运行程序,结果如下:

显然,程序结果与设想的结果不符。

错误的原因是,在For ...next的语法中,如果省略了Step,那么程序就会默认为1,对循环计数器的修改是自动的。所以在程序段中就多出一条语句:

  i=i+1

所以得到的结果不是数1、2、3……的阶乘,而是1、3、5……的奇数相乘。删除这条语句重新运行程序。

 正确的结果为:

3、断点调试

在V6.0众多的调试工具中,断点调试工具是最优秀的一个调试工具。设置断点的方法:

方法一:通过选择菜单“调试”\“断点切换”,就可以在当前行插入一个断点,插入断点的行就会以红色来加亮,如果再次选择该菜单项,则清除该断点。

方法二:将鼠标放于一语句的左边,然后单击鼠标,则就在该语句行插入一断点。如果在设置断点的语句行的左边"断点"处再次单击鼠标,则该断点被清除。

在进入中断模式后,可以按键盘上的F8键来单步执行程序,而且可通过"?变量"的形式来随时的查看变量的值。

如,上面的例子中,先设置一个断点,然后运行程序:

运行后,在立即窗口中输入:

    ?j

则按Enter键后在立即窗口中就会输出变量j的当前值:

    1

五、综合示例     

设计一个简单应用,用来计算两个输入数值的和,其中用到1个FORM,3个Label,2个Text,一个Command。要求除了表达数值的字符外,其它类型字符无法录入文本框。

(1) 设计效果图如下

(2) 添加代码:

单击“工程”/“添加模块”,添加一个标准模块,输入如下代码,创建一个公用函数过程,这个函数的功能是过滤输入的数据,让非法类型的数据不能录入到文本框。

Function valitext(keyin As Integer, validatestring As String, editable As Boolean) As Integer
    Dim validatelist As String
    Dim keyout As Integer
    If editable = True Then
        validatelist = UCase(validatestring) & Chr(8)
    Else
        validatelist = UCase(validatestring)
    End If
    If InStr(1, validatelist, UCase(Chr(keyin)), 1) > 0 Then'第一个参数设置每次查找的起始位置,最后一个参数是指定字符串比较的类型,1表示执行文本比较。
        keyout = keyin
    Else
        keyout = 0
        Beep
    End If
    valitext = keyout
End Function

在窗体模块中,录入响应如下几个事件的代码:

Private Sub Command1_Click()
    Label3.Caption = Val(Text1.Text) + Val(Text2.Text)
End Sub

Private Sub Text1_KeyPress(KeyAscii As Integer)
    KeyAscii = valitext(KeyAscii, "0123456789.e", True)
End Sub

Private Sub Text2_KeyPress(KeyAscii As Integer)
    KeyAscii = valitext(KeyAscii, "0123456789.e", True)
End Sub

[程序演示]       [程序下载]