用VB制做图型菜单控件
 
ocx控件对于开发过Windows应用的朋友来说一定不会陌生,它以外部component的形式对程序提供强大的支持,如VB工具栏中的picturebox、label、image等都是ocx控件。然而要开发自己的ocx却有一定难度,以前程序员大都用cdk (control develop kit)来设计,但是这需要掌握相当的c语言技巧,其实VB从5.0开始就支持ocx控件开发,我们可从VB的samples中找到一些例子,然而这些例子比较抽象,不好理解,下面请跟我一起来做个十分实用的ocx控件,你只要懂一些VB编程基础就可以了!
大家对Windows的菜单非常熟悉,这种统一格式的灰色菜单有时并不能满足一些应用程序的要求,特别是那些对界面要求比较高的多媒体设计。问题是,VC、VB中提供的菜单编辑器并不支持图形菜单功能!不要着急,我们马上自己来开发一个!创建 menus工程首先打开VB5.0,在新建工程中选择Activex 控件, 这时设计窗口出现一个类似form表单的界面 这就是你的ocx设计器,缺省名为UserControl1。
1. 在"工程"菜单上单击"Project1 属性",打开"工程属性"对话框。选择"通用"选项卡,将工程名称改为menus
2. 在UserControl1 的"属性"窗口中点击"名称" 属性并且把用户控件名改为menuControl
你可能注意到, menuControl的"属性"窗口和标准的VB的form"属性"窗口既类似又有不同。记住,现在我们设计的是ocx控件,而不是VB中的form!添加 Test 工程
由于 .ocx 工程不能独立运行,所以我们将添加一个Test工程。在同一工程组中装入设计工程(menus工程)和测试工程(Test工程)将十分有利于调试运行中的部件。
1.在"文件"菜单上单击"添加工程"以打开"添加工程"对话框。
    注意: 不要单击"打开工程" 或"新建工程",因为这样会关闭控件工程。
2.双击"Standard EXE"图标以添加普通的 .exe 工程(即我们的Test工程)。 
    该新工程立即成为这个工程组的"启动"工程。"工程资源管理器"窗口以黑体名字的显示来标识"启动"工程。
注意: ActiveX 控件工程是不能作为启动工程的。
3. 在"文件"菜单上单击"保存工程组"用以保存测试工程和工程组。
& ocx代码设计 &
1. 在menus工程的module模块中增加如下代码:
Type menuRect '创建用户自定义类型
left As Integer 
top As Integer
right As Integer
bottom As Integer
End Type
2. 在控件设计窗口中加入一个picturebox控件,取名为bmpmenu。
在bmpmenu中加入另一个picturebox控件,取名为menuitem。
注意: menuitem是bmpmenu的子控件。并且,这两个picturebox的
AutoSize属性皆设为True,BorderStyle属性皆设为None。
3. 在控件设计窗口中的General->Declarations中增加如下代码:
Public g_selectedItem As String 
Dim g_fileName As String '菜单文件名
Dim g_height As Integer 'ocx控件的高度
Dim g_width As Integer 'ocx控件的宽度
Dim bmpRects() As menuRect 'bmpRects()用来记录所有菜单项的位置
Dim currentItemNumbers As Integer
Dim currentItemNumber As Integer
Public Event MENUITEMDOWN()
注意: MENUITEMDOWN是我们添加到控件中的一个事件,它的详细作用我们稍后会具体介绍。
4. 为我们的菜单控件添加两个属性: childs 和 BmpName
childs属性表示菜单控件的菜单项数目, BmpName属性表示菜单控件的菜单文件名
首先,打开menuControl的代码窗口。在"工具"菜单上点击"添加过程"打开"添加过程"对话框。在"名称"框输入名字childs。单击"属性"和"公共的"后再单击"确定"。
你将看到如下的代码:
Public Property Get childs() As Variant
End PropertyPublic Property Let del1(ByVal vNewValue As Variant)
End Property
然后,在代码窗口更改我们新创建的属性过程(修改的部份以黑体表示):
Public Property Get childs() As Integer
childs = currentItemNumbers
End PropertyPublic Property Let childs(ByVal vNewValue As Integer)
currentItemNumbers = vNewValue
PropertyChanged "childs"
PopAndResize ' 这是一个子过程
End Property
当你对childs属性赋新值时均执行 Property Let 过程, 而当你对childs属性检索该属性值时均执行 Property Get 过程。对BmpName属性也同样处理,它的代码部份如下:
Public Property Get BmpName() As String
BmpName = g_fileName
End PropertyPublic Property Let BmpName(ByVal vNewValue As String)
g_fileName = vNewValue
PropertyChanged "BmpName"
PopAndResize 
End Property
5. 为了初始化ocx控件,在 UserControl_InitProperties 事件过程中
添加如下代码:
Private Sub UserControl_InitProperties()
BmpName = ""
childs = 0
End Sub
在UserControl_Resize 事件过程中添加如下代码:
Private Sub UserControl_Resize()
If g_height < 20 And g_width < 20 Then '决定设计状态时的控件大小 
UserControl.Height = 300
UserControl.Width = 300
Else
UserControl.Height = g_height '决定运行状态时的控件大小
UserControl.Width = g_width
End If
End Sub
注意: ocx控件设计涉及到许多重要概念,大家必须亲自实践才能真正融会贯通,
下面简单地介绍一下ocx的事件过程设计。
与一般的可编程对象不同,ocx控件同时具有设计时和运行时的两种行为。 即开发者在设计时把一个控件放入窗体时,控件中的一些代码将会执行。
例如,把我们设计的menu控件放入test工程的窗体时, 放置在 UserControl_Resize 事件过程中的代码将会执行;当test工程进入运行状态时, UserControl_Resize事件过程中的代码也会执行。所以,我在UserControl_Resize过程中使用If...Else语句来决定menu控件分别在设计状态和运行状态时的尺寸大小。
6. 好了,我们的ocx控件马上就要完工了,再坚持一下!(ocx确实有点难度,不过它可是代表最新的技术设计概念哦!)
把下面的sub过程加入ocx设计器的代码部分:
Sub popbmpMenu(xposition As Integer, yposition As Integer, itemNumbers As_ Integer, fileName As String)
ReDim bmpRects(itemNumbers)
bmpmenu.Visible = False
g_fileName = fileName
bmpmenu.Picture = LoadPicture(g_fileName + ".bmp")menuitem.Visible = False
currentItemNumbers = itemNumbers
itemHeight = Int(bmpmenu.Height / itemNumbers)
For i = 0 To itemNumbers - 1
bmpRects(i).left = xposition
bmpRects(i).right = bmpmenu.Width
If i = 0 Then
bmpRects(i).top = yposition
Else
bmpRects(i).top = bmpRects(i - 1).bottom
End If
bmpRects(i).bottom = itemHeight * (i + 1)
Next i
bmpmenu.ZOrder 0
bmpmenu.left = 0
bmpmenu.top = 0
bmpmenu.Visible = True
g_height = bmpmenu.Height ' 菜单位图文件决定了ocx控件的大小
g_width = bmpmenu.Width
End Sub
Sub PopAndResize()
menuitem.Visible = False
If childs > 0 And BmpName <> "" Then
Call popbmpMenu(0, 0, childs, BmpName)
End If
UserControl.Height = g_height ' 调整ocx控件的大小
UserControl.Width = g_width
End SubPrivate Sub GetSelectedItem() '判断哪个菜单项被选中
g_selectedItem = right(g_fileName, 2) + "-" + LTrim(Str(currentItemNumber))
End Sub最后是两个picturebox上的一些代码:
Private Sub bmpmenu_MouseMove(Button As Integer, Shift As Integer, X As Single, _
Y As Single)
menuitem.Visible = False
For i = 0 To currentItemNumbers - 1
If X > bmpRects(i).left And Y > bmpRects(i).top And X < bmpRects(i).right _
And Y < bmpRects(i).bottom Then
currentItemNumber = 0
menuitem.Picture = LoadPicture(g_fileName + "-" + LTrim(Str(i + 1)) + ".bmp")
currentItemNumber = i + 1
menuitem.Move bmpRects(i).left , bmpRects(i).top 
menuitem.Visible = True
Exit Sub
End If
Next i 
End SubPrivate Sub menuitem_MouseDown(Button As Integer, Shift As Integer, X As Single, _
Y As Single)
GetSelectedItem
menuitem.Visible = False
'启动MENUITEMDOWN事件,此事件的执行代码不写在ocx
'中,而是写在调用此ocx的应用中。
RaiseEvent MENUITEMDOWN
End Sub& 测试ocx控件 &OK, 我们的ocx终于完成了!下面我们可以来测试一下了!
制作一张菜单bmp文件,它的名字为c2.bmp,共有5个菜单项。
制作5个菜单项的激活状态位图,取名为c2-1.bmp,c2-2.bmp,c2-3.bmp,c2-4.bmp,c2-5.bmp
将这些位图文件存放在和工程组同层的文件夹中。
关闭ocx设计器窗口,打开ocxTest窗口,我们发现在ToolBox工具栏中多了一个新的控件,这就是我们所设计的menuControl。在ocxTest窗口上放置一个menuControl控件,然后在代码窗口中填入以下程序:
Private Sub Form_Click()
menuControl1.BmpName = App.Path + "\c2"
menuControl1.childs = 5
menuControl1.Visible = True
End SubPrivate Sub Form_Load()
menuControl1.Visible = False
End SubPrivate Sub menuControl1_MENUITEMDOWN()
menuControl1.Visible = False
Dim selectMenu As Integer
selectMenu = Val(Trim(Mid(menuControl1.g_selectedItem, 4, 2)))
Select Case selectMenu
Case 1
'第一个菜单项被选中
Case 2
'第二个菜单项被选中
Case 3
'第三个菜单项被选中
Case 4
'第四个菜单项被选中
Case 5
'第五个菜单项被选中End Select
End Sub怎么样?调用够简单的吧!这正是微软提出的组件设计的优势。不错,ocx设计的确有些难度,然而一旦设计好,它的调用是十分方便的。ocx的可重用率和易维护性是值得我们在设计时多花些时间的,更重要的是,在软件设计越来越复杂的今天,采用正确的组件设计思想是尤为关键的!
& 编译ocx &如果一切顺利的话,我们就可以编译ocx了。
1. 在"工程资源管理器

解决方案 »

  1.   

    利用VB自制OCX控件 [ 作者: 不详   添加时间: 2001-11-7 17:16:57 ]
     来源:www.vbeden.com  如今OCX控件在编程中已占领了很重要的地位,我们可以利用OCX控件完成一些相当复杂的编程操作.同时OCX 控件还有利于主程序的简单化、功能的重用、隐常程序实现细节、便于升级、传播方便等优点。现在我们可以利用VB 5.0方便的制作出自己的OCX控件供我们在编程中使用同时还可以把它送给你周围喜欢编程的朋友! 下面列出制作OCX控件的步骤:
    一:新建OCX   打开VB 5.0选择新建工程在对话框中选择ActiveX 控件(如图一(map1.gif))打开后会见一空的文档这就是 OCX控件的初始界面。想看一看空OCX控件的效果吗?选择“添加工程”选中标准EXE,这时你就可以像调用其它控件一样在左边的工具栏里选择刚才新建的OCX控件图标放在标准的EXE文档中看一看有什么效果!(什么也没有!) 自然因为刚才的OCX文档是空的嘛(废话太多,数个痰盂向我飞来)!
    二:创建界面
      一般我们用VB创建OCX控件都是在我们的控件里添加其它的控件来组合成一个完整的控件(也可以让它只完成某种算法)比如:你可以在上面添加一按钮、编辑框这时你再用第一部的方法看一看效果,是不是控件上多了一个按钮和一个编辑框。
    三:OCX属性
      一个OCX控件有许多的属性,比如控件背景是否透明(BackStyle),控件是否可以获得焦点 (CanGetFocus)等。这些属性都可以在控件的“属性框”中找到。
    四:添加事件
      一个控件有很多事件如:Click、MouseDown、MouseUp、MouseMove等。要触发这些事件都需要你加入代码。在控件的声明处加入Public Event Click()就表明该控件有一“Click”事件。自己编写的控件有什么事件就在声明处加几条事件。关于触发事件是使用“RaiseEvent”语句来完成的如:RaiseEvent 事件名(参数)。 五:用户属性
      一个控件应有许多属性供用户设置如:控件的背景色、控件要显示的图形等。它们通常用Property Get和 Property Let两条语句来完成。前者表示给用户显示一个属性的值,后者表示用户设置一个属性的值。 六:保存属性和读取属性
      当属性被用户更改后需要将该属性值保存,以便控件运行时读取更改后的属性值。它们分别用 ReadProperty和WriteProperty两种方法来完成。前者表示读取一个属性值,后者表示写入一个属性值。   好了一个简单的OCX控件制作方法大概就需要以上几步就可完成。下面本人将编写一个简单的OCX控件供大家参考。此控件的功能是在控件中显示一个圆,当鼠标移到控件上的时候控件上的圆便会在鼠标不离开控件的前提下跟随鼠标移动。   新建一OCX控件,将控件的BorderStyle属性改为1,再加入一SHAPE控件将其形状改为Circle(如图二(map2.gif)) 添加以下代码: Public Event Click() '定义该控件要产生的事件
    Dim CircleX As Integer, CircleY As Integer Private Sub UserControl_Click()
      RaiseEvent Click '触发Click事件
    End Sub Private Sub UserControl_Initialize()
      CircleX = Shape1.Width / 2
      CircleY = Shape1.Height / 2
    End Sub Private Sub UserControl_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
      Shape1.BackColor = RGB(0, 0, 255)
    End Sub Private Sub UserControl_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    Dim MoveX As Integer, MoveY As Integer
      MoveX = X - Shape1.Width / 2
      MoveY = Y - Shape1.Height / 2
      If (MoveX < 0) Or (MoveX + Shape1.Width > UserControl.ScaleWidth) Or _
        (MoveY < 0) Or (MoveY + Shape1.Height > UserControl.ScaleHeight) Then Exit Sub
      Shape1.Move MoveX, MoveY
    End Sub Private Sub UserControl_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
      Shape1.BackColor = RGB(255, 0, 0)
    End Sub Property Get PosX() As Integer '取得CircleX的值显示给用户
      PosX = CircleX
    End Property Property Let PosX(ByVal New_X As Integer) '把用户写入的值设置到OCX控件内部
      If (New_X < Shape1.Width / 2) Or _
        (New_X > UserControl.ScaleWidth - Shape1.Width / 2) Then
        MsgBox ("圆的X值超出界限了")
      Else
        CircleX = New_X
        Call UserControl_Resize
      End If
    End Property Property Get PosY() As Integer
      PosY = CircleY
    End Property Property Let PosY(ByVal New_Y As Integer)
      If (New_Y < Shape1.Height / 2) Or _
        (New_Y > UserControl.ScaleHeight - Shape1.Height / 2) Then
        MsgBox ("圆的Y值超出界限了")
      Else
        CircleY = New_Y
        Call UserControl_Resize
      End If
    End Property Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
      CircleX = PropBag.ReadProperty("CircleX", Shape1.Width / 2) '将用户设置的值读出来
      CircleY = PropBag.ReadProperty("CircleY", Shape1.Height / 2) '同上
      Call UserControl_Resize
    End Sub Private Sub UserControl_Resize()
      Shape1.Move CircleX, CircleY
    End Sub Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
      Call PropBag.WriteProperty("CircleX", CircleX, Shape1.Width / 2) '将用户设置的值保存
      Call PropBag.WriteProperty("CircleY", CircleY, Shape1.Height / 2) '同上
    End Sub   麻雀虽小,五脏俱全。这个OCX控件完成的任务虽然简单,但是OCX控件的基本操作全都有喔! 有兴趣的朋友不妨一试。