ActiveX深入浅出

     可以这样说,Active平台代表了Microsoft的世界观。使用ActiveX控件,来构筑包括从与用户交互和适应COM的事务处理监视器,到Web服务器,全部实现自动化的机构,这就是Microsoft的打算了。桌面用的、基于COM的组件叫做ActiveX控件。所谓ActiveX控件不过是遵从一定的标准、与客户端交互的COM对象而已。基于组件的应用开发,其方法和组装电子装置一样,可以用已制作好的组件部件来构筑应用。这确实是一项很令人神往的技术。虽然说起来高深,但随着ActiveX的广泛应用,越来越多的编程工具都支持创建ActiveX控件了。这其中就包括了VB。在VB中设计一个ActiveX并不比一个普通的VB应用程序难上许多。这篇文章,就是一步步教你如何在VB中创建一个ActiveX控件。
   ActiveX深入浅出(一)
 什么是ActiveX控件?
   一个简单的回答就是:拥有图形界面的类。你可能曾经用过类来编程,那是一种实现代码重用的的好方法。当然,它也提供了很多其它的好处,这儿并不打算一一列举出来。ActiveX 控件将这个概念近一步的深化了,能够让你编写一个窗口小部件(widget),然后把它打包起来,在以后的程序里用到它,或者作为特定的问题的解决方法,给程序员提供更简便的编程方法。用ActiveX你可以建造“复合式”控件,由其它的几种控件组合而成。也就是说,在ActiveX控件中你不但可以使用VB中诸如文本框,图片框之类的普通控件,你还可以用其它的ActiveX控件来构成你自己的控件,实现你想要的功能,并把它打包以背后用。
 ActiveX控件的组成
   一个ActiveX控件由它的一些成员组成:属性,方法以及事件。它们之间有什么样的逻辑关联呢?还是用我们的身体来做个例子吧,将身体就看成一个ActiveX控件。这个控件首先应该有一些属性,比如:眼是否睁开属性。显然,这个属性值应该有两种情况:开或则闭。使用的时候能够告诉“身体控件”让这个属性换一个新的值,以决定睁眼或者闭眼,或者得到当前的属性值,以知道目前的身体状况。
   方法是控件中过程和函数的统称,同其它任何的VB函数和过程并没有什么不同,你同样可以向它们传递参数,并返回想要的值。假设有一个类描叙了身体这个对象,它应该有“Look”这个方法,并且应该能接受“Direction”这个参数,那么,这个方法就应该写成这样:
   Public Sub Look(Direction As Integer)
    Select Case Direction
    Case 0
    '向左看
    Case 1
    '向右看
    Case 2
    '向前看
    Case 3
    '向后看
    End Select
    End Sub
   若要求返回值,我们就举一个“读”的例子吧。这时你必须把“Read”这个方法声明成函数而不是过程 Public Function Read() As String
    '读的一些操作
    Read = "Hello from the World"
    End Function
   在身体控件中,我们用的还是“Look”这个方法。当调用这个方法的时候,就是指定眼睛应该去“看”了。同样,我们还指定了另外一个方法,“Read”。这一个方法将用来返回看到的东西。
   “方法”这个概念是不是很简单呢?如果你还不能够理解,也可以这样来想:你的控件就像是一台机器,你拨动控制开关(输入参数),转动把手(调用方法),然后机器的灯闪动,运行起来(执行方法),最后从机器里蹦出一些东西(返回值),就是这样了。但等等……如果你的机器想告诉你什么事情,它该怎么做呢?这时就该是“事件”出场了。
   最后,身体控件还会提供一个“眨眼”的事件,用来在进行眨眼这个动作的时候,通知开发者发生了这个事件,但是不必知道身体内部的工作方式和为什么这个事件会被触发。
   来动手吧,作一个控件。不要以为作一个控件是非常难的事,虽然它和一般应用程序的编写是有一点不同的地方。在上面我们已经知道,一个ActiveX控件是由属性,方法和事件组成的,我们先看看如何在程序中实现这些东西,然后再把它们联系起来,组合成一个右机的整体。属性最简单的形式是用public声明的公用变量。例如,如果把下面这段代码放到你的控件工程的声明部分:
   Public EyeOpen As Boolean
   这样,你就可以在后边的代码中使用这个属性了。但是,这种属性所能够做的事实在太少了。它几乎是不能够正常工作的。因为在程序的设计期间,属性值的任何变化都必须通知Visual Basic,以便把控件实例标记为需要保存。而又因为属性值可能显示在多个地方,因此当属性值发生改变时必须通知开发环境,以便使它能够同步显示“属性”窗口、“属性页”对话框等位置上的属性值。
   上面说的是不是有点难理解呢?要是不明白说的什么,不要急,你先打开一个工程,添加一个控件,试着改改这个控件的一些属性,在来看看上面的话,是不是明白了呢?我们现在的任务,就是要作一个这样的控件啊。由此可以看出,控件编程和一般的编程还是有一些区别的。那么,该怎么实现数行呢?这要使用到属性过程。
   当一个属性值被引用或者设置的时候,属性过程自动的被调用。下面我们就来添加一个这样的属性:打开代码窗口,再点击“工具”菜单,选择“添加过程”子菜单,弹出的对话框中,填入过程名“EyeOpen”,然后再把类型设置为“属性”。当你点了确定之后,VB自动的为你创建了一个属性过程的原型,代码会被添加到代码窗口中:
   Public Property Get EyeOpen() As Variant
  End Property
   Public Property Let EyeOpen(ByVal vNewValue As Variant)
  End Property
   剩下的你要做的,是写入属性处理代码,把这个骨架填满。
   你可以看到,VB实际上为你写了两个,“Get”和“Let”属性过程,有点疑惑吗?实际上很简单:“Get”是当属性值被引用的时候调用的过程,而“Let”则是当属性值被写入的时候调用的。(事实上还有第三种类型的过程,我们将在后面遇到,这里先提个醒)要让一个属性过程能够正确的工作,必须还要有一个变量来保存真正的属性值。把下面这一行添加到声明部分:
   Private m_EyeOpen As Boolean
   注意到那个'm_'前缀,一般是放在用户控件的内部变量之前。现在该是填充过程框架的时候了。先看看Let过程,这个过程带有一个参数:缺省的是名字是vNewValue,variant类型。但我们想要一个布尔类型的变量而不是variant,因为眼睛在我们的模型中只有开和闭两种状态。所以把这个参数改成“New_EyeOpen As Boolean”。如果你想要这个属性作为只读属性出现,那么就不要再改动Let过程,让过程体空在那儿。否则,当想要设置这个属性值的时候,应该执行这样的代码:
   Public Property Let EyeOpen(New_EyeOpen As Boolean)
   m_EyeOpen = New_EyeOpen
   PropertyChanged "EyeOpen"
   '后面还可以写一些相关的代码,对属性的设置做出反应
   End Property
   当要在程序中写入属性值的时候,就会调用这个函数,先把属性值保存在一个私有变量里面,然后执行PropertyChanged这个内部方法,它用来告诉Visual Basic属性值有了变化,并触发一个WriteProperties事件。关于这一点的具体的内容,在后面还会提到。
   而Get过程更为简单!它和标准函数没有两样:
   Public Property Get EyeOpen() As Boolean
   EyeOpen = m_EyeOpen
   End Property
   做完这些就完了吗?不!忘了前面说的属性值是需要保存的,因此当编程会话发生转换的时候,它们能够保持下来。那么怎么样来保存和取出属性值呢?这时就要用到PropertyBag对象了。
   使用PropertyBag
   PropertyBag对象包含两个方法:一个用来读出,一个用来写入。前面提到了,当任何的属性发生变化得时候,会触发控件的WriteProperties事件。这时就可以把属性值保存在属性包中了。下面的代码实现这个功能:
   PropBag.WriteProperty "EyeOpen", m_EyeOpen, True
   PropBag是PropertyBag对象的一个实例。WriteProperty函数包含三个参数,第一格是属性名称,后面的是要保存的值,最后的参数是在无用户定义属性的情况下,将写入的默认值。把这个和ReadPropertiy方法的默认值设置结合起来,就可以为属性值设置缺省值了。如果属性值和缺省值相同,那么属性值就不会真正的别保存。当要读出的时候,ReadProperty函数发现在属性包中没有内容,就会返回缺省值。这样可以节省一些系统开销。要注意的是:必须将一个成员属性的名称作为字符串传递。当你将控件作国际化处理时,不要改变这一字符串的名称,它必须与该属性的申明的名称相匹配。
   当控件被重启动后,你必须重载如所有保存的属性值。当每次读取PropertyBag中的数据时,ReadProperties事件被触发。在这个事件的处理过程中,你要做的任务就是载入保存在属性包中的属性值,调用ReadProperty函数来实现值一点。要注意的是:在读和写两个函数中的缺省值设置要一样。例如:
   m_EyeOpen = PropBag.ReadProperty("EyeOpen", True)
   一个同步缺省值的方法是为其设置常量,在需要的时候直接使用常量来代替具体的值,这样就不用担心出问题了。例如:
  Private Const m_def_EyeOpen = True
   “m_def_”前缀通常放在缺省值常量的前面。
   当控件被启动的时候,一般要为属性设置初始值。这一般是在usercontrol的InitProperties 事件中实现的。这个事件发生于控件实例首次实体化时,也就是把控件实例放置到窗体上的时候,它在以后的整个设计期间里都不会再发生。Usercontrol还有一个Initialize事件,它是每次控件实例被创建时触发的。显然,初始化过程如果放在Initialize事件中就太过频繁了,没有这个必要。设计ActiveX 控件需要根本性地转变一些观念。需要响应的关键事件是往往不同于一般的程序设计。下面是我们想要实现的代码:
   Private Sub UserControl_InitProperties()
   m_EyeOpen = m_def_EyeOpen
   End Sub
   好,到现在关于属性的部分基本就结束了,下一篇中我们要讲到的是另外的一个成员:方法。
   [SplitPage]
    What an Event
  
    事件是用来通知使用控件的程序员,某件事情发生了,好让程序员可以相应的处理。事件在VB编程中比比皆是,例如,当鼠标点击控件事会发生Click事件,文本框内容改变了会发生Change事件,等等。但这儿要讲的不同于这些东西,也不同于InitProperties, ReadProperties, WriteProperties前面提到过的事件,那些是系统已经定义好了的。我们要做的是定义自己的事件。自定义的事件必须声明在模块的声明部分,然后就能够在任何地方任何时候,只要你觉得应该有事件了,就可以触发它。例如,一个代表眨眼的事件应该这样声明:
   Public Event Blink()
  在括号中,你可以放置任何你想要传递给事件的参数。对于一个Click事件,这个参数可能鼠标点击时x和y坐标。而这在理,应为只是眨眼,就用不到额外的参数了。
   触发事件,得使用RaiseEvent方法。对于我们的眨眼事件,我们把它和一个计时器联系起来,这样它就能够时不时的眨眼了:
   Private Sub Blinker_Timer()
   RaiseEvent Blink
  End Sub
   就这样,一个事件就完成了,没有任何技巧或者秘密可言。现在留给程序原作的,只要编写相应的事件处理过程就行了,就像下面这样:
   Private Sub BodyControl1_Blink()
   Debug.Print "嘻嘻,我又眨眼了!"
   End Sub
   到这儿,大部分关于ActiveX控件的基本问题都讲了。后面的将是一些更高级的内容,包括图片和字体属性,“关于”对话框,运行时之读属性等等。在继续之前,再看看前面的内容,好好的在理解一下。好了吗?OK,现在开始更为激动的历程。
   首先,我们先来看看对于属性,还有什么可以挖掘的东西,比如颜色或者图片之类的东西。
   高级属性设计
   色彩值被存贮在长整形变量中,但是如果你只是定义一个长整形变量,显然是不能够得到像vb中所提供的那个颜色选单:
   这看起来很复杂,实际上做起来却一点也不难:所有你要做的只是把属性声明为OLE_COLOR类型,就像下面这段代码所作的那样:
   Public Property Get BackColor() As OLE_COLOR
   BackColor = UserControl.BackColor
   End Property
   Public Property Let BackColor(ByVal New_BackColor As OLE_COLOR)
   UserControl.BackColor = New_BackColor
   PropertyChanged "BackColor"
   End Property
   记得前面曾经提过除了Let,Get,还有第三种属性过程吗?现在揭开迷底:它就是Set属性过程,当要给对象变量赋值的时候,是不能用Let,而必须用Set来代替。这是因为保存在控件内部的对象变量,保存的并不是对象的拷贝,而只是对象的引用,也就是一个内存地址了。为了同一般变量的复制保存区别开来,VB引入了Set属性过程。
   或许你可能知道:字体和图片就是保存在对象中的,而且,它们都拥有各自的的对话框来设置相关属性。要用到这些对话框,所有我们要作的就是把图片或者字体申声明为Picture或者Font对象类型,并且为其设置Set属性过程。
   Public Property Get Font() As Font
   Set Font = lblText.Font
   End Property
  Public Property Set Font(ByVal New_Font As Font)
   Set lblText.Font = New_Font
   PropertyChanged "Font"
   End Property
   看看上面的代码,你是不是在想:也没有什么很难的啊。确实,就是这样简单啊。下面,我们来看看如何为控键建立只读属性。这也是在控件的设计中用的比较多的一个内容。
   只读属性
   最简单的方法,就是不要在Let/Set属性过程中加入任何东西。但通常,这并不能满足要求,有时,你可能需要一个运行时只读的属性。所谓运行时——和设计时相对应,是指控件最终在一个开发完成的程序中运行,而设计时指的是控件被使用在开发程序的过程中。
   要实现运行时只读,要用到Usercontrol的AmbientProperties对象。它提供了很多关于控件容器的属性。中有一个UserMode属性,当控件处在运行时状态时,UserMode值为真。通过在Let/Get过程中提供对UserMode的检测,就可以很容易的实现运行是只读属性了:
   Public Property Get MultiLine() As Boolean
   MultiLine = m_MultiLine
   End Property
   Public Property Let MultiLine(ByVal New_MultiLine As Boolean)
   If Ambient.UserMode Then
   Err.Raise 382
  
    Exit Sub
   EndIf
   m_MultiLine = New_MultiLine
  PropertyChanged "MultiLine"
  End Property
   这段代码保护属性只能够在设计时被修改,如果在运行时试图改变它,就会产生“Property is read-only at run-time”错误。
  和AmbientProperties对象相近的还有Extender对象。对于Extender对象,在开始编写控件前有必要好好的了解。Extender对象是一个晚期绑定的借口,开发员可以通过它访问由控件容器(而不是控件本身)维护和控制的控件属性。它提供了一些属性,像Name,Enable,Left,Top,Height,Width等等,这些大多出现在一般的控件中,在编写控件属性前,应该看看是否已经存在在Extender对象中,一方面可以避免重复工作,另一方面也更有效率。
   但是使用Extender对象还存在一些问题:并不是所有的容器都支持访问相同的Extender属性。所以选用Extender对象是必须十分小心,否这做出的控件只能为特定的容器所使用。但如果你只是为VB开发控件,那就不用有这些顾虑了,尽量的用吧。
   还有一点要注意的是,Extender对象不能在usercontrol的initialize事件中访问它,但是可以在initProperties和ReadProperties事件中使用。
   枚举
   在控件中设置属性时,使用枚举是一种很常见的的方式。它提供了一个下拉列表和若干选项让你选择。这样方便了用户的操作,又不用考虑过多的兼容性和错误处理问题,简化了属性设置,而且更加安全。
   首先,必须建立一个枚举结构,放在声明部分。然后给出一系列的常量和对应的字符串。常量值可以零,或者是任何比它前面一个常量值大的整数。如果没有给出常量,那么VB自动为其赋值,第一个未指定的赋值为零,其它的值为前面一个数加一:
  Public Enum eDirection
   Left
   Right = 1
   Up
   Down
  End Enum     要实现枚举属性,必须创建一个带有Let和Get属性过程的标准属性。这里的技巧是将属性的类型声明为给出的枚举类型:
   Public Property Get Direction() As eDirection
 Direction = m_Direction
   End Property
   Public Property Let Direction(ByVal New_Direction As eDirection)
   m_Direction = New_Direction
   PropertyChanged "Direction"
   End Property
   唯一要注意的是,只能够在设计时修改属性列表,而不允许在运行时这么做。其它的,像读,写,保存和检索,都和使用标准属性是一样的。
   这就是所有的技巧了。难以置信的简单,是吗?你的控件是不是看起来更为专业了呢?
   Usercontrol对象
   用Visual Basic 所创建的ActiveX 控件总是由UserControl 对象加上选中放到UserControl 上的任何控件(称为子控件或则组成控件)所组成。就象Visual Basic 窗体一样,UserControl 对象具有代码模块以及可视化的设计器。将组成控件放到UserControl 对象的设计器上,就象把控件放到窗体上一样。在窗体上放置ActiveX 控件的实例时,就创建了UserControl 对象,以及放在UserControl 设计器上的所有子控件的实例。这些对象都被封装在控件中。
   UserControl 对象有自己的属性、方法和事件。对于一些属性,像BorderStyle,BackColor等等,与其自己写代码来实现BackColor 属性,不如就直接使用UserControl对象的。这就意味着ActiveX 控件的BackColor 属性只需要简单地调用UserControl 对象的BackColor 属性就行了。同样地,也可以在UserControl 对象现有的Click 事件的基础上设计您的控件的Click 事件。
  事实上在前面的内容中,我们时时都在和这个对象打交道,但是它有一些特别的东西,是应该值的多一些注意的。它的大部分属性应该在控件的设计时来完成设置。在这儿我解释一些比较晦涩难懂得属性的含义,还有一些一望既知,就不多费口舌了。
   Alignable属性,当被设置成真的时候,VB将自动为控件添加一个新的属性:align。这样就能够像放置工具条那样安排控件在容器中的位置,而且这还意味着你的控件能够被放置在MDI程序中。
  CanGetFocus属性,能够决定用户控件是否能够在运行时获得焦点。当要创建一个图形控件,或者像Timer那样在运行是不可见的控件时,就要设置这个值为False。要注意的是:只要控件至少包含一个设置为能够接收焦点的子控件,CanGetFocus 属性就不能设置为False。如果CanGetFocus 设置为False,则其所有的子控件都不能设置为接收焦点。
  ControlContainer属性定义一个控件是否能够像frames或者PictureBoxes控件那样作为控件容器包含其它的控件。
   DefaultCancel 属性可以为控件添加Default和Cancel属性。在添加Default 和Cancel 属性后,控件就可充当标准命令按钮。也就是说当Default被设为Ture后,按下回车键会触发控件的click事件,而当Cancel属性被设为Ture时,Esc键按下也会触发click事件。
   你可以通过检查AmbientProperties对象的DisplayAsDefault属性来知道控件是不是缺省控件。
   InvisibleAtRuntime能够让你建立像Timer之类的控件,在运行的时候是不可见的。
   最后,ToolboxBitmap 属性用来指定放在VB工具箱上的图标的。微软建议的大小是32x32,但是实践证明23x23或24x24工作起来更好,32x32是被缩放到那么大了再显示出来的。
   属性的属性
   说起来有点绕口,也有点费解,是吗?。VB允许为控件的每个成员设置属性。这里面包含了一些较高级的内容,能够让你建造更为专业化的控件。在菜单栏中点击工具|过程属性,会出现下面的对话框:
   你可以在描叙框内输入一段对控件的说明,在帮助上下文标识符内输入一个帮助的关联ID号,将你的控件与一个帮助文件关联起来,这样当点了属性后再按F1键就可以给出这个属性的帮助内容。
   利用“在属性浏览其中使用本页”字段,可以给控件的定制属性页分配选定的号码。这样当用户从VB的属性浏览器中选择该成员时,VB将直接线是属性页。“属性分类”字段能够让属性在VB的属性浏览器的“按分类序”模式中出现在特定的类别下。这些类别包括外观,字体,位置,杂项等等,只要选择一项就行了。“隐藏该成员”可以让属性不在属性浏览器中显示出来,这对于一些不想让用户看到得公有成员有用,但是要记住,它只是隐藏而不是不许被使用。而利用“在属性浏览器中不显示”可以在控件的设计时(而不是在运行时)把属性从属性浏览器中去掉。作为一般的原则,任何用ReadProperties和WriteProperties实现的永久的属性,都应当被属性浏览器显示出来,反之,任何非永久性的属性就不应被显露出来。
  “缺省用户界面”用来设置控键的缺省属性和方法。比如,因为Caption属性是Lable控键的缺省属性,那么就可以对代码进行一些简化,把Label1.Caption = "Hello"
   可以简写为:Label1 = "Hello"
   最后是关于数据绑定的部分。在VB中,数据库的应用是很常见的。利用MS的数据访问功能,可以很容易的把控件属性和数据库的字段联系起来。当选择了“属性为数据绑定”和“绑定属性到数据字段”两个复选框后,该属性就可以以标准绑定控键的方式运行,这意味着你可以在为该属性选择数据库控件和字段名称。
   最后的话
   到这儿,几乎大部分和用户控件有关的内容都讲完了。你现在就可以开始为自己的程序定制合适的控件,还可以让别人分享你的成果。虽然内容不是很多,但要完全吃透也不是件容易的事。所以,多看多练还是必要的,学无止境嘛。
   当然,没有十全十美的东西,对于ActiveX控件也是这样的。合理的使用用户控件是必须要注意的事情。ActiveX控件很容易就会变得很复杂,一不小心的话,你所付出的会远远多于你所得到的。
   如果你只是想要一个带有属性和方法的对象,那么类或许是更好的选择。用户控件与应用程序间相对复杂的接口,会占用较多的资源。而在编程方面,控件也要比类复杂一些。但是如果你要求属性,方法和事件实现永久的数据存贮,后者要在程序的外部实现组件更新,那么就要用到用户控件了。总之,不要以为这是一件容易的是。虽然我在这儿讲得很简单,但是对一个完整的用户控件的设计,编写,以及测试和调试所作的工作,绝对不会比一个一般的VB应用程序来的少。
VB6.0动态加载ActiveX控件漫谈 深圳 罗汉军 罗德昌 熟悉VB的朋友对使用ActiveX控件一定不会陌生,众多控件极大地方便了编程, 但唯一的缺陷是不能动态加载控件,必须在设计时通过引用,将控件放置在窗体上。 VB6.0已能够解决该问题,只是帮助中没有明确说明,并且没有描述到一些关键功 能,由于以前的版本中可以动态创建进程外服务:如果对象是外部可创建的,可在 Set 语句中用 New 关键字、CreateObject 或 GetObject 从部件外面将对象引用赋予变 量。 如果对象是从属对象,则需使用高层对象的方法,在 Set 语句中指定一个对象引 用: Dim xlApp1 As Excel.Application Set xlApp1 = New Excel.Application 或 Dim xlApp As Object '定义存放引用对象的变量。 Set xlApp = CreateObject("excel.application") xlApp.Visible = True ---- 这些语法很容易造成误导,以为动态加载ActiveX控件也是此方法,可能有朋 友也象我一样利用CreateObject尝试了无数次,却无功而返,不知微软公司是出于何种 考虑,动态加载ActiveX控件是扩展控件集合的方式实现,通过实际摸索,终于就如 何实现动态ActiveX控件找出了一条切实可行的方法,下面以一个具体的实例来详细说 明。 一、ActiveX控件 ---- ActiveX 控件是 Visual Basic 工具箱的扩充部分。使用 ActiveX 控件的方 法与使用其它标准内装的控件,如 CheckBox 控件,完全一样。在程序中加入 ActiveX 控件后,它将成为开发和运行环境的一部分,并为应用程序提供新的功能。 ---- ActiveX 部件通过客户端/服务器关系与应用程序— 及与部件相互之间— 交 互作用。客户端是使用部件功能的应用程序代码或部件。服务器是部件及其关联的对象。 例如,假设应用程序使用 ActiveX 控件来提供一个标准的雇员窗体,供公司的多种 应用程序使用。提供雇员窗体的 ActiveX 控件就是服务器,使用这个控件的应用程序 就是服务器的客户端。 二、加载方法 ---- VB6.0中对Controls 集合进行了扩展,以前版本中Controls 集合在窗体上列 举出已加载的控件,这在迭代过程中是很有用的。Controls 集合标识一个叫做 Controls的内在窗体级变量。如果忽略可选的 object 所在处的整数,则关键字 Controls 必须包括在内。我们通常在窗口中使用如下代码: Text1.Text="Hello, world" 其实也可以使用如下代码达到同一目的: Controls(1).Text="Hello, world" ---- 在VB6.0中除了原来的Clear、Remove 方法外(很奇怪,为什么微软在VB5.0中 只提供这两种方法,而没有提供Add方法,因为没有Add,这两种方法也就没什么用处), 增加了Add方法,该方法就是用于动态加载控件的: Controls.Add(progid as String, Name as String); progid: ActiveX部件的ProgID,如:"VB.CheckBox"; Name:ActiveX部件加载后的名称,如: "MyCheckBox"; 若要在窗体上添加一个名为MyButton的按钮,可以使用: dim oControl as Object '窗体级变量 注意:这里声明为Object对象类型 Private Sub LoadControl() Set oControl = Controls.Add ("VB.CommandButton", "MyButton") oControl.Left = 10 oControl.Top = 10 oControl.Visible = True '使控件可见 End Sub ---- 这是VB6.0的标准语法,它在例程中也是如此演示的,不过该方法虽然现实了 控件的动态加载,按钮显示在窗体上,可以象普通按钮一样按下去,但加载的控件不能 预先设计响应事件代码,如:事件Sub MyButton_Click()将是非法的,当然,可以将要 响应的事件封装在控件内部。就编程的观点来看该方法没什么大的用处,开发ActiveX 控件的目的是为了资源共享,为了被其他开发人员利用,所以要提供必要的事件接口, 显然利用该方法不行,通过分析VBControls等相关对象,找出VBControlExtender对象 与EventInfo相结合能提供事件陷井捕捉,VBControlExtender对象对动态添加控件特 别有用,它提供了一套通用的属性、方法、事件给开发人员,它的一个突出特点是能编 程设计控件的事件,熟习类编程的朋友对带事件的对象声明一定不会陌生: ---- Dim WithEvents objElemt as CElemtVBControlExtender也不例外,声明的 语法一样,只不过它有个特殊的事件ObjectEvent(Info As EventInfo),它能捕捉到对 象使用RaiseEvent产生的所有事件,EventInfo数据结构映射了事件的名称、参数个数 和参数的值。VBControlExtender和 EventInfo相结合,采用Select Case 就可以预先 将不同类对象的事件放置一起,各自独立运作。将上面的代码改写一下就能提供Click 事件了: ---- Dim WithEvents oControl As VBControlExtender '带事件声明声明之后您 就可以在代码窗口的左上角的对象下拉框中发现该对象出现了,也就是说,该对象有了 事件或方法了,它的事件有DragDrop,DragOver ,LostFocus ,GotFocus , ObjectEvent和Validate,其中ObjectEvent是通用的事件捕捉。 Private Sub LoadControl() Set oControl = Controls.Add ("VB. CommandButton", "MyButton") oControl.Visible = True End Sub Private Sub oControl_ObjectEvent(Info As EventInfo) Select Case Info.Name Case "Click" 'Click事件 '您可以添加处理Click事件代码 MsgBox "您按了MyButton!" Case Else ' 其他事件 ' Handle unknown events here. End Select End Sub ---- 当然对微软提供的标准控件能采用该方法添加,大家都不会怀疑,但自己开 发的控件也能吗?答案是肯定的,我们可以用一个实际的例子进行说明。 三、实例描述 ---- 假设一个本地网络的监控系统,需要在原理图与实物示意图间切换,原理图 包括组网结构、传输资源、监控主机等,而实物示意图包括路由器、设备、采集器等, 当然两种图的事件要一致,如双击某个设备图形将显示给设备的实时数据等,为了简化 维护,将原理图与实物示意图封装成ActiveX控件,由于每种图需要加载许多图形控 件,消耗资源较大,不能同时加载,需要将其分解为两个控件,在切换时首先卸载一个 控件,然后加载另一个控件,所以要实现动态加载ActiveX控件。 ---- 原理图控件为--Theory.ocx ,对应工程为CTheory; ---- 实物图控件为---Fact.ocx ,对应工程为CFact; ---- 注意:为了简化,在设计控件时不设置许可证关键字。 ---- 实物图控件上的图形对象可以被拖动,拖动后的位置信息通过事件 ChangePosition来通知拥有该控件的窗体,以便下次加载能显示在最后位置,实物 图和原理图控件都有双击事件完成的工作相同,其他事件此处忽略。 ---- 四、具体示例 ---- 1、准备工作 ---- 对控件Theory.ocx 、Fact.ocx 进行注册(利用Regsvr32.exe注册); ---- 建立窗体frmTest.frm ,在窗体上放置按钮cmdLoadOcx—“原理图” ---- 2、声明窗体级变量与加载函数LoadControl Dim WithEvents oControl As VBControlExtender '地图仿真控件对象 Dim mblnTheory As Boolean '是否显示原理图 Private Function LoadControl(intType As Integer) If Not oControl Is Nothing Then '首先判断对象是否存在,若存在则卸载 Controls.Remove("MapView") '卸载控件,此操作非常重要 End If If intType = 0 Then Set oControl = Controls.Add ("CTheory. Theory", "MapView") Else Set oControl = Controls.Add ("CFact.Fact", "MapView") End If oControl.Height = 3500 oControl.Width = 6500 oControl.Top = 100 oControl.Visible = True End Function Private Sub Form_Load() mblnTheory = True End Sub ---- 3、为按钮cmdLoadOcx编写代码 Private Sub cmdLoadOCX_Click() If mblnTheory Then Call LoadControl(0) mblnTheory = False cmdLoadOCX.Caption = "实物图" Else Call LoadControl(1) mblnTheory = True cmdLoadOCX.Caption = "原理图" End If End Sub ---- 4、为事件ChangePosition编写代码 Private Sub oControl_ObjectEvent (Info As EventInfo) Select Case Info.Name Case "ChangePosition" MsgBox CStr(Info.EventParamters.Item( 1).Value) + ":" + _ CStr(Info.EventParamters.Item(2).Value) Case "DbClick" '双击处理代码 Case Else ' End Select End Sub ---- 注意: EventInfo的参数EventParamters集合中是以1开始的,一般来说,微 软新的集合一般是以1开始的,而旧的是以0开始的,如RdoErrors.Item(0)。 ---- 5、关闭窗口前卸载控件 Private Sub Form_Unload(Cancel As Integer) Controls.Remove("MapView") Set oControl = Nothing End Sub ---- 6、特别注意 ---- 通过Controls.Add方法添加的ActiveX控件一定不能在该工程中有该控件的任 何引用,否则系统将出错。 四、小结 ---- 通过使用动态加载ActiveX控件使用庞大的应用程序变得很小,将不同的 ActiveX控件进行各种组合,使应用程序更加灵活多变,如您的应用系统要处理三十种门禁, 而某个具体的用户可能只有一种或两种门禁,根本没必要首先将所有门禁包含到应用 中,可将各个门禁独立封装,只安装注册需要的组件,就象Windows的自定义安装一样。 微软的未来技术基础是分布式的组件技术(DCOM),将会把代码的重用发挥得淋漓尽致。 您不妨试一试动态加载,也许会产生令您惊喜的效果! ---- 本代码在Win98、VB6.0(英文版)上编译、运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值