想了解一下VBA中自定义类和事件,以及注册事件处理程序的方法。

折腾了大半天,觉得这样的方式实在称不上“注册”,所以加一个“伪”字。纯粹是瞎试,原理也还没有摸透。先留着,有时间再接着摸。

做以下尝试:

1、建一个自定义类(类模块),类名:Qiqiu

该类提供一个Daqi的方法,每执行一次,x(记录气球的体积)的值+i,如果x的值大于max,则触发自定义的Change事件。

为节省细节不使用属性过程,变量直接用public

 Public Event Change(q As qiqiu)   'Event关键字声明事件,事件参数是Qiqiu类型。 
'推模式还是拉模式?一直感觉有参数的都该是推模式,傻傻分不清楚啊。
Public x As Integer '记录实际体积
Public max As Integer '记录最大体积
Function Daqi(i As Integer) '模拟给Qiqiu打气的情形
x = x + i
If x > max Then
RaiseEvent Change(Me) 'RaiseEvent关键字触发事件。在事件触发时,把自已的实例引用传给订阅者。
x =
End If
End Function

2、建三个简单窗体MainFrm,UserFrm1,UserFrm2

MainFrm启动后,点击“弹出窗体按钮”,UserFrm1和UserFrm2显示出来

3、UserFrm1、UserFrm2需要关联Qiqiu的Change事件(观察气球状态的变化对事件做出响应),需要做一些准备

下面是UserFrm1中的代码(为简化案例,UserFrm2的代码和UserFrm1完全相同,实际上可以完全是不同的响应)

注意第一行的WithEvents关键字的变量声明,后面需要使用这个变量将方法(事件处理程序)关联到事件。

 Public WithEvents qiu As Qiqiu          '关联Qiqiu的事件的关键,维护一个Qiqiu的引用,既然有引用,本案的Change事件的参数就显得很多余。

 Private Sub qiu_Change(q As Qiqiu)      '事件的响应程序
Me.TextBox1 = "气球爆炸了,爆炸时体积是:" + CStr(q.x)
End Sub

4、MainFrm主窗体代码:

 Public q As Qiqiu
Private Sub UserForm_Initialize()
Set q = New Qiqiu '窗体初始化,初始化Qiqiu类的实例
q.max = 10 '将q的最大体积设定为10
End Sub Private Sub btn_Click() '点击按钮“弹出窗体”执行的代码,实例化UserFrm1和UserFrm2并显示
Dim f1 As UserFrm1, f2 As UserFrm2
  Set f1 = New UserFrm1
  Set f2 = New UserFrm2
  Set f1.qiu = q '第3中的WithEvents关键字声名的变量在此处使用
  Set f2.qiu = q '使f1.qiu,f2.qiu分别指向Qiqiu类的实例q(即:注册)
    f1.Show False
    f2.Show False
End Sub Private Sub btndq_Click() '点击按钮“打气”执行的代码
q.daqi () '调用q的打气方法给Qiqiu打气,每次打入气体体积为5。当q.x大于q.max时触发事件
End Sub

5、程序执行效果:(虽然实现了效果,但理解上感觉模模糊糊)

打气三次时触发事件(此时气球的体积是15,超过了气球的max体积10),事件关联的处理程序提示,气球爆炸,并获取爆炸时的体积

6、小结:

VBA中,类的事件可能是很封闭的。不像C#事件开放了注册和移除的接口,只要方法签名相同,就可以很方便的指向事件的响应方法,根本不需要在订阅者类的内部再声明和发布者直接相关东西(变量引用),减小耦合度。

其实摸索了VBA的对象浏览器后,也可以找到类中事件的冰山一角,可以看到它的签名。比如Worksheet的Change事件。

7、补充一点转来的总结(我自己按想法修改了一些用词):

主题对象(被观察者、事件发布者)对客户端(观察者、订阅者)一无所知

  1. 观察者引用一个主题对象,对这个观察者,它可将引用放置在 WithEvents 变量中来处理那些主题对象。发布者没有订阅者的信息。它向未知数目的听众进行广播, 剧院中可能一个观众都没有。
  2. 主题对象不会控制接收事件的观察者的次序。(好像这点和C#有很大不同,C#事件注册的顺序可以决定事件的执行顺序)
  3. 当对象引发事件时,其所有订阅者都在引发事件的对象再次获得控制之前处理该事件。
  4. 如果事件包含 ByRef 参数,则该参数可被任何处理事件的客户程序改变。只有最后的客户端进行的改变才对引发事件的对象可见,因为(如上所述),直到所有客户端都处理该事件之前,引发事件的对象不会再度获得控制。

为了将某个事件添加到一个类中,然后使用该事件,可以这样做:

  1. 在定义类的类模块声明部分,用 Event 语句来声明事件—该事件带有希望它带有的任何参数。事件总是 Public。 注意 事件不能有命名的参数、Optional可选的参数、或 ParamArray可变参数。事件没有返回值。
  2. 在类模块代码中的合适地方,用 RaiseEvent 语句来引发事件,并提供所需要的参数。
  3. 在将要处理事件的模块声明部分,使用 WithEvents 关键字,添加该类类型的变量。它必须是一个模块级的变量。
  4. 在代码窗口左边的下拉菜单上,选择声明为 WithEvents 的变量。
  5. 在代码窗口右边的下拉菜单上,选择希望处理的事件。(可以为类声明多个事件。)
  6. 使用所提供的参数,将代码添加到事件过程中。

VBA中自定义类和事件的(伪)注册的更多相关文章

  1. C#中自定义类数组和结构数组的使用

    如有雷同,不胜荣幸,若转载,请注明 C#中自定义类数组和结构数组的使用 最近在很多项目中发现很多时候给定的数组要实现某个逻辑或处理很是麻烦,一维数组,二维数组,,,等等需要经过n多转换,还不如自己写一 ...

  2. 在VS2005中设置WPF中自定义按钮的事件

    原文:在VS2005中设置WPF中自定义按钮的事件 上篇讲了如何在Blend中绘制圆角矩形(http://blog.csdn.net/johnsuna/archive/2007/08/13/17407 ...

  3. ExtJS 中自定义类

    首先我们来看一看在Javascript中,是怎样自定义类的: var Person = function (name, age) { this.Name = ""; this.Ag ...

  4. (转).Net中自定义类作为Dictionary的key详解

    在定义数据结构时,Dictionary提供了快速查找数据的功能,另外Dictionary< TKey, TValue >属于key-value键值对数据结构,提供了泛型的灵活性,是数据结构 ...

  5. Comparable接口——容器中自定义类排序

    1.容器TreeMap,默认根据Key对象中某个属性的从小到大排列元素. (1)如下代码示例,Key是整型数字,所以按照其从小到大的顺序排列 public class TestTreeMap { pu ...

  6. MFC中 自定义类访问主对话框控件的方法

    之前一直在找有木有好点的方法.现在终于被我找到,收藏之~~~~~~ 在使用mfc的时候经常遇到自定义类访问主对话框控件的问题,例如自定义类中的方法要输出一段字符串到主对话框的EDIT控件.控制对话框的 ...

  7. Python中自定义类如果重写了__repr__方法为什么会影响到str的输出?

    这是因为Python3中,str的输出是调用类的实例方法__str__来输出,如果__str__方法没有重写,则自动继承object类的__str__方法,而object类的__str__方法是调用_ ...

  8. java 中自定义类的概述

    作业: 描述商品类 Goods 4个属性 商品名字 大小 价格 库存 把商品类放进集合中 小米品牌 大小 价格 库存的数量 都存集合 华为..... 魅族 public class Goods{ St ...

  9. Python中自定义类未定义__lt__方法使用sort/sorted排序会怎么处理?

    在<第8.23节 Python中使用sort/sorted排序与"富比较"方法的关系分析>中介绍了排序方法sort和函数sorted在没有提供key参数的情况下默认调用 ...

随机推荐

  1. Writable、WritableComparable和comparators

    hadoop的序列化格式 hadoop自身的序列化存储格式就是实现了Writable接口的类,他只实现了前面两点,压缩和快速.但是不容易扩展,也不跨语言. 我们先来看下Writable接口,Writa ...

  2. Floyd算法核心代码证明

    Flody  大家都知道这个最终模版: for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[i ...

  3. 个人的java web开发书单

    首发至个人博客http://www.zidafone.com/blog/36 以下是对一些读过的书和一些买后随便翻了翻的书的个人感觉.都是java web开发的程序员可能接触的书,其他的如设计/手机开 ...

  4. TI CC2541的整体目标

    1. App端会发送一定数量的byte过来蓝牙, 每2个byte是一个汉字的编码. 2. 拿到汉字编码之后, 统计字符数量, 然后通过SPI, 搜索编码 3. 收到的编码, 每个汉字字符有32个byt ...

  5. Google 开发新的开源系统 Fuchsia

    google 最新os 下载 https://github.com/fuchsia-mirror/magenta 本文转自:http://www.oschina.net/news/76094/goog ...

  6. C#:控制台程序调用中间库创建窗体

    1.类库项目引用System.Windows.Forms并添加引用后,才可创建窗体. 2.控制台应用程序调用中间库(DLL)中的方法创建窗体:中间类库使用反射下的Assembly加载包含窗体的类库及创 ...

  7. jquery ui 常用(一)(自动完成 | 标签页 | 折叠面板 | 带图标的按钮 | 日期选择器| )

    条件,引用3个文件 jquery-ui.min.css; jquery.min.js; jquery-ui.min.js. 一.自动完成 http://www.w3cschool.cc/jqueryu ...

  8. JAVA 值传递

    Java里方法的参数传递方式只有一种:值传递 值传递:当系统开始执行方法时,系统为形参执行初始化,就是把实参变量的值赋给方法的形参变量,方法的操作的并不是实际的实参变量 引用型变量:系统复制的是变量, ...

  9. MyBatis 判断条件为等于的问题

    在用MyBatis操作数据库的时候相信很多人都用到,当在判断null, 大于,大于等于,小于,小于等于,不等于时估计很多都用到,比较容易实现了,这里就省略了,但唯独判断条件为等于时估计蛮多人遇到坑了, ...

  10. 通过spring工厂读取property配置文件

    /** * Created by ywq on 2016/6/30. */ @Named public class PropertyConfig { private static AbstractBe ...