使用MVC设计模式在Access中实现数据的“查改增删”

在知乎上写了一篇文章,有兴趣的朋友可以通过这个链接访问。

使用MVC设计模式在Access中实现数据的“查改增删” – Jasoftiger的文章 – 知乎

因为知乎上不能上传示例文件,可以从这里下载:MVC Design Pattern in Access VBA

使用MVC设计模式在Access中实现数据的“查改增删”后记 – Jasoftiger的文章 – 知乎

MVC Design Pattern in Access VBA v2 彻底解耦MV

VBA 类模块系列之十五——结语

VBA类模块系列文章终于写完了。第一次填完一个坑,先给自己一点掌声^_^。

本系列,从类的概念和思想谈起,逐一介绍了类对象的属性,方法和事件。当然也谈到了类中的常量和错误处理以及类对象的生命周期。作为一个入门级的系列,该谈的内容都差不多谈完了。

在介绍类的自定义事件时,为了示例的效果,引入了稍微复杂一点的“心脏起搏器”的做法,这里本质上,其实是属于类对象之间的关系范畴(一个类对象包含了另一个类对象)。有了这样一个有自主意识的类对象,我们可以有更多的想象空间,比如,做成一个谈话机器人。当然这个方向就有点跑偏了,大多数人使用Access VBA并不是要搞这个。

关于VB/VBA类模块,其实还有很多内容没有谈到,比如:集合类,类对象之间的关系,子类化问题,多态(多接口),接口继承等等。这些等将来有兴致,再开一个高级类模块的系列谈谈。

最后,向广大的群友们致谢。没有你们的支持和鼓励,意见和建议,按我的一贯作风,这个系列肯定是半途夭折^_^。特别鸣谢:tmtony(王站),河北-SQL Designer,滋阴壮阳冬瓜汤。

 

VBA 类模块系列之十四——在类中定义和触发自己的事件

现在是时候解锁两个新技能了——在类中定义和触发自己的事件(Event)。

作为一个有一定经验的Access VBA开发人员,你没少写过事件的响应程序。比如:Form_Load(),Command0_Click(),Text0_Enter()等等。总是使用别人定义好的事件,有没有觉得乏味和沮丧?

现在是时候解锁两个新技能了——在类中定义触发自己的事件(Event)。这两个技能,再加上上一篇介绍的WithEvents,这三项技能一旦合体,就会变形成一支笔,神笔马良的笔,凡是用这只笔创建的类对象,就立刻有了自主意识。

前面几篇一直要说给Person类创建一个生日提醒事件,终于吹过的牛逼要兑现了^_^,所以我们在Person类的头部写如下代码:

'Person 类代码
Option Compare Database
Option Explicit
 
'
'以上代码省略

Public Event Birthday()
'
'以下代码省略

第8行就是我们定义的生日(Birthday)事件。是不是出奇的简单?^_^Public表示生日事件是公有的,事件是用来往外转播的,私有事件没有任何意义。所以,这里必须是Public,你可以试试将它改成Private,编译就会报错。Event是VBA的又一个关键字,专门用来定义事件。Birthday就是我们的事件名,后面有一对括号,我们可以在括号里面添加参数,当往外传播事件时,连同参数一起传递出去。我们先弄简单一点,不写任何参数。

生日事件定义好了,但是,在Person类中,我们该怎么来触发这个事件呢?

还记得上一篇给Person类安装的心脏起搏器吗?它每隔0.7秒就能获得一次脉冲,让她有机会思考一下今天是不是她的生日。如果是,就触发生日事件。我们修改一下Person类的Timer事件响应子程序如下:

Private Sub mfrm_Timer()
'    Debug.Print "我是" & Me.Name & ",现在时间是:" & Now() & ",我有脉搏和心跳了!"
    If Format(mdatDOB, "MMDD") = Format(Date, "MMDD") Then
        RaiseEvent Birthday
    End If
End Sub

第3行代码就是她的思考过程,比较一下当前日期(Date)的月和日与她的出生日期的月和日的值是否一致,如果一致,就执行第4行代码。

第4行代码就是触发生日事件的代码!RaiseEvent是与事件相关的第3个关键字(前2个分别是监听事件的WithEvents和定义事件的Event)RaiseEvent字面意思是“发起事件”。Birthday就是刚才我们定义的生日事件名称。

现在,我们在Person类中定义好了事件Birthday,也在Person类中,适当的地方,适当的时候,发起了这个事件。Person类(事件源 Event Source)中该做的事件都做完了,剩下的就是测试一下,看看事件监听者(Event Sink)能否监听到这个事件。第十二篇讲过,事件监听者必须是类对象,所以这个测试不能在模块1(标准模块)中来进行了。我们使用窗体4(窗体模块,也是一种类模块)来做测试。

上一篇我们在窗体4代码窗口的头部定义了一个objPerson对象变量,现在,我们还要监听objPerson对象指向的Person类对象的Birthday事件,所以,我们需要给objPerson添加WithEvents修饰符。如下代码所示:

'窗体4
Option Compare Database
Option Explicit

Private WithEvents objPerson As Person
'
'以下代码省略

有了WithEvents,就可以在窗体4的代码窗口的左上角的下拉框中,找到对象变量objPerson,然后为它的Birthday事件编写事件响应程序。如下代码所示:

'窗体4
Option Compare Database
Option Explicit

Private WithEvents objPerson As Person

Private Sub Form_Load()
    Set objPerson = New Person
    objPerson.Name = "赵冰冰"
    objPerson.Gender = Female
    objPerson.DOB = #2/3/1995#
    
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Set objPerson = Nothing
End Sub

Private Sub objPerson_Birthday()
    MsgBox "今天是" & objPerson.Name & "的生日"
End Sub

事件监听者的代码也写好了,我们打开窗体4。等着一个消息窗口谈出来,告诉我们“今天是赵冰冰的生日”。

可是等了好久都没出现什么消息窗口。什么原因?是不是事件监听失败了?呃,不是的,因为今天不是她的生日,你要等到明年2月3日,才会弹出这个消息窗口。呃,要等半年多时间,有点太久了,大家都这么忙的,我们把“生日”,改成“生秒”。比如说,你是1995年2月3日8时30分25秒出生的,那么任何时点的第25秒,比如8点31分25秒,你的“生秒”就到了。所以每分钟你都能过一个“生秒”。我们修改Person类中“心脏起搏器的脉冲”响应代码:

Private Sub mfrm_Timer()
'    Debug.Print "我是" & Me.Name & ",现在时间是:" & Now() & ",我有脉搏和心跳了!"
'    If Format(mdatDOB, "MMDD") = Format(Date, "MMDD") Then
    If Format(mdatDOB, "SS") = Format(Now, "SS") Then
        RaiseEvent Birthday
    End If
End Sub

也调整一下窗体4的代码如下:

'窗体4
Option Compare Database
Option Explicit

Private WithEvents objPerson As Person

Private Sub Form_Load()
    Set objPerson = New Person
    objPerson.Name = "赵冰冰"
    objPerson.Gender = Female
'    objPerson.DOB = #2/3/1995#
    objPerson.DOB = #2/3/1995 8:30:25 AM#
    
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Set objPerson = Nothing
End Sub

Private Sub objPerson_Birthday()
'    MsgBox "今天是" & objPerson.Name & "的生日"
    MsgBox "现在时间是" & Time & ",是" & objPerson.Name & "的生秒"
End Sub

然后打开窗口4,最多等待1分钟,就能看到弹出窗口:

我们完美的完成了生日事件的监听测试。

这个生日事件,有点不足之处,就是不知道她是多少岁的生日,叫我怎么为她选择数字蜡烛呢?

现在,让我们给事件加上参数。重新定义Birthday事件。

'Person 类代码
'
'以上代码省略

'Public Event Birthday()
Public Event Birthday(lintAge As Integer)
'
'以下代码省略

第6行代码,为生日事件添加了一个年龄参数lintAge。那么当你触发这个事件,向外部传播之前,需要给给这个参数赋值。

我们修改一下触发事件的代码:

Private Sub mfrm_Timer()
'    Debug.Print "我是" & Me.Name & ",现在时间是:" & Now() & ",我有脉搏和心跳了!"
'    If Format(mdatDOB, "MMDD") = Format(Date, "MMDD") Then
    If Format(mdatDOB, "SS") = Format(Now, "SS") Then
'        RaiseEvent Birthday
        RaiseEvent Birthday(Age)
    End If
End Sub

这里直接使用现成的类属性Age作为实参向外传递。

然后我们同步修改一下窗体4中的事件监听响应代码如下:

'
'以上代码省略
Private Sub objPerson_Birthday(lintAge As Integer)
    MsgBox "现在时间是" & Time & ",是" & objPerson.Name & "的" & lintAge & "岁生秒"
End Sub

再次打开窗体4,等待1分钟,弹出如下消息窗:

这就是带参数的事件的定义,触发,和响应。

总结一下。事件看起来神秘莫测,实际上,它的定义和触发都是非常简单的。在类中定义什么样的事件?在类的什么地方触发它?在什么时候触发它?这3个问题,是设计事件时,需要通盘考虑的主要问题。事件定义好以后,就可以在各个地方监听和响应事件。

最后,将我的Access文件贴在这里,供大家下载。VBA 类模块系列之十四——在类中定义和触发自己的事件