Access 开发框架(翻译+改编)系列之六——更多的类

在上个系列中,我们讨论到创建一个框架类和文本框类。当文本框获得焦点时,文本框类仅仅把它的背景色改变了一下,当它 … 继续阅读“Access 开发框架(翻译+改编)系列之六——更多的类”

在上个系列中,我们讨论到创建一个框架类和文本框类。当文本框获得焦点时,文本框类仅仅把它的背景色改变了一下,当它失去焦点时,再改回去。窗体类也只是扫描窗体的控件集合,为任何我们创建了类的控件实例化一个对象,目前,我们只是对文本框创建了类。

今天我将为我们的框架添加更多的类。首先我要添加一个组合框类。组合框类也只是根据其获得或失去焦点时,改变其自身的背景颜色。我知道这有点无聊,呵呵,但是它能清晰的向我们展示,窗体的控件扫描程序确实为我们实例化了这个类,控件类确实被加载到了内存,而且控件的一些指定的事件能被类接收到。相信我,为了理解类是如何工作的,WithEvents是如何工作的,类可以怎样使用,多个类如何一起协同,我们已经朝这个目标走了很长一段路了。

类dclsCbo

组合框类的开始部分看起来跟文本框类很像。实际上,我直接拷贝了文本框类的代码,然后用查找替换功能将文本框替换成组合框,这样我们得到了一个新的类:

'from www.Jasoftiger.com by Jasoftiger @ May 2,2017
Option Compare Database
Option Explicit

'定义一个带有事件的组合框
Private WithEvents mcbo As ComboBox

'一个常量被定义为[Event Procedure]
Private Const mcstrEventProcedure = "[Event Procedure]"

'漂亮的蓝色颜色代号,将会被设置成组合框的背景色
Private Const mclngBackColor As Long = 16777088

'保存组合框原来背景颜色的地方
Private mlngBackColorOrig As Long

d类的头部定义了私有的,带有事件的组合框控件对象变量,创建了一个字符常量,一个长整形常量,保存颜色代码,以及一个长整形变量来保存原始的背景色。

'每个类的 Init 函数初始化这个类,传入指向某个具体控件的指针
Public Sub Init(ByRef lcbo As ComboBox)

    '将传入的指针保存到类的私有变量中
    Set mcbo = lcbo
    
    '将控件的 OnEnter 属性设置为 [Event Procedure]
    mcbo.OnEnter = mcstrEventProcedure

    '为 OnExit 做相同的设置
    mcbo.OnExit = mcstrEventProcedure
    
End Sub

例程Init将参数传入的某个具体的组合框的指针保存在类的私有对象变量mcbo中。同时设置好组合框的OnEnter和OnExit属性。

'每个类的 Term 方法清除所有指向类中的对象的指针
Public Sub Term()

    '将指向控件的指针设置成Nothing
    Set mcbo = Nothing
    
End Sub

Term 方法用来释放或清除指向组合框控件的指针。

'这里有组合框的 OnEnter 和 OnExit 事件的事件处理代码
Private Sub mcbo_Enter()

    '当组合框获得焦点时,将原来的背景色保存下来
    mlngBackColorOrig = mcbo.BackColor
    
    '将组合框的背景颜色设置成我们预先定义的颜色
    mcbo.BackColor = mclngBackColor
    
End Sub

Private Sub mcbo_Exit(Cancel As Integer)

    '当组合框失去焦点时,还原其背景颜色
    mcbo.BackColor = mlngBackColorOrig
    
End Sub

最后组合框的Enter事件将控件的原始背景色保存下来,然后改变控件的背景色。OnExit事件将背景色还原。

所有代码基本上与文本框控件一模一样。

窗体类dclsFrm的修改

窗体类dclsFrm的修改只有一点,就是在控件扫描例程中,添加一个Case语句,让其能对组合框控件实例化一个对象。

Private Sub FindControls()
    Dim ctl As Access.Control
    
    '遍历窗体的所有控件
    For Each ctl In mfrm.Controls
        With ctl
            Select Case .ControlType
                Case acTextBox      '找到TextBox控件
                    '实例化一个dclsCtlTextBox控件,将其保存在集合对象中,以其名字作为键值
                    mcolClasses.Add New dclsCtlTextBox, .Name
                    '执行该dclsCtlTextBox控件的Init方法,将控件传递给对象
                    mcolClasses(.Name).Init ctl
                Case acComboBox
                    '实例化一个dclsCtlComboBox控件,将其保存在集合对象中,以其名字作为键值
                    mcolClasses.Add New dclsCtlComboBox, .Name
                    '执行该dclsCtlComboBox控件的Init方法,将控件传递给对象
                    mcolClasses(.Name).Init ctl
'                Case acCheckBox
'
'                Case acListBox
'
'                Case Else
                
            End Select
        End With
    Next ctl
End Sub

注意到在case acComboBox语句下,我们实例化了一个新的类dclsCtlComboBox,并将其添加到集合对象中,使用控件名作为关键字,以便后续引用。通过这种方式,我们可以节省中间变量的定义,获得简洁的代码。

现在,我们已经创建了一个新的类,用来处理任何我们想要的组合框的功能。我们也在dclsFrm类中添加了2行代码,使其能与新的类一起工作。我们可以继续为列表框、复选框、单选框等等控件创建类,但是通过文本框和组合框的演示,已经足够向我们展示框架系统的强大能力了。

想看到新组合框类dclsComboBox的功能,我们可以打开窗体frmPeople7,用Tab键遍历窗体的每个控件。组合框现在也能像文本框一样,根据焦点情况自动变换颜色了。

上述示例代码可以在该下载文件中看到6.1更多类dclsCtlComboBox示例

计时类dclsTimer

在这里我们要引入一个计时类,主要是想计算一下刚才的窗体打开花了多长时间,特别是想知道控件扫描花了多久。

计时类完整的展示了类的优点:复用性和封装性。实际代码我相信是从ADH(译者:估计是Access Developer Handbook)中提取出来的,但是它是一个单一的实例,也就是说,它只有几个函数和一个存储时间的变量,一次只能对一件事情计算时间。我们把它转换成了一个类,封装了它的代码,用文档记录下了它的工作原理 。它非常典型的向我们展示,一个类并不需要多复杂就能做非常有用的事情。

'from www.Jasoftiger.com by Jasoftiger @ May 3,2017
Option Compare Database
Option Explicit

'声明API函数,得到当前系统时间,以微秒为单位
Private Declare Function apiGetTime Lib "winmm.dll" Alias "timeGetTime" () As Long

'用于保存当前时间值,长整型
Private lngStartTime As Long

在计时类的头部,我声明了一个函数,用来调用Windows 的API函数,获取当前系统时间,以微秒为单位,返回结果以长整形来表示。另外我也定义了一个长整形变量,用来存储开始时间。

'首先运行StartTimer,获得当前时间值
Public Sub StartTimer()
    lngStartTime = apiGetTime()
End Sub

'函数返回间隔事件值,微秒为单位
Public Function EndTimer() As Long
    EndTimer = apiGetTime() - lngStartTime
End Function

类中只有2个方法,StartTime用来保存计时开始时间,EndTimer返回结束时间与开始时间的时间差。要记住返回的结果是以微秒为单位的。

最后,我在类dclsFrm中使用这个计时类来为我们做一点计时工作。

'from www.Jasoftiger.com by Jasoftiger @ May 3,2017
Option Compare Database
Option Explicit

'定义一个集合变量,用来存储控件类的实例
Private mcolClasses As Collection

'定义自己的窗体
Private mfrm As Form

'定义一个计时类对象变量
Private mclsTimer As clsTimer

我在类dclsFrm的头部定义了一个计时类对象变量mclsTimer,以便在类的内部其他任何地方可以引用它。

Private Sub Class_Initialize()

    '创建集合变量的实例
    Set mcolClasses = New Collection
    
    '创建计时类的实例
    Set mclsTimer = New clsTimer
    
    '开始计时
    mclsTimer.StartTimer
    
End Sub

在类的初始化事件Initialize中,我实例化了计时类,然后开始计时。

Public Sub Term()
    'Term会被执行2次,第二次不再执行
    If Not mfrm Is Nothing Then
        '在立即窗口打印窗体打开的时间
        Debug.Print mfrm.Name & "打开的时间是" & mclsTimer.EndTimer & "微秒"
        
        '销毁计时类对象变量
        Set mclsTimer = Nothing
        
        '销毁集对象中的所有对象后,再销毁集合对象本身
        ClsDestroy
        
        '释放指向实际窗体的指针,解除环形引用
        Set mfrm = Nothing
    End If
End Sub

在Term方法中,我在立即窗口打印出计时类对象的EndTimer函数值,这样就将窗体打开的时间显示出来了。

现在我们将使用另外一个计时类的实例,来为控件扫描计算耗时。我们针对FindControls例程做如下修改:

Private Sub FindControls()
    Dim ctl As Access.Control
    
    Dim lclsTimer As clsTimer
    Set lclsTimer = New clsTimer
    lclsTimer.StartTimer
    
    '遍历窗体的所有控件
    For Each ctl In mfrm.Controls
        With ctl
            Select Case .ControlType
                Case acTextBox      '找到TextBox控件
                    '实例化一个dclsCtlTextBox控件,将其保存在集合对象中,以其名字作为键值
                    mcolClasses.Add New dclsCtlTextBox, .Name
                    '执行该dclsCtlTextBox控件的Init方法,将控件传递给对象
                    mcolClasses(.Name).Init ctl
                Case acComboBox
                    '实例化一个dclsCtlComboBox控件,将其保存在集合对象中,以其名字作为键值
                    mcolClasses.Add New dclsCtlComboBox, .Name
                    '执行该dclsCtlComboBox控件的Init方法,将控件传递给对象
                    mcolClasses(.Name).Init ctl
'                Case acCheckBox
'
'                Case acListBox
'
'                Case Else
                
            End Select
        End With
    Next ctl
    
    Debug.Print mfrm.Name & "的控件扫描耗时" & lclsTimer.EndTimer & "微秒"
    Set lclsTimer = Nothing
End Sub

我们在控件扫描例程的头部定义一个计时类对象变量lclsTimer,然后开始计时。我们在例程退出前,结束计时,将耗时打印到立即窗口。然后销毁对象。

我在我的2.5GHz AMD开发电脑上运行这个窗体,控件扫描程序耗时1微秒完成扫描工作(译者注:我的电脑耗时0微秒)。在这么短的时间内,控件扫描程序加载了8个控件。我的立即窗口显示:

frmPeople7的控件扫描耗时1微秒

在我们创建越来越多的各种控件类的时,这个计时类对我们将会非常有用。当窗体加载它的所有的类的时候,我们想要监控其所耗时间,如果加载时间一下子跳到很久,我们能马上察觉,然后调查原因。

上述示例代码可以在该下载文件中看到6.1更多类clsTimer示例

总结

在该系列中,我们添加了一个新的控件类,来处理组合框的功能,然后我们又添加了一个计时类。这些类再一次向我们演示了创建一个类以及使用一个类是非常简单的。dclsFrm做了修改,允许控件扫描程序,为所有的组合框控件实例化一个dclsComboBox对象。我们也设置了2个计时类控件,一个在类的头部,对整个类内部可见,用来为窗体的打开时间计时;另一个在控件扫描例程中,用来计算加载所有的控件类对象呢所耗时间。

我们现在有了4个类,它们相互协同,创建了一个小小的“系统”,能让窗体为其上的控件实例化对象,控件能拥有Access本来没有赋予的功能。(译者注:如果仅仅只是为Access已有的类添加新的功能,可以在网络上搜索关键词“子类化(Sub-Classing)”了解更多的信息。)计时类可以告诉我们任何例程运行耗时的情况。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注