青岛网站制作方法,phpcms学校网站模板,公司黄页查询,长沙县营销型网站建设选哪家第一章 开头篇 ——认识类 Visual Basic是基于对象的编程(注#xff1a;本文所有的代码和讨论将都以VB为基础模型#xff0c;不过我会尽量使用一些大家在VBA中常见的例子来做说明的。)#xff0c;所以我们常见的一些东西其实都与类有关。不…第一章 开头篇 ——认识类 Visual Basic是基于对象的编程(注本文所有的代码和讨论将都以VB为基础模型不过我会尽量使用一些大家在VBA中常见的例子来做说明的。)所以我们常见的一些东西其实都与类有关。不需惊讶是的类其实离我们很近它们正和我们天天相处。可以夸张的说如果离开了类我们的VB(VBA)就无用武之地了。 我们还是先来看一条非常简单的语句让我们亲身体会一下类与我们的距离 例一Sub Hello() Range(A1).Value Hello,worldEnd Sub
复制代码 我们就暂且称之为VBA的Hello world吧看到这个简单到不能再简单的例子或许你笑了这个根本就和类没有关系嘛。是的我们在这里并没有明显的看到类但是我们看到了Range(A1)这个对象而在Range(A1)前面还隐藏着一个对象ActiveSheet,而在ActiveSheet前面又有一个ActiveWorkbook而在ActiveWorkbook前面还有一个Application。是的这些都是对象我们依旧没有看到类。先别急我们再从后面往前面看一遍Range(A1).ValueHello,World Value是什么Value是一个Range(A1)这个对象的一个属性。那它是从哪来的呢它是由Range类定义的是的我们发现了第一个出现在我们的面前的类 Range。你或许一下还接受不了你刚刚在前不是还说Range(A1)是一个对象吗怎么现在又说Range又是一个类呢。呵呵不着急我们还是看看下面的这个例子或许你就很快就会明白了。 例二Sub Hello() Dim Range1 As Range 引用一个Range类 Set Range1 Range(A1) 将类实例化 Range1.Value Hello,world 这时Range1又是一个对象了 Set Range1 Nothing 销毁一个类的实例End Sub
复制代码 或许你现在已经发现了我们引用了一个Range类并将之实例化后修改了它的属性。而在例一中我们只不过是将这一切都以隐藏起来直接对一个对象Range(A1)修改它的属性但Range(A1)这个对象正是引用了Ragne这个类才具有了Range类的属性Value。类本身并不直接为我们做什么但是它却又一直默默的隐藏在幕后规化着我们的动作。是的这就是类它往往都是通过对象的方式展现在我们的面前让我们无时无刻与之交流却又常常在不经意间忽视了它的存在。现在再回到例一去看就会很容易的发现ActiveSheet实际引用了Worksheet类、ActiveWorkbook引用了Workbook类而Application则引用了和它同名的Application类这也正是我们会经常被混淆一个概念一个对象可以和被它所引用的类同名原来我们在短短一个赋值的语句中已经在与这么多的类打交道。 类就像是我们呼吸的空气我们一直深在其中却又常常忘却了它的存在。那我们又要怎样来区分类和对象呢其实它们经常成对的出现在我们面前只是一个看得见摸得着的一个却深藏不露。我们可以这样去理解类与对象类是一个概念或是一种定义每个类拥有其自己的特征和行为方式而对象就是某个类的实例。就像人人是一个定义你一看到人就会想到它是直立行走、有头、五官……等各种特征但当人被具体到你、我、或是某一个人的时候它就是一个对象了。 既然类与我们和程序息息相关那么我们更应该去好好的了解它了。让我们准备好行囊到神秘的类的领地去好好浏览一番吧。第二篇 走近类 ——类的基本写法及注意项 如果说上一篇让我们看到类隐藏在神秘面纱后的面孔那么现在我们将要做的就是真正的看清楚它。不过在此之前我还是要让大家再次熟悉一下面的几个关键词
Public 外部可供类或是模块自身外调用Private 内部只能由类或是模块内部直接调用Friend 友元只能由工程内调用(即在当前工程内部调用时相当于Public而对外部工程相当于Private。)。Sub、Function 过程(不返回值)和函数(返回值)在类中我们可将其视为类的方法Property Get 返回属性的值Property Let 设置属性的值Property Set 设置对象属性的值即该属性含有对象引用。 [注Property往往是成双出现在类中的某一个属性上甚至可能三者同时出现当一个类中某个属性只有Property Get时代表这一属性为只读属性比如在VB工程中Form类的hWnd属性。]Event 定义用户自定义的事件 [注Event可以像声明过程的参数一样来声明事件的参数但有以下不同事件不能有带命名参数、Optional 参数、或者 ParamArray参数。事件没有返回值。]RaiseEvent 引发在一个类、窗体、或者文档中的模块级中声明的一个事件。Implements 指定要在包含该语句的类模块中实现的接口或类。当我们熟悉上面的几个关键词后再来分析类就会发现类其实也挺简单。我们将从一个简单的类开始来正式的学习类的定义和使用方法。不过在此之前我建议大家先去读读qee用兄写的一个帖子VBA类隐者的秘密qee用兄在那个帖子中图文并茂已经将类清晰的描绘出来并亲自带领我们写了一个类。在此让我们向qee用兄道声辛苦并送上万分感激。好了现在我们来写一个非常简单的类用它来实现两个数的四则运算。 类的名称定义为四则运算Option Explicit Public Event OnError(ByVal Number As Long, ByVal Description As String, ByVal Source As String) Private lNum1 As Double, lNum2 As Double Public Property Let Number1(ByVal Number As Double) lNum1 NumberEnd Property Public Property Get Number1() As Double Number1 lNum1End Property Public Property Let Number2(ByVal Number As Double) lNum2 NumberEnd Property Public Property Get Number2() As Double Number2 lNum2End Property Public Property Get 和() As Double On Error GoTo Error01 和 lNum1 lNum2 Exit PropertyError01: RaiseEvent OnError(Err.Number, Err.Description, Err.Source) Err.Clear 和 0End Property Public Property Get 差() As Double On Error GoTo Error02 差 lNum1 - lNum2 Exit PropertyError02: RaiseEvent OnError(Err.Number, Err.Description, Err.Source) Err.Clear 差 0End Property Public Property Get 积() As Double On Error GoTo Error03 积 lNum1 * lNum2 Exit PropertyError03: RaiseEvent OnError(Err.Number, Err.Description, Err.Source) Err.Clear 积 0End Property Public Property Get 商() As Double On Error GoTo Error04 商 lNum1 / lNum2 Exit PropertyError04: RaiseEvent OnError(Err.Number, Err.Description, Err.Source) Err.Clear 商 0End Property Public Sub ClearNumber() lNum1 0: lNum2 0End Sub
复制代码 然后将下面的代码写到ThisWorkbook模块中 Option Explicit Private WithEvents Class1 As 四则运算 Sub TestClass() If Class1 Is Nothing Then Set Class1 New 四则运算 Class1.ClearNumber Class1.Number1 9 Class1.Number2 8 Debug.Print Class1.和 Debug.Print Class1.差 Debug.Print Class1.积 Debug.Print Class1.商End Sub Private Sub Class1_OnError(ByVal Number As Long, ByVal Description As String, ByVal Source As String) MsgBox 类中发生错误错误代码 Number 错误提示 Description 错误源 SourceEnd Sub
复制代码 这是一个非常简单的类的定义和应用(当然实际编程时很少有人会为了四则运算而专门写个类。)虽然它很简单不过它却几乎包含了类所有常见的特征属性、方法、事件。运行TestClass后我们可以看到立即窗口中的输出并接收到一个MsgBox的窗体它提示了我们在类运行过程中产生了一个除数为零的错误而这正是类中的OnError事件激发的。 虽然这个类本身没有什么实际的应用的意义但是我们却可以用它来做一个模版我们只要参照这个类就可以很快的写出自己想要的类。 你现在是不是已经有了自己马上要编写一个类的冲动呢那么我们开始准备动手吧不过在你动手之前我这里还要给你几点关于类的建议(这些都是笔者自身积累的经验或是前辈们的忠告。) 一、在正式编写一个类的前期最好尽可能规定好所有要用到的接口与方法。一旦一个类被正式发行后(封装成DLL并被其它的工程引用)后期维护时尽量不要再去修改已有的接口函数及其参数除非你打算将所有的工程全部重写 二、为你类中的属性、方法、事件取个有意义的名字(特别是声明为Public方式的)如果允许的话最好使用“工具”-“过程属性”为其添加一些描叙说明它的意义或是调用方式这样可以方便查看其属性并让客户(甚至是自己)能很快明白这个函数的用途 三、只暴露必须的接口供外部调用不要将一些可能仅在类内部使用属性和方法暴露给类外部 四、不到万不得已不要在类中定义Public方式的变量 五、重新编译新版本的DLL时最好是按“版本相同、版本兼容、版本不兼容”这个次序来选择编译后DLL版本兼容性即能用“版本相同”方式就不要用“版本兼容”更不要用“版本不兼容”因为“版本不兼容”方式就意味着你的旧工程在其重新编译前将无法使用这个DLL。一旦准备使用无兼容性版本那么就要考虑如何减少部件的用户以后可能遇到的麻烦。如果以后的版本还要做一些可能破坏兼容性的修改那么最好把这些修改集中在一次进行。计划对不兼容进行的修改时把工程作为一个新的任务来对待应该投入尽可能多的精力进行计划就象在创建新的部件。创建不兼容版本这要求三个步骤改变工程名改变文件名以及通过选定不兼容进行编译(注这三个步骤最好是一定要做到否则将会让我们在后面安装引用此类时会发生各种问题而导致我们无法使用类) 六、可能的话不要忘了在类内部添加错误处理过程尽管这些错误处理过程可能会损耗我们的运行时间但它们能让我们的类更健壮 七、类中不能定义Public方式的结构(Type)如果必需要这么做的话可以使用一个类来代替Type 八、在类的Class_Terminate()事件中释放被当前类所引用的类或是数据。当我们某个类的实例已经不再需要时请将其使用 Set[I]Object[/I] Nothing 的方式来销毁它 九、……(留给大家填吧)……第三篇 为什么要使用类 ——类的作用及其重要性 现在我们已经初步的认识了类甚至我们已经可以自己开始写自己的类了。但是我们却还不是很清楚类给我们带来了什么我们为什么要用类我们就先来看看类的好处吧。 1、代码重用 2、降低程序的藕合度 3、增强程序的可拓展性 4、易修改性 5、…… “藕合度”指的是程序模块间存在联系的紧密程度。如果一个程序各模块存在的联系太紧密时就意味后期的修改将会非常复杂甚至于有时为了修改某一个代码功能时可能要全部工程文件都修改一次并且由于各模块间的函数与变量相互相交调用严重的影响了程序的可读性。自然我们不希望发生这种情况。而类正是我们规避这个情况发生的首选工具。因为类对于外部而言我们所需要的仅仅是调用类的接口函数而类内部的结构与运行我们并不需要去关心它。就像一个用来处理数据的类我们只要约定好类的接口就可以在外部直接获取或是修改我们想要的数据。但是数据存在哪用什么方式或是结构组成我们根本就不需要了解我们所要做的只是在外部直接调用就好了。这就意味着我们一旦约定好了接口就可以同时开始开发前台与后台的数据处理块。而且当我们需要修改数据的存储方式时也会变得更加简单。比如我们现有一个处理数据的类它目前是使用文件来存储数据现在我们需要把数据存储改为SQL数据库这时我们要做的仅仅是在类内部修改要数据的处理过程而对于整个工程而言我们甚至不用做任何的修改就可以直接用了。这样对于大型工程而言犹为重要因为我们常常把类封装成DLL这就意味我们仅仅只需要重新编译这个DLL然后发布给用户升级就完成了这次修改否则的话我们只能将整个工程全部修改后重新发布给用户才能使用。如果你经常使用VBA的话就一定会常常遇见Range这个类Office每一次大版本升级其内部的文件结构都会有变动但是我们在VBA中对Range的操作却无需更改就可以直接使用了这个正是因为Range类虽然内部改动了但是它的接口还是一样所以我们原有的VBA工程并不需要改动就可以直接使用了。 现在我们再来通过一个选中区域高亮显示的例子来看看类的其它几个特性吧 Option Explicit Private Type TypeRageLast Range As Range ColorIndex As LongEnd Type Private Const ColorIndex 5Private RangeLast() As TypeRageLast Private Sub Worksheet_SelectionChange(ByVal Target As Range) Dim I As Long Dim Range1 As Range On Error Resume Next If UBound(RangeLast) -1 Then ReDim RangeLast(0) ElseIf UBound(RangeLast) Then For I 1 To UBound(RangeLast) If RangeLast(I).Range.Interior.ColorIndex ColorIndex Then RangeLast(I).Range.Interior.ColorIndex RangeLast(I).ColorIndex End If Next I End If I 0 ReDim RangeLast(0) For Each Range1 In Target I I 1: ReDim Preserve RangeLast(I) RangeLast(I).ColorIndex Range1.Interior.ColorIndex Set RangeLast(I).Range Range1 Next Range1 Target.Interior.ColorIndex ColorIndexEnd Sub
复制代码
这段代码的功能很简单(我们在这里并不去讨论它的功能)只是在工作表中选中某个区域后就把该区域的背景色设置为并且恢复上次被选中区域的原有背景色。我们只要把上面的代码拷贝到我们需要的工作表的代码中就可以实现这个功能了。但是当我在实际使用过程中就会发现一些新的问题 、如果我们的工作簿中有200个工作表需要这个功能我们就要复制200次——晕倒…… 、复制完200次后我们再在各个工作表中实际测试一下。就会发现新的问题当我们在各个工作表之间切换焦点时原工作表中最后一次被高亮的区域没有被还原现在我们需要修改代码——在每一个工作表中Deactivate事件中加上相应处理代码。天啊又得200次……又要晕了吧 、文档开始正式使用了但是当我们新增一个工作表后发现我们又得重新切换到VBA中再复制一次代码如果这个文档是给客户使用的难道我们还要要求客户每次增加工作表时自己去复制VBA代码……这次彻底晕了吧^_^ 难道就没有更方便的方法吗 当然有了。我们就用类来试试吧,我们先建一个clsSelHeightColor类 ‘类名clsSelHeightColorOption Explicit Private Type TypeRageLast Range As Range ColorIndex As LongEnd Type Private Const ColorIndex 5Private WithEvents mSheet As WorksheetPrivate WithEvents Workbook As WorkbookPrivate RangeLast() As TypeRageLast Private Sub mSheet_Activate() Dim I As Long Dim Range1 As Range On Error Resume Next I 0 ReDim RangeLast(0) For Each Range1 In Selection I I 1: ReDim Preserve RangeLast(I) RangeLast(I).ColorIndex Range1.Interior.ColorIndex Set RangeLast(I).Range Range1 Range1.Interior.ColorIndex ColorIndex Next Range1End Sub Private Sub mSheet_Deactivate() mSheet_SelectionChange NothingEnd Sub Private Sub mSheet_SelectionChange(ByVal Target As Range) Dim I As Long Dim Range1 As Range On Error Resume Next If UBound(RangeLast) Then For I 1 To UBound(RangeLast) If RangeLast(I).Range.Interior.ColorIndex ColorIndex Then RangeLast(I).Range.Interior.ColorIndex RangeLast(I).ColorIndex End If Next I End If I 0 ReDim RangeLast(0) For Each Range1 In Target I I 1: ReDim Preserve RangeLast(I) RangeLast(I).ColorIndex Range1.Interior.ColorIndex Set RangeLast(I).Range Range1 Next Range1 Target.Interior.ColorIndex ColorIndexEnd Sub Public Property Set Sheet(ByVal mSheet1 As Worksheet) If Not (mSheet Is Nothing) Then mSheet_SelectionChange Nothing Set mSheet mSheet1 Call mSheet_ActivateEnd Property Public Property Get Sheet() As Worksheet Set Sheet mSheetEnd Property Private Sub Class_Initialize() ReDim RangeLast(0)End Sub Private Sub Class_Terminate() mSheet_SelectionChange Nothing Erase RangeLastEnd Sub
复制代码
然后再把下面的代码复制到ThisWorkbook中的代码框中 Option Explicit Dim mSheets As Collection Private Sub Workbook_BeforeClose(Cancel As Boolean) Set mSheets NothingEnd Sub Private Sub Workbook_NewSheet(ByVal Sh As Object) Dim clsSheet As New clsSelHeightColor Set clsSheet.Sheet Sh mSheets.Add clsSheet Set clsSheet NothingEnd Sub Private Sub WorkBook_Open() Dim clsSheet As clsSelHeightColor Dim Sheet As Worksheet On Error Resume Next Set mSheets New Collection For Each Sheet In Sheets Set clsSheet New clsSelHeightColor Set clsSheet.Sheet Sheet mSheets.Add clsSheet Set clsSheet Nothing NextEnd Sub
复制代码
保存后重开工作簿测试……嘿嘿全部搞定而且当我们要增加的Deactivate事件中的代码仅仅只需类中添加一次只有一行代码(上面的类中已经添加好了)方便吧当我们新增工作表时也不再需要去复制一次代码了一行代码都不用写了就已经实现了这个功能。当其它工作簿也需要这个代码时我们只要在ThisWorkbook代码中增加相应的代码并导入这个类就行了比一个一个表去复制代码方便多——一想到相同的代码要在每个工作表中复制一次就头晕了。 在此我们对类的功能和特性有了一定的了解不过还有更多的东西在等待我们去发现。当下面向对象编程如此风行类在其中的功劳是无与伦比的。 现在还犹豫什么快去和类做一次“距离接触”吧…… 在此此文也终于要作一个小小的终结了。大家也跟随我初步对类作了点了解也算是对类有了个第一印象吧。当然类的高级应用和复杂型的类我们并没有去了解或许哪天你会引领我去浏览一番 我也谢谢大家浪费不少时间来看完这个帖子如果对我有意见或是觉得我在胡说八道要拍我砖头的请下手留点情——我很怕疼有意见扔扔鸡蛋或是RMB就好了。如果觉得看完对你有所帮助的话就帮顶下吧或是你看完有什么问题的话也可以直接问只要我会的我一定回(太难的问题还请恕我无能为力)。