回 帖 发 新 帖 刷新版面

主题:[原创]打造自己的多风格按纽--用户控件制作详解(2)

打造自己的多风格按纽--用户控件制作详解(2)

  现在我们点击“运行→启动”来试一下。哎,不对呀,刚才明明在属性窗口中选择了“菱形”或别
的形状,运行后怎么又成了普通的矩形了?别急别急,这是由于控件还不能保存属性值的缘故。再回到
控件设计的 UserControl 页面,在代码窗口还需要添加两个事件的过程代码:

Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
vState = PropBag.ReadProperty("state", 0)
End Sub

Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
PropBag.WriteProperty "state", vState, 0
End Sub

  这两个事件是在程序的设计阶段或运行阶段,需要读取或保存属性值时,由系统调用的。
  ReadProperty 方法读取一个属性值,WriteProperty 方法写入一个属性值,PropBag参数保存了控
件在程序设计时在属性窗口中改变过的属性值。在程序运行时,系统会调用 ReadProperties 过程,将
PropBag 中保存的属性值赋给相应的属性;而 WriteProperties是在工程文件保存时被调用的,这时系
统会把控件的属性值写进属性包保存起来。

注意几点:
1.ReadProperty方法的第一个参数是属性名,它含有我们们所设置的属性值;第二个参数是可选的,它
 表示默认值,当 VB没有在 PropBag 中找到指定属性值的时候,会使用默认值代替,如果省略了默认
 值,在找不到指定属性值的时候就出错了。
2.WriteProperty 方法的第一个参数是属性名,第二个参数是属性值,第三个是默认属性值。
3.这两个方法中的属性名与 Let 和 Get 过程中的属性名必须相同。
4.这两个过程是私有的,以后不论你为控件设计了多少属性,都在这两个过程中读、写;而 Get和 Let
 过程是公用的,且一般是成对出现的,你为控件设计了多少个属性,一般就应该有多少对 Get和 Let
 过程。

  好,经过以上的处理,控件已经能够读、写属性值了,但要在程序运行时使读出来的属性值生效,
则还要添加一个过程:

Private Sub UserControl_Show()
DrawButton
End Sub

  现在再照前述方法退出控件设计进入程序设计(如果进入程序设计,发现对象窗口中的按纽表面有
斜条纹,这种情况下无法设置控件的属性,可以点出对象窗口右上角的“×”,清除窗口后,再次点击
“视图→对象窗口”即可),选择按纽的外观形状,并运行程序,怎么样,按纽已经显示出我们所选择
的形状了吧?
  下面我们再来进行一些改进。
  在工具箱中,用户控件的图标都是千篇一律的,我们有必要让自制的按纽显示一个个性化的图标,
视觉感受要爽多了。回到程序设计的 UserControl页面,在属性窗口找到 ToolboxBitmap,为这个属性
选择一个中意的图片,它就是以后在工具箱中显示的自制按纽控件的图标了,注意图片是 16×15 象素
16色。
  还有,当我们从工具箱中把按纽控件画到窗体上时,按纽所显示的初始形状,是由InitProperties
事件决定的,所以我们在 UserControl 页面的代码窗口再添加以下代码:

Private Sub UserControl_InitProperties()
vState = 0
End Sub

  在这里,将变量 vState 的值设为 0,按纽刚画到窗体上的初始形状是圆角矩形,你可以将这个值
在 0—17 范围内任意设置,那么按纽的初始形状就会以相对应的外观出现了。
  当按纽控件画到窗体上后,中间用户会根据自己的需要来改变它的大小,但我们发现它并不象我们
所希望的那样即时变化大小(但运行时或结束运行后再来看对象窗口,它的大小是按照我们的设置显示
的),怎么办?那就再在 PrettyCmd 的代码窗口添加以下代码:

Private Sub UserControl_Resize()
DrawButton
End Sub

  好了,现在再画一个按纽到窗体上,调整其大小,按纽也马上起了变化,哈哈,所心所欲!
  但是,还有一个不是问题的问题,那就是我们把自制按纽画到窗体后,在属性窗口查看它的属性说
明,发现 State 属性的说明只有简单的“State”五个英文字符,如果我们想对它作进一步的叙述,又
该怎么办呢?让我们再回到 UserControl页面,点击“工具→过程属性”菜单项,这时会跳出一个对话
框,我们在“名称”下拉框中选中“State”,在下面的“描述”框中就可以输入对这个属性的说明文字
了。暂且就输入“返回/设置按纽控件的形状外观。”这几个字符吧,点“确定”。再到窗体页面的对象
窗口选中 PrettyCmd 控件,看看属性窗口下面对 State 属性的说明,呵呵,正是我们刚才输入的那几
个字!
  现在,按纽控件的形状属性是基本设计好了,但这样的按纽只能看不能用(你到窗体的代码窗口上
面的下拉框中找不到 PrettyCmd控件的单击事件),而我们的目的是要使用它(呸,这不是废话吗),
为此,我们还要为它添加事件。
  就象定义属性一样,我们首先要在 UserControl 页面代码窗口的 Option Explicit 节中声明要产
生的事件(注意声明事件也必须是公用的),在这里我们只声明按纽控件的单击事件(当然按纽的事件
不止这一个,但那些事件的定义方法都与此相仿,读者诸君可根据需要自行添加,权当家庭作业吧):
单击“工具→添加过程”,在弹出的对话框名称栏中输入“Click”,在“类型”单选按纽中选择“事
件”,点击“确定”,于是 Option Explicit 节中就多了这么一行:

Public Event Click()

  然后再输入对此事件的处理过程代码(以后你为控件添加的任何事件都必须有类似的代码):

Private Sub UserControl_Click()
RaiseEvent Click '触发Click事件
End Sub

  RaiseEvent的功能是把用户控件或其上的子控件的事件进行转发。上面的代码的意思是:当你单击
用户控件时,VB 就转发出一个单击事件,这个事件发给谁呢?呵呵,当然是发给窗体代码页中的
PrettyCmd_Click()事件啦。
  现在我们可以在窗体的代码窗口添加代码了(到目前为止,我们还没有为窗体写一行代码呢),在
窗体代码窗口上面的下拉框中选择 PrettyCmd 控件的单击事件,在此事件中添加代码如下:

Private Sub PrettyCmd1_Click()
MsgBox "你单击了按纽!"
End Sub

  至此,小功告成!赶快运行一下吧(心情是迫不及待啊)……OK,出现了按纽,在按纽上单击,好
啊,跳出对话框“你单击了按纽!”……登时沐浴在成功的喜悦之中!

二、改进

  自制的按纽已经能够起作用了,当然值得高兴,但是,前面不是说过:圆角矩形能够变化出常规矩
形和椭圆形等四种形状吗,怎么变呢?还有,四边形中的尖角按纽能否让中间用户自己选择斜边的缩进
量呢?当然都可以实现,不过这需要我们再为 PrettyCmd 增加一个属性:IndentValue。
  我们首先在 PrettyCmd 代码窗口的 Option Explicit 节中声明一个变量:

Dim vIndentValue As Integer

  再按照前述的添加属性的办法,添加以下过程代码:

Public Property Get IndentValue() As Integer
IndentValue = vIndentValue
End Property

Public Property Let IndentValue(ByVal vNewValue As Integer)
If vNewValue < 0 Then
  MsgBox "缩进量超出范围!"
Else
  vIndentValue = vNewValue
  PropertyChanged "IndentValue"
  DrawButton
End If
End Property

  你看,与 State属性的代码很类似,只是多了当输入的属性值超出范围时的处理。
  增添对该属性的描述:“返回/设置按纽控件在非三角形的外观时,斜边或者圆弧边的缩进量。”

  再在ReadProperties过程中添加以下代码:

vIndentValue = PropBag.ReadProperty("IndentValue", 0)

  在 WriteProperties 过程中添加:

PropBag.WriteProperty "IndentValue", vIndentValue, 0

  还要修改 DrawRoundCorner 过程:

Private Sub DrawRoundCorner() 
Dim hRgn As Long
UserControl.BorderStyle = IIf(vIndentValue = 0, 1, 0)
hRgn = CreateRoundRectRgn(0, 0, UserControl.ScaleWidth, UserControl.ScaleHeight, vIndentValue, vIndentValue)
SetWindowRgn UserControl.hwnd, hRgn, True
DeleteObject hRgn
End Sub

  注意过程中对 UserControl.BorderStyle 属性进行了设置,其作用是:当中间用户将按纽的形状
设置为直角矩形时,就为这个矩形加一个边框,看起更加具有立体感。

  最后修改 DrawParallelogram 过程,修改后的代码如下:

Private Sub DrawParallelogram() '四边形
Dim pt(0 To 3) As POINTAPI, hRgn As Long, IV_1 As Integer, IV_2 As Integer
IV_1 = IIf(vIndentValue = 0, UserControl.ScaleWidth / 3, vIndentValue)
IV_2 = IIf(vIndentValue = 0, UserControl.ScaleHeight / 3, vIndentValue)

Select Case vState
  Case 1 '左下斜
    pt(0).X = IV_1: 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 = IV_1: pt(3).Y = UserControl.ScaleHeight
  Case 3 '右下斜
    pt(0).X = 0: pt(0).Y = 0
    pt(1).X = IV_1 * 2: 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 = IV_1 * 2: 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 = IV_2
    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 = IV_2
    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 = IV_2 * 2
    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 = IV_2 * 2
  Case 9 '两边平行左斜
    pt(0).X = 0: pt(0).Y = IV_2
    pt(1).X = UserControl.ScaleWidth: pt(1).Y = 0
    pt(2).X = UserControl.ScaleWidth: pt(2).Y = UserControl.ScaleHeight - IV_2
    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 = IV_2
    pt(2).X = UserControl.ScaleWidth: pt(2).Y = UserControl.ScaleHeight
    pt(3).X = 0: pt(3).Y = UserControl.ScaleHeight - IV_2
  Case 11 '上下平行左斜
    pt(0).X = IV_1: pt(0).Y = 0
    pt(1).X = UserControl.ScaleWidth: pt(1).Y = 0
    pt(2).X = UserControl.ScaleWidth - IV_1: 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 - IV_1: pt(1).Y = 0
    pt(2).X = UserControl.ScaleWidth: pt(2).Y = UserControl.ScaleHeight
    pt(3).X = IV_1: 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

  好了,现在进入窗体页面,你就可以设置 IndentValue属性了,观察一下,按纽外形是不是也随之
发生了改变?并且,只要输入适当的属性值,就可以让圆角矩形变为直角矩形、椭圆形或圆形了。



三、按纽的 Enabled 属性的处理

  我们知道,大部份控件都有 Enabled属性,以便让中间用户决定该控件是否响应用户生成事件,那
么,我们自制的这个按纽也应具有这个属性。但是,我们在程序设计的窗体页面对按纽的这个属性却无
法进行设置(总为 True),因此,我们还要为这个属性添加过程代码:

Public Property Get Enabled() As Boolean
Enabled = UserControl.Enabled
End Property

Public Property Let Enabled(ByVal NewEnabled As Boolean)

UserControl.Enabled = NewEnabled
PropertyChanged "Enabled"
End Property

  在 ReadProperties 过程中添加以下代码:

UserControl.Enabled = PropBag.ReadProperty("Enabled", True)

  在 WriteProperties 过程中添加以下代码:

PropBag.WriteProperty "Enabled", UserControl.Enabled, True

  为该属性添加描述:“返回/设置一个值,决定该控件是否响应用户生成事件。”
  现在再在程序设计的窗体页面中将按纽的这个属性设为 False,运行,不再弹出“你单击了按纽”
的对话框,可知这个属性已经起作用了。
  要注意的是:这个属性不需要在 Option Explicit节中声明一个对应的变量,因为它是用户控件的
基本属性。


四、Caption 属性
  如果没有这个属性,那么按纽上面就是光秃秃的,谁知道它是干什么用的?
  Caption 属性不是用户控件的基本属性,所以我们要在 Option Explicit 节中声明一个变量:

Dim vCaption As String

  添加属性过程代码:

Public Property Get Caption() As String
Caption = vCaption
End Property

Public Property Let Caption(ByVal NewCaption As String)
vCaption = NewCaption
PropertyChanged "Caption"
PrintCaption
End Property

  这段代码中调用了一个子过程 PrintCaption,作用是打印出中间用户输入的标题字符,其代码如
下:

Private Sub PrintCaption()
UserControl.CurrentX = (UserControl.ScaleWidth - UserControl.TextWidth(vCaption)) / 2
UserControl.CurrentY = (UserControl.ScaleHeight - UserControl.TextHeight(vCaption)) / 2
UserControl.Print vCaption
End Sub

  那么,在 DrawButton 过程中也应调用它,所以在 DrawButton 过程的最后增加一行代码:

PrintCaption

  再在 ReadProperties 过程中添加以下代码:

vCaption = PropBag.ReadProperty("Caption", Ambient.DisplayName)

  在 WriteProperties 过程中添加以下代码:

PropBag.WriteProperty "Caption", vCaption, Ambient.DisplayName

  在 InitProperties 过程添加以下代码:

vCaption = Ambient.DisplayName

  为 Caption 属性添加描述:“返回/设置按纽控件的标题文本。”
  有了前面的制作经验,这些代码都容易理解了,我也不再详细解说了,只对新出现的 Ambient.Dis
playName 解释一下:Ambient 对象用于给用户控件提供一些环境信息,比如 DisplayName 就是取得该
控件的默认名称,中间用户将按纽控件画到窗体上时,系统自动为 Caption 属性赋值这个默认名称。
  这里我们是将标题文本打印在按纽控件的中央,当然还可以将文本打印到控件的任意位置,但需要
增加指示文本位置的属性,这个问题我们以后再解决。
  今天的实验就到这里吧,欲知进展如何,且看明日分解。

回复列表 (共3个回复)

沙发

沙发

板凳

支持!
解答了一些以前遇到的说起来不大却到处找不到的问题

3 楼

up
[img]http://upload.programfan.com/upfile/200711201208888.rar[/img]

我来回复

您尚未登录,请登录后再回复。点此登录或注册