Excel精英培训网

 找回密码
 注册
数据透视表40+个常用小技巧,让你一次学会!
楼主: qee用

[分享] VBA类:隐者的秘密

  [复制链接]
 楼主| 发表于 2006-11-16 09:40 | 显示全部楼层

5.集合Collection

Collection是我们在使用类时最常用到的对象。一个Collection对象代表一组相关的项目,虽然它的成员并不被强制要求是同一类型的的,但请记住,这通常并不能给我们带来额外的方便,相反,我们通常是用来收集同一类型的数据。

建立集合的方法和建立其它对象一样,如:

Dim col As New Collection

集合建立后,可以使用Add方法添加成员,用Remove方法删除成员,用Item方法从集合中返回特定成员。

Private Sub CommandButton1_Click()

  Dim col As New Collection

  Dim i%

  Dim ct As Control

  For Each ct In Me.Controls

    If Left(ct.Name, 7) = "TextBox" Then col.Add ct

  Next ct

  For i = col.Count To 1 Step -1

    MsgBox "下面删除成员" & col.Item(i).Name

    col.Remove i

  Next i

End Sub

上面的代码先将窗体上所有的TextBox加入到集合中,然后再删除掉。Count属性返回集合的成员数量,Remove方法后面的参数是集合成员的索引号。成员的索引号通常是按照加入的顺序自然编号,从1开始,但可以在加入时使用Add方法的参数进行改变。Add方法的完整语法是:

object.Add item[, key][, before][, after]

item 必需的。任意类型的表达式,指定要添加到集合中的成员。

key 可选的。唯一字符串表达式,指定可以使用的键字符串,代替位置索引来访问集合中的成员。

before/after 可选的。表达式,指定集合中的相对位置。

下面语句向集合增加一个对象TextBox1,并定义该成员的关键字为tx1

col.Add TextBox1, "tx1"

然后,下面两句都可以向集合中增加一个TextBox2,并把它放在成员TextBox1的前面。

col.Add TextBox2, , col.Count

col.Add TextBox2, , "tx1"

第一句中,因为只有一个成员,所以col.Count也是索引号

 

[此贴子已经被作者于2007-3-27 23:20:35编辑过]
excel精英培训的微信平台,每天都会发送excel学习教程和资料。扫一扫明天就可以收到新教程
回复

使用道具 举报

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

6.使用事件的WithEvents变量

WithEvents不是一个单独的语句,为了使用对象的事件,需要在声明该对象时使用WithEvents关键字。例如:

Dim WithEvents app As Application

将上面的语句写入ThisWorkBook的模块,可以看到在通用框中出现了一个变量app

在通用框选择app后,左边的声明框便会显示app的事件。

需要注意的是,使用WithEvents只是声明了对象变量,而并不实际生成对象,为了生成真实的对象,你仍然需要在声明后向生成其它对象一样,使用Set语句进行指定。此外,WithEvents变量不能是通用类变量如Object,而必须指定类名,也不能把WithEvents变量声明为As New。不能在标准模块中使用WithEvents

回复

使用道具 举报

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

7.初识类

现在,请打开你的VBE,主菜单-插入-类模块。

插入了一个类模块,也就建立了一个类。类模块的名字就是类的名字。你现在看到的,她的名字叫“类1”,这是VBA按她姐妹排行给她取的的,是的,VBA一贯如此,你早就熟悉了这种规则,现在,在标准模块或其它模块中输入Dim …As的时候,提示框中她已经出现了。但我知道,有件事你正耿耿于怀,“类1”,太没个性了,想改成自己要的名字吧。很容易,和你改标准模块的名字一样,打开属性窗口,看到了吧,第一行就是她的名字,随你的意愿修改吧。

你或许已经注意到,在名字下面,只有一个属性:Instancing,其值也只有两个选项:PrivatePublicNotCreatable。事实上,你完全可以忽略这个Instancing,就象你完全忽略条件编译指令一样,因为在VBA中我们几乎用不到它们,而只需维持她的默认值即可。至少我是这样认为的,但我给不了您充足的理由,而只是个人的一种狭隘经历。既然提到了,就简单说明一下:

Instancing属性决定该“类”在其它工程中是否可以被使用。我们知道,标准模块中的Public过程,可以保存在宏工作簿甚至直接被另一工作簿的工程调用,但类中的代码是不可分割的整体,所以必须整体决定是否允许外用。当Instancing属性设为Private(默认)时,不允许其它工程访问。当设置为PublicNotCreatable时,只有在自己的工程创建了该类的对象时,其它工程才允许使用这个对象,注意,仅仅是在本工程中创建的对象,而不能用她在其它工程中创建对象。

隐者已经来到你的身边,透过薄薄的面纱,你似乎已看到她神秘的微笑。站起身来,走过去吧!

回复

使用道具 举报

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

四、创建类属性

让我们想一下作为类的使用者时,我们是如何操作对象的属性的,对象属性的操作不外乎读和写两种。当我们要给对象的某个属性赋值时,我们会:

TextBox1.Text=”abc”

当我们要读取对象的属性时,

S= TextBox1.Text

现在,看看作为类的提供者需要怎样做。

我们将“类1”改名为“MyClass”并为它创建一个名称为x的字符型属性。

1.使用Public变量创建类属性

在类模块中写下行代码:

Public x$

是的,就这么简单,通常情况下,只需要这么简单。

回复

使用道具 举报

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

2.使用Property过程创建类属性

Private s$

Public Property Get x() As String

  x = s

End Property

Public Property Let x(ByVal c As String)

  s = c

End Property

我们可以省去上面默认的Public。但看上去还是有点麻烦哦,不仅需要两个公共过程,而且还要一个辅助的私有变量s和一个参数c。在类模块中,Property过程把对属性的读写分开了,说一下Property过程的工作机制,当标准模块中的代码读取对象的属性时,便会触发存在的Property Get过程,或者说Property Get过程提供了属性的读功能,同样,Property Let过程提供了写属性。这样,上面的两个过程(当然在模块中没有先后的要求),可以只有一个,或者虽然两个都有,但却不全是Public,从而提供出去的属性是只读或只写(呵呵,没见过只写哈)。仅仅是为了提供只读或只写的属性,代码就从一行变成了七行?!这样的理由,你不会信服, VBA中的类通常是提供给我们自己使用的!如果它确实是只读的,我们自觉地去只读就是了!我们使用Property过程还有其它理由,最基本的一条,我们可以利用这个“过程”来做我们想做的事。看一看:

Public Property Let x(ByVal c As String)

  s = Format(c, "0000")

End Property

这里我们只是简单的利用了一下,更多的在后面你会看到。此外,谁会保证有一天你不使用VB给别人提供类呢,这个技术可是通用的。提供一段标准模块的测试代码,来看看我们上面构建的类属性,你自己试试吧。

Sub aTest()

  Dim mc As New MyClass

  mc.x = "123"

  Debug.Print mc.x

End Sub

就象我们给普通变量和对象变量赋值的方式不同一样,对象变量是使用Set赋值的。对“对象”属性,VBA提供了Property Set来代替构建“普通”属性使用的Property Let。来看一段代码:

Private tx As Object

Property Get x() As Object

  Set x = tx

End Property

Property Set x(ByVal o As Object)

  Set tx = o

End Property

和前面的比较一下,除了多一个Set,实在没有什么不同。

告诉你一个小秘诀,你可以按照Function去记住Property Get的用法,按照Sub去记住Property Let /Set

 

[此贴子已经被作者于2007-3-27 23:22:44编辑过]
回复

使用道具 举报

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

3.属性的初始值

我们常常希望,当一个对象建立的时候,它的某些属性会被自动赋予一个初始值,这样,对具有最常见的属性值的对象可以减少重复性的赋值工作。这需要借助于类的构建函数来完成。

在类模块代码窗口的“通用”框中点击向下的小三角箭头,选择“Class”,右面声明框中可以看到两个选项,“Initialize”和“Terminate”,我们对它们应该不陌生,很多对象都有这两个事件,Initialize事件当对象建立时发生,Terminate事件在对象对释放时发生。由于类是静态存在的,它并不是真正的对象,所以在类模块中,它们通常被称为构建函数和析构函数,或构建过程和析构过程。对它们的理解和你在对象中的用法并没有什么不同。当一个对象被建立时,构建函数将被首先执行,同样,当对象释放后,将执行析构函数。

下面建立MyClass,属性x初始值为0001”的全部测试代码:

[类模块MyClass的代码]

Option Explicit

Private s$

Public Property Get x() As String

  x = s

End Property

Public Property Let x(ByVal c As String)

  s = c

End Property

Private Sub Class_Initialize()

  s = "0001"

End Sub

[标准模块1的代码]

Option Explicit

Sub aTest()

  Dim mc As New MyClass

  Debug.Print mc.x

End Sub

隐者为你揭开了第一层面纱,你隐约已看到她美丽的面厐,虽然还不是很清晰,但你知道,早晚会的。

 

[此贴子已经被作者于2007-3-27 23:24:01编辑过]
回复

使用道具 举报

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

五、创建类方法

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

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

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

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

 

[此贴子已经被作者于2007-3-27 23:26:48编辑过]
回复

使用道具 举报

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

2.类的方法环境

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

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

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

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

回复

使用道具 举报

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

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 

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

 

[此贴子已经被作者于2007-3-27 23:29:32编辑过]
回复

使用道具 举报

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

六、创建类事件

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

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

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

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|Excel精英培训 ( 豫ICP备11015029号 )

GMT+8, 2024-4-20 16:49 , Processed in 0.402911 second(s), 6 queries , Gzip On, Yac On.

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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