vb动态创建控件

熟悉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),将会把代码的重用发挥得淋漓尽致。您不妨试一试动态加载,也许会产生令您惊喜的效果!   


===============================================================================
最简单的方法:控件数组。
  在VB编程中有一个控件数组的概念, 通过置控件的INDEX 索引值的控制, 不仅可以建立一组具有紧密联系的相同控件, 而且可以实现在程序运行状态中增加控件数目功能, 这在实际编程中应用的非常广泛;在编程中实现具体增加控件数组如下代码即可,请先在窗体上添加一个option控件,名称为option1,并将index属性设为0.

Private Sub Form_ load()

   Dim i As Integer

      For i = 1 To 5

          Load Option1(i) ' 给数组添加 5 个选项按钮。

          Option1(i).Top = Option1(i - 1).Top + 350’设置顶端位置

          Option1(i).left= Option1(i - 1).left+800

          Option1(i).Visible = True’显示增加的控件

          Option1(i).caption = i

      Next i

End Sub

Private Sub Option1_Click(Index As Integer)

MsgBox ("您点击了index为" & Index & "的option控件.")

End Sub

(二)通过Add方法实现
  Add方法在Controls集合中添加一个控件并返回一个对控件的引用。Add方法的语法为:

  Object.Add(ProgID,Name,Container)

  其中Object为要添加元素的集合,ProgID为标示的字符串.可通过对象浏览器来确定,例如,CommandButton控件的ProgID是VB.CommandButton. Name是控件的名称. Container是包含添加控件的容器,可以为form或Frame控件等等。

        Option Explicit

    '通过使用WithEvents关键字声明一个对象变量为新的命令按钮

        Private WithEvents NewButton As CommandButton

        '增加控件

        Private Sub Command1_Click()

        If NewButton Is Nothing Then

        '增加新的按钮cmdNew

        Set NewButton = Controls.Add("VB.CommandButton", "cmdNew", Me)

        '确定新增按钮cmdNew的位置

        NewButton.Move Command1.Left + Command1.Width + 240, Command1.Top

        NewButton.Caption = "新增的按钮"

        NewButton.Visible = True

        End If

        End Sub

        '新增控件的单击事件

Private Sub NewButton_Click()

        MsgBox "您选中的是动态增加的按钮!"

End Sub

(三)借助 VBControlExtender实现.
  如果增加的控件没有添加在”工具箱”中,必须把控件的License关键字添加到License集合中,并且需要声明它为 VBControlExtender对象,并且向Add方法引用上设置该对象变量。然后,利用VBControlExtender事件来编程该控件的事件。请在窗体上添加一个名词为 text1的控件。

 Option Explicit

Dim WithEvents ctlDynamic As VBControlExtender

Private Sub Form_Load()

Dim I as integer

Licenses.Add "MSComctlLib.TreeCtrl"

Set ctlDynamic = Controls.Add("MSComctlLib.TreeCtrl", "myctl", Form1)

' 设置控制的位置和大小

ctlDynamic.Move 1, 1, 2500, 3500

' 添加一些节点到 TreeView控件

For i = 1 To 10

ctlDynamic.object.nodes.Add Key:="Test" & Str(i), Text:="Test" & Str(i)

ctlDynamic.object.nodes.Add Relative:="Test" _

& Str(i), Relationship:=4, Text:="TestChild" & Str(i)

 Next i

' 显示控件

ctlDynamic.Visible = True

End Sub

Private Sub ctlDynamic_ObjectEvent(Info As EventInfo)

' 测试 TreeView控件的 Click事件

select  case Info.Name

case "Click"

Text1.Text = "你点击了  " & ctlDynamic.object.selecteditem.Text

case else

‘添加其他事件

End select

End Sub

(四)控件删除
  删除控件使用Remove方法,但只能删除那些用Add方法添加的控件.对于(二)中添加的控件NewButton,可以用如下代码删除

Private Sub Command2_Click()

If NewButton Is Nothing Then

Else

Controls.Remove NewButton

Set NewButton = Nothing

End If

End Sub

以上程序代码在vb6.0中文企业版中运行通过。

posted @ 2012-12-11 21:53  梦在旅途  阅读(1536)  评论(0编辑  收藏  举报