主题:[原创]打造自己的多风格按纽--用户控件制作详解(1)
我相信各位与我一样,经常在网上看到别人发布的多彩多姿的按纽控件,但总有不尽如意的地方,
那么,就让我们自己来制作多风格的按纽控件,一起来体验成功的乐趣吧!
为了便于表述,下面把在用户控件页面中进行的设计活动称为“控件设计”,把在窗体页面中进行
的设计活动称为“程序设计”,把进行程序设计的程序员称为“中间用户”,把最后使用程序的用户称
为“最终用户”。
制作这个按纽控件要达到的目的:形状多样(共有21种形状),背景颜色多样,文本标题显示多样
(运用字符显示特技),能够显示真彩图片。
新建一个工程,添加一个窗体(用于测试用户控件),再添加 ActiveX控件,这个控件就是用户控
件,也就是我们的工作对象。这时在工程管理器中可以看到工程多出了一个类别:UserControls,窗体
在 Forms列表中,而用户控件在 UserControls 列表中。好,我们在用户控件的属性窗口中将它的名称
改一下,就叫 PrettyCmd 吧,再将它的另外几个属性改一下:
①ScaleMode=3,因为我们会在代码中使用 API 函数来改变它的形状,而 API 函数是以“象素”为单
位的;
②BackColor=绿色,你可以改为其它任何颜色,这只是为了以后做试验时能与窗体的背景区别开来;
③AutoRedraw=True
④ScaleWidth=2520
⑤ScaleHeight=1035
其它属性暂时不加理会。
现在可以使用窗体设计器在这个用户控件上面随便放些其它东西,例如图片框或者标签什么的,就
像一个窗体一样,但是注意这个“窗体”实际上它将成为中间用户在程序设计时控件的外观形状,我们
现在是在做按纽控件试验,就不必添加别的控件了。
在 UserControl页面的代码编辑器中你也可以发现,设计一个用户控件时跟设计窗体是很相似的,
只是对象变成用户控件,Form 对象现在变成 UserControl 对象,触发的事件不一样而已。
一、自制多风格按纽的形状外观
自制按纽一个很重要的原因就是我们想获得各种形状,所以首先让我们来设计它的形状。
中间用户在进行程序设计时,常要在控件的某些属性的下拉列表中进行选择,我们要自制的按纽共
有18种形状(实际是21种形状,因为其中的“圆角矩形”可以变化出矩形、圆角矩形、椭圆形、圆形等
四种形状,如何变化以后会讲到),所以也必须能够供中间用户选择,那么,在控件设计阶段时,就要
在 UserControls 页面的代码窗口的 Option Explicit 节中输入以下代码:
Public Enum mState
圆角矩形
左斜下角
左斜上角
右斜下角
右斜上角
上斜左角
上斜右角
下斜左角
下斜右角
两边平行左斜
两边平行右斜
上下平行左斜
上下平行右斜
菱形
上三角形
下三角形
左三角形
右三角形
End Enum
注意:
①如果你设计的控件的其它属性,其属性值不需要用下拉框加以选择的话,就不要在此进行类似定义。
②属性值我这写的都是汉字,以后中间用户打开这个属性的下拉框时看到也是汉字,如果你的英文很过
关,改为英文当然也可以。
③这个属性是公用的,要用 Public 来定义。
这样的定义方法是不是有点象程序设计时的用户自定义数据类型?mState是它的名称。另外,我们
还要声明一个变量 vState 来表示中间用户所选择的属性值。还有,为了打造出这些形状,必须借助于
API 函数,这些 API 函数必须事先声明。
下面在 Option Explicit 节中继续输入:
'创建多边形
Private Declare Function CreatePolygonRgn Lib "gdi32" (lpPoint As POINTAPI, ByVal nCount As Long, ByVal nPolyFillMode As Long) As Long
'创建圆角矩形
Private Declare Function CreateRoundRectRgn Lib "gdi32" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long, ByVal X3 As Long, ByVal Y3 As Long) As Long
'改变窗口的区域
Private Declare Function SetWindowRgn Lib "user32" (ByVal hwnd As Long, ByVal hRgn As Long, ByVal bRedraw As Boolean) As Long
'删除GDI对象
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Type POINTAPI
x As Long
y As Long
End Type
Dim vState As Integer
再输入以下过程代码:
Private Sub DrawButton() '绘制按纽形状
Select Case vState
Case 0 '圆角矩形
DrawRoundCorner
Case 1 To 13 '四边形
DrawParallelogram
Case 14 To 17 '三角形
DrawDiagonal
End Select
End Sub
Private Sub DrawDiagonal() '三角
Dim pt(0 To 2) As POINTAPI, hRgn As Long
Select Case vState
Case 14 '上三角
pt(0).x = UserControl.ScaleWidth / 2: pt(0).y = 0
pt(1).x = UserControl.ScaleWidth: pt(1).y = UserControl.ScaleHeight
pt(2).x = 0: pt(2).y = UserControl.ScaleHeight
Case 15 '下三角
pt(0).x = 0: pt(0).y = 0
pt(1).x = UserControl.ScaleWidth: pt(1).y = 0
pt(2).x = UserControl.ScaleWidth / 2: pt(2).y = UserControl.ScaleHeight
Case 16 '左三角
pt(0).x = 0: pt(0).y = UserControl.ScaleHeight / 2
pt(1).x = UserControl.ScaleWidth: pt(1).y = 0
pt(2).x = UserControl.ScaleWidth: pt(2).y = UserControl.ScaleHeight
Case 17 '右三角
pt(0).x = 0: pt(0).y = 0
pt(1).x = UserControl.ScaleWidth: pt(1).y = UserControl.ScaleHeight / 2
pt(2).x = 0: pt(2).y = UserControl.ScaleHeight
End Select
hRgn = CreatePolygonRgn(pt(0), 3, 1) '创建一个由3个点围成的区域
SetWindowRgn UserControl.hwnd, hRgn, True
DeleteObject hRgn
End Sub
Private Sub DrawRoundCorner() '圆角矩形
Dim hRgn As Long
hRgn = CreateRoundRectRgn(0, 0, UserControl.ScaleWidth, UserControl.ScaleHeight, 10, 10)
SetWindowRgn UserControl.hwnd, hRgn, True
DeleteObject hRgn
End Sub
Private Sub DrawParallelogram() '四边形
Dim pt(0 To 3) As POINTAPI, hRgn As Long
Select Case vState
Case 1 '左下斜
pt(0).x = UserControl.ScaleWidth / 3: pt(0).y = 0
pt(1).x = UserControl.ScaleWidth: pt(1).y = 0
pt(2).x = UserControl.ScaleWidth: pt(2).y = UserControl.ScaleHeight
pt(3).x = 0: pt(3).y = UserControl.ScaleHeight
Case 2 '左上斜
pt(0).x = 0: pt(0).y = 0
pt(1).x = UserControl.ScaleWidth: pt(1).y = 0
pt(2).x = UserControl.ScaleWidth: pt(2).y = UserControl.ScaleHeight
pt(3).x = UserControl.ScaleWidth / 3: pt(3).y = UserControl.ScaleHeight
Case 3 '右下斜
pt(0).x = 0: pt(0).y = 0
pt(1).x = UserControl.ScaleWidth * 2 / 3: pt(1).y = 0
pt(2).x = UserControl.ScaleWidth: pt(2).y = UserControl.ScaleHeight
pt(3).x = 0: pt(3).y = UserControl.ScaleHeight
Case 4 '右上斜
pt(0).x = 0: pt(0).y = 0
pt(1).x = UserControl.ScaleWidth: pt(1).y = 0
pt(2).x = UserControl.ScaleWidth * 2 / 3: pt(2).y = UserControl.ScaleHeight
pt(3).x = 0: pt(3).y = UserControl.ScaleHeight
Case 5 '上左斜
pt(0).x = 0: pt(0).y = 0
pt(1).x = UserControl.ScaleWidth: pt(1).y = UserControl.ScaleHeight / 3
pt(2).x = UserControl.ScaleWidth: pt(2).y = UserControl.ScaleHeight
pt(3).x = 0: pt(3).y = UserControl.ScaleHeight
Case 6 '上右斜
pt(0).x = 0: pt(0).y = UserControl.ScaleHeight / 3
pt(1).x = UserControl.ScaleWidth: pt(1).y = 0
pt(2).x = UserControl.ScaleWidth: pt(2).y = UserControl.ScaleHeight
pt(3).x = 0: pt(3).y = UserControl.ScaleHeight
Case 7 '下左斜
pt(0).x = 0: pt(0).y = 0
pt(1).x = UserControl.ScaleWidth: pt(1).y = 0
pt(2).x = UserControl.ScaleWidth: pt(2).y = UserControl.ScaleHeight * 2 / 3
pt(3).x = 0: pt(3).y = UserControl.ScaleHeight
Case 8 '下右斜
pt(0).x = 0: pt(0).y = 0
pt(1).x = UserControl.ScaleWidth: pt(1).y = 0
pt(2).x = UserControl.ScaleWidth: pt(2).y = UserControl.ScaleHeight
pt(3).x = 0: pt(3).y = UserControl.ScaleHeight * 2 / 3
Case 9 '两边平行左斜
pt(0).x = 0: pt(0).y = UserControl.ScaleHeight / 3
pt(1).x = UserControl.ScaleWidth: pt(1).y = 0
pt(2).x = UserControl.ScaleWidth: pt(2).y = UserControl.ScaleHeight * 2 / 3
pt(3).x = 0: pt(3).y = UserControl.ScaleHeight
Case 10 '两边平行右斜
pt(0).x = 0: pt(0).y = 0
pt(1).x = UserControl.ScaleWidth: pt(1).y = UserControl.ScaleHeight / 3
pt(2).x = UserControl.ScaleWidth: pt(2).y = UserControl.ScaleHeight
pt(3).x = 0: pt(3).y = UserControl.ScaleHeight * 2 / 3
Case 11 '上下平行左斜
pt(0).x = UserControl.ScaleWidth / 3: pt(0).y = 0
pt(1).x = UserControl.ScaleWidth: pt(1).y = 0
pt(2).x = UserControl.ScaleWidth * 2 / 3: pt(2).y = UserControl.ScaleHeight
pt(3).x = 0: pt(3).y = UserControl.ScaleHeight
Case 12 '上下平行右斜
pt(0).x = 0: pt(0).y = 0
pt(1).x = UserControl.ScaleWidth * 2 / 3: pt(1).y = 0
pt(2).x = UserControl.ScaleWidth: pt(2).y = UserControl.ScaleHeight
pt(3).x = UserControl.ScaleWidth / 3: pt(3).y = UserControl.ScaleHeight
Case 13 '菱形
pt(0).x = 0: pt(0).y = UserControl.ScaleHeight / 2
pt(1).x = UserControl.ScaleWidth / 2: pt(1).y = 0
pt(2).x = UserControl.ScaleWidth: pt(2).y = UserControl.ScaleHeight / 2
pt(3).x = UserControl.ScaleWidth / 2: pt(3).y = UserControl.ScaleHeight
End Select
hRgn = CreatePolygonRgn(pt(0), 4, 1) '创建一个由4个点围成的区域
SetWindowRgn UserControl.hwnd, hRgn, True
DeleteObject hRgn
End Sub
为了在程序设计时能够设置和显示这个属性,我们还必须添加有关的代码,也可以通过“工具”菜
单的“添加过程”来添加然后修改。如果通过菜单添加,VB其实是自动为你写几行固定格式的代码,免
去你手工输入的麻烦,我们就通过菜单来添加吧。
选择“工具→添加过程”,跳出一个对话框,然后在单选按纽中选择“属性”,再在“名称”栏中
输入一个名字:State(在窗体页面的相关控件的属性窗口中会显示出这个属性名),点击确定,VB 就
会自动生成几行这样的代码:
Public Property Get state() As Variant
End Property
Public Property Let state(ByVal vNewValue As Variant)
End Property
Get 和 Let 是两个相对的属性过程(注意它们都是公用的),它们都是供中间用户在程序设计阶
段使用的:Get 过程是当中间用户在窗体页面点击窗体上的按纽控件时被激活,显示该属性的值,你可
以在里面写一些适当的运算然后把结果赋给该属性名,就像函数一样;Let 过程是当中间用户在窗体页
面的“属性”窗口修改完按纽控件的属性值后被激活,修改该属性的值,vNewValue 是被赋的值(这个
变量名是可以修改的),你可以把得到的 vNewValue的值按自己的需要作任何用途。如果你手动输入上
面的代码,你要确保 Get 和 Let 后面的属性名应该相同,有关的数据类型也要相同,如果你是通过自
动生成得到代码的,那么要注意修改数据类型。如果你想建一个只读的属性,只要把 Let过程删除就行
了。我们就修改成如下的代码吧:
Public Property Get State() As mState '取得vState的值显示给中间用户
State = vState
End Property
Public Property Let State(ByVal vNewValue As mState) '把中间用户的输入值设置到按纽控件内部
vState = vNewValue
PropertyChanged "state"
DrawButton
End Property
注意在 Let过程中调用了 DrawButton过程,有了这一句,那么中间用户在 PrettyCmd 的 State属
性下拉框中选择了某一种形状后,对应的按纽就会立即在对象窗口中改变为相应的形状。
你还会发现 Let过程中有一句 PropertyChanged "state",PropertyChanged 方法是用户控件特有
的,用来通知属性编辑器:“喂,这里有一个属性值被改变了,麻烦你处理一下!”于是系统会根据需要
记录下这个被改变的属性值,以便进一步处理(通常是保存该值),后面带的参数 state 是属性名。
这样处理之后,控件就能改变属性了。现在你点击 UserControl页面的代码窗口右上角的“×”以
及对象窗口右上角的“×”,退出控件设计,进入程序设计,调出 Form1的对象窗口和工具箱,这时你
会发现工具箱中多了一个名为 PrettyCmd的控件,这就是我们刚才自己制作的按纽控件了。把它画到窗
体上,就可以看到控件的属性窗口中已经有了一个具有下拉框的 state属性。改变这个属性值,窗体上
的按纽也随之改变了形状,哈哈!
今天的实验就到这里吧,明天接着做。最后请运行一次,保存我们的成果。UserControl 是以普通
的文本文件格式存储的,该文件包含 UserControl 以及它的构成控件源代码和属性值。在 VB中这个文
件的扩展名为 .ctl,以后控件中要用到图形元素,VB 会就将它存放在同名的 .ctx 文件中。
附件中是按纽样品图片