ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
楼主: qee用

VBA类:隐者的秘密

  [复制链接]

TA的精华主题

TA的得分主题

 楼主| 发表于 2006-11-9 17:17 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
本帖已被收录到知识树中,索引项:类和类模块

五、创建类方法

放松一下,请拿出你家的紫砂壶,泡上一壶好茶,听我给你将类的方法的故事,你的茶品完了,我的故事也差不多就讲完了。

1.构建类的方法其实就是在类模块中写公共的SubFunction

现在我们给前面提到的MyClass创建一个方法PutIntoActiveCell,功能是将x属性值写入活动单元格。

Public x$

Sub PutIntoActiveCell()

  ActiveCell = x

End Sub

在标准模块中用下面的代码测试一下:

Sub aTest()

  Dim mc As New MyClass

  mc.x = "abc"

  mc.PutIntoActiveCell

End Sub

这是本回要告诉你的全部吗?你还没有开始品茶吧?就这样了结束?这是最重要和基本的,但却不是全部。

你是否有一种感觉,但你不能清楚地说出来? 端起你可爱的茶杯,品一口茶,我们继续。

[此贴子已经被作者于2006-11-9 21:58:37编辑过]

TA的精华主题

TA的得分主题

 楼主| 发表于 2006-11-9 17:17 | 显示全部楼层

2.类的方法环境

借用广为众知的一个名词“数据环境”,虽然不准确,但我实在想不出更好的称谓来代替,姑且这么叫吧。稍后你就会知道它的含义。

类可以象VBA提供给我们的很多标准类一样风光无限,所有的程序设计者都在工程中使用它,但更多时候,我们所构建的类只在特定的环境下被使用,类的方法环境是指包括类所在工程的其它成员在内的,可以调用的资源的集合。工作簿、工作表、窗体或其它,在类模块中,你可以象在标准模块中一样操作它们,千万不要因为换成了类模块而产生任何疑虑,作为类的创建者,你要让类模块中的代码象你在标准模块中一样亲近它们,只要你认为必要。脱离了方法环境的、谨小慎微的、封闭的类实在没有什么意义。如果你预期方法环境在运行时可能会有变化,你要事先预知它们并象在标准模块中一样使用恰当的措施,比如你不能确定运行时活动工作表的名称(但你确定届时会是一个工作表),你可以使用ActiveSheet

我反复说“和标准模块一样”,就是想告诉你在类模块中创建方法时,对工程中其它成员的操作,和你已经熟悉的标准模块中的方式的实在没有什么不同,这一原则适用于类模块中所有代码(也许叫代码环境更准确些),而不仅仅是构建方法的代码。

现在,你知道了,你刚才的感觉到的是开放的方法环境。是的,以后你会更深地体会到,作为好的提供者,开放的思维有多重要。

[此贴子已经被作者于2006-11-9 22:04:54编辑过]

TA的精华主题

TA的得分主题

 楼主| 发表于 2006-11-9 17:19 | 显示全部楼层

3.方法的兄弟成员事件

类方法的执行需要在代码中以显性的方式指定,象上面的mc.PutIntoActiveCell,有时候,当最终操作者触发类对象成员(属性)的某个事件,需要在事件发生时产生一系列的操作,这时,我们要运用成员事件。成员事件和方法都是类提供的一系列代码的操作,倆兄弟的区别在于,成员事件无法也不必再由代码显性调用。

我们来看一个具有普遍意义的事例。

[重要例]

窗体UserForm1上有5CommandButton控件(名称分别为默认CommandButton 1- CommandButton 5)和1TextBox控件(名称为TextBox1)。要求当各个CommandButton控件被点击时,它的按钮文字(Caption)会写入TextBox1

如果不用类,我们需要为5CommandButton控件分别写5个相同的Click事件代码。如:

Private Sub CommandButton 1_Click()

  TextBox1 = CommandButton 1.Caption

End Sub

下面是用类的成员事件方法的代码:

类模块Cmds的代码

Option Explicit

Public WithEvents cmd As CommandButton

Private Sub cmd_Click()

  UserForm1.TextBox1 = cmd.Caption

End Sub

窗体UserForm1的代码

Option Explicit

Dim co As New Collection

Private Sub UserForm_Initialize()

  Dim i%

  Dim myc As Cmds

  For i = 1 To 5

    Set myc = New Cmds

    Set myc.cmd = Me.Controls("CommandButton" & i)

    co.Add myc

  Next i

  Set myc = Nothing

End Sub

仔细玩味上例的每一行代码,直至品完你壶中的茶。呵呵,因为它实在很有用。最后提一下Friend关键字,虽然在VBA中几乎没有什么用,但如果有一天你要制作ActiveX部件,可能会用到它。之所以要有Friend关键字,是因为类的私有部分在类模块外是不可见的,但有时却需要从外面访问这些私有部分,这时,可以使用Friend关键字使属性和方法成为“友元成员”。友元成员在本工程中相当于Public,但在工程外,它仍是Private 

隐者为你揭去了第二层面纱,你几乎已看清她美丽的面庞,她带着甜蜜的微笑,似乎在问:什么才是最美的期待? 

[此贴子已经被作者于2006-11-11 22:27:23编辑过]
BZFtRcWk.jpg

TA的精华主题

TA的得分主题

 楼主| 发表于 2006-11-11 22:30 | 显示全部楼层

六、创建类事件

VBA中,因为我们既是提供者,也是使用者,所以通过良好地构建类的属性和方法,已可以满足我们需要全部的要求。我不再去解释这个观点,在本回后你自然会明白。从这个意义上讲,创建类事件实在没有必要。唯一的遗憾是,我们没有体会到作为创建者的全部乐趣,标准类给我们提供了各种事件,当然希望自己也可以做到,想象中这应当是一件激动人心的事,所以,追求快乐是创建类事件的重要理由,另一个理由,前面已经提到。

回到前面我们的MyClass类,我们将x属性改名为Value属性,虽然对属性、方法以及事件的命名,VBA没有特别的限制,但建议您不要象我前面那样,随便取一个x,可能的话,要尽量和标准类的成员(属性、方法以及事件)名称相一致。

现在我们为“使用”者提供一个“Change”事件,不错,我们给它取名为“Change”,而不再是随意的“y”或其它(虽然也可以),这样,我也不用解释这个事件的用意了,呵呵。为了做到这一点,看看我们应该做什么。

1.第一步:使用Event语句声明事件

看一下类模块中现在的代码:

Option Explicit

Public Event Change(ByRef Cancel As Boolean)

Private s$

Public Property Get Value() As String

  Value = s

End Property

Public Property Let Value(ByVal c As String)

  s = c

End Property

Private Sub Class_Initialize()

  s = "abc"  初始值

End Sub

和前面的代码比较,多出了一句:

Public Event Change(ByRef Cancel As Boolean)

这就是Event语句,只此一句,我们已经为我们的类声明(我想使用“注册”一词是不是更妥切)了一个事件Change。在看Event语句产生的效果前,先来看它的特性:

1)为了声明事件,Event总是Public的,这好理解吧。

2)事件可以不带参数,如Public Event Change(),也可以带参数,如我们上面给出的,但参数不能是命名参数,可选参数或数组参数。这里我只解释一下命名参数的含义。我们知道,事件可以因特定的用户事件而触发,也可以在代码中象方法一样指定执行,如下面的CommandButton1_Click

Private Sub CommandButton2_Click()

  CommandButton1_Click

End Sub

但在调用对象的方法时我们通常喜欢这样的方式:

Selection.Sort Key1:=Range("A2"), Order1:=xlAscending

这里Key1Order1就是命名参数,命名参数的好处是我们不必记住它们的次序,调用时直接以名称和冒号后加等于号指定它的值,但对事件的调用却不允许这样。

3)事件没有返回值。

TA的精华主题

TA的得分主题

 楼主| 发表于 2006-11-11 22:34 | 显示全部楼层

现在我们看一下,Event为我们做了什么。

建立一窗体UserForm1,添加一个TextBox控件(名称为TextBox1),两个CommandButton控件(名称为CommandButton1CommandButton2),CommandButton1Caption设置为“赋值”,CommandButton2Caption设置为“读值”,窗体的代码如下:

Option Explicit

Dim WithEvents mc As MyClass

Private Sub CommandButton1_Click()

  mc.Value = TextBox1 '赋值

End Sub

Private Sub CommandButton2_Click()

  MsgBox "mc当前的值为" & mc.Value '读值

End Sub

Private Sub UserForm_Initialize()

  Set mc = New MyClass

End Sub

上面这段代码实现的是,当点击CommandButton1时便会将TextBox1的值赋给mcValue,当点击CommandButton2时便会显示mc当前的Value值。

来运行一下这个窗体,先点击CommandButton2,此时显示“abc”,是mc的初始值,然后在TextBox1输入“123”,点击CommandButton1,再点击CommandButton2,显示“123”,说明赋值成功了。

呵呵,忘了,我们要做什么了!现在,请从UserForm1代码窗口的“通用”框中选择mc,哇!我们声明的事件在右边“声明”框中已经出现了!

我们定义这个事件是希望当mc的值改变时响应的,现在就迫不及待地给它写一句代码吧:

Private Sub mc_Change(ByRef Cancel As Boolean)

  If MsgBox("要改变mc的值吗?", vbYesNo) = vbNo Then Cancel = True

End Sub

上面这句代码你不会陌生吧,希望当用户选择了在改变时给用户一个确认的机会。

但是,现在点击CommandButton1,却不会给你选择的机会,我们还有一步没有做。

TA的精华主题

TA的得分主题

 楼主| 发表于 2006-11-11 22:37 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册

2.第二步:使用RaiseEvent语句引发事件

声明了事件后,我们要做的,便是找到所有与事件发生关联的地方,使用RaiseEvents语句引发事件,这里引发的含义相当于Call,就是调用用户在事件中写的代码。在本例中,只有一个地方,就是Property Let Value过程中:

  Dim chyn As Boolean

  RaiseEvent Change(chyn)

  If chyn Then Exit Property

通过传递回的chyn,决定是否执行后面的赋值语句。下面就是添加了RaiseEvents语句后的类模块的代码:

Option Explicit

Public Event Change(ByRef Cancel As Boolean)

Private s$

Public Property Get Value() As String

  Value = s

End Property

Public Property Let Value(ByVal c As String)

  Dim chyn As Boolean

  RaiseEvent Change(chyn)

  If chyn Then Exit Property

  s = c

End Property

Private Sub Class_Initialize()

  s = "abc"

End Sub

现在你可以去运行你的窗体了,我们要的效果应该是达到了吧。为了便于你调试,下面给出窗体的全部代码:

Option Explicit

Dim WithEvents mc As MyClass

Private Sub CommandButton1_Click()

  mc.Value = TextBox1 '赋值

End Sub

Private Sub CommandButton2_Click()

  MsgBox "mc当前的值为" & mc.Value '读值

End Sub

Private Sub UserForm_Initialize()

  Set mc = New MyClass

End Sub

Private Sub mc_Change(ByRef Cancel As Boolean)

  If MsgBox("要改变mc的值吗?", vbYesNo) = vbNo Then Cancel = True

End Sub

当然,我们可以把上面mc_Change的代码要做的直接在Property Let Value过程的代码中,从而不使用事件。这就是在本回的开头说的。

事件的构建已经完成,说了这么多,其实你只要记住两步的标题就可以了。到这里,关于VBA类最基本最重要的部分已经给朋友们介绍完了。余下的,留着您在未来的探索路上慢慢体会吧,也请您不要忘了和大家分享您的喜悦。

隐者已向你展示了她所有的秘密,铅华去尽,只有美丽!

[此贴子已经被作者于2006-11-15 14:26:51编辑过]

TA的精华主题

TA的得分主题

 楼主| 发表于 2006-11-11 23:00 | 显示全部楼层

七、一个完整的类实例

现在,提供一道简单的测试,帮大家回顾一下前面的知识。建议您做一下,因为VBA是实践性的。

题目要求:

(一)构建两个类:

1Student

具有2个属性:

1Name:可读写。

2Id:可读写,但只能写一次。格式为字母S加两位整数,如S01S02…等。

2Students

具有1个属性,3个方法,2个事件:

1Count属性:只读,返回Student成员数量。

2Item方法:使用下标如Stus.Item(i)的方式调用,返回相应的Student成员,i可以是Student成员的自然顺序,也可以是Student成员的Id

3Add方法:增加Student成员。当增加成员时,按顺序递增生成成员的Id,每个Id号只用一次,不因删除成员受影响。

4Remove方法:删除Student成员。

5BeforeAdd事件:在增加成员前作出响应,允许用户取消增加成员。

6AfterRemove事件:在删除成员后响应。

[此贴子已经被作者于2006-11-15 15:18:30编辑过]

gBtMzVVe.rar

21.03 KB, 下载次数: 4592

TA的精华主题

TA的得分主题

 楼主| 发表于 2006-11-15 15:23 | 显示全部楼层

(二)构建一个用户窗体测试前面的类:

1.窗体上包含四个CommandButton,分别完成如下功能:

1)增加成员

使用InputBox输入Student成员的Name,完成增加。

2)删除成员

使用InputBox输入Student成员的Id或自然序号,完成删除。

3)显示学员总数。

使用MsgBox显示Student成员总数。

4)查询学员

使用InputBox输入Student成员的Id或自然序号,然后使用MsgBox显示相应Student成员的Name

2.一个ListView,即时显示现有的所有Student成员。

3.事件处理

1BeforeAdd事件,查找现有成员的Name是否有和要增加的成员的Name相同的,如有,则给出提示,让用户选择是否增加。

2AfterRemove事件,刷新ListView显示

(三)未尽之处自由发挥,假定用户操作规范,可不考虑错误处理。

(呵呵,不知怎么回事,有时很少的内容,一层楼竟然发不下)

参考答案:

TA的精华主题

TA的得分主题

 楼主| 发表于 2006-11-15 16:34 | 显示全部楼层

八、未完的结尾

当初构思这篇文章时想说的话已经想不起来了,现在最想说的是感谢,感谢所有关注这个贴子的朋友,感谢朋友们在我写作过程中给予的鼓励,感谢taller兄的特别鼓励。成文仓促水平所限,错漏之处请朋友们予以补充指正吧。

VBA类虽然留给了我们一些遗憾,比如不能构建隐藏成员、默认成员,也不能创建列举属性,但并不影响它作为VBA的一项非常有用技术。本文期望的,是把您带入这个大门,不知做到了没有,对它的进一步探讨,以后有机会再和朋友们一起学习吧。

[em12]

评分

5

查看全部评分

TA的精华主题

TA的得分主题

发表于 2006-11-15 17:18 | 显示全部楼层
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-22 16:26 , Processed in 0.052666 second(s), 8 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表