这里是《VB6.0 ActiveX 控件开发详解》的第三章,介绍如何布局用户控件设计器的界面,和如何初始化控件。如果你没有看过第一章,建议你先去看看:《VB6.0 ActiveX 控件开发详解 [第二章:布局界面与初始化]》
第一节:属性的设计
废话不多说,前文再续,书接上一回。上回说到,如何对界面进行布置和写动态修改控件大小的代码;这一回,我来介绍一下如何设计属性,这是难点。
启动我们上次做好的工程,我们需要想想,我们这个控件需要些什么属性?既然是图片框架控件,那么我们就需要一个Picture属性,同时,在以前的代码里,没有调整图片框控件大小的代码,不知道你是否有留意。所以,我们就需要2个自定义的属性,来调整图片框的大小,暂且定为PicHeight属性和PicWidth属性。
首先,打开用户控件设计器的代码窗口,选择【工具】菜单下的【添加过程】菜单项,在【名称】文本框里输入“PicHeight”,在【类型】那里选择“属性”,然后单击确定,就会出现如下属性过程:
在默认的情况下,每一个属性都是变体类型的,然而,有很多时候,我们的属性不拘泥于变体类型,而是可以是其他类型。在VB6.0的度量单位中,对象的长(Width)和宽(或说高,Height)使用单精度类型(Single)衡量的,于是我们对上述的过程作出更改,将原来的变体改成单精度类型。修改好后如下图所示:
因为不能在Let过程里对属性进行赋值,所以我们需要先建立一个变量来保存取得得值。(如果到这里不能理解的,就证明你不了解属性过程的运作机制。本文另附一片专门介绍属性过程的文章,请先去看:《VB6.0属性过程详解(Property过程)》,看完第一章就可以回来了。)
VB代码开始:
Dim gPicHeight As Single
Public Property Get PicHeight() As Single
PicHeight = gPicHeight
End Property
Public Property Let PicHeight(ByVal vNewValue As Single)
gPicHeight = vNewValue
End Property
VB代码结束
一般,我们以g加属性名称来命名这个变量,而且必须将这个变量声明成与属性相同的类型。
在上面,我们先将由Let过程取回的新属性值保存到gPicHeight变量中,等到需要取得属性值时,才在Get过程当中由变量将值赋给属性。
到这里,一个属性的基本运作代码已经完成,尽管这个属性没有任何的功能,但他已经具备了一个属性的所有特征,例如:写入检测(Let过程),取回新属性值(Let过程的vNewValue参数),读取属性值(Get过程)。这是设计一个属性必须要的步骤,无论设置什么属性(除了只读和只写的属性,这两种我会在以后说到),统统复制这种格式一定不会错,所以,尽管设计一个控件所编写的代码有50%甚至更多的代码是在属性过程这里,但是属性过程的代码却有80%甚至更多是大同小异的,所以,复制粘贴是很好的方法。
第二节:属性的映射
这一节,主要说一下怎么给属性设计一些功能,上面的属性是用来修改图片框的高度的,我们先来回忆一下,图片框如果要改变它的高度,应该用它的什么属性?这个不用我说,可是,我们又应该在那里添加代码以修改图片框的高度呢,因为有两个属性过程~~。那么,我就得先问你,你是想更改立即生效呢,还是等到下一次这个属性被读取时才生效?说到这里,我想认真看过我那篇附属文章的人已经明白了一半了。
如果要立即生效,就应该加在Let过程里,而要等到下一次属性被读取时才生效,那么就应该加到Get过程里。于是,我们在上述的代码中再加入一句代码,因为我们向我们对图片框的大小的修改能立即显示出来,让我们能立即确定现在的数值是我们需要的,所以,我们在Let过程里添加代码,添加的代码如下:
VB代码开始:
Picture1.Height = vNewValue '调整图片框的高度(vNewValue是新的属性值)
VB代码结束
这样,我们就能在修改属性的瞬间同时修改图片框的高度。然而,这种修改,你知道,客户看得到,但是作为控件的容器却不知道,有不少时候容器自身的属性需要根据你的变化而变化,这时候,我们就需要一种机制来通知容器。其实,这并不麻烦,因为Visual Basic 6.0提供了一个名为PropertyChanged的方法,使到容器可以知道是哪个属性作出了更改,并因而调整自己。所以,我们在Let过程(必须是Let过程,因为无论你的属性修改是否立即生效,属性的值都已经更改)当中加入如下代码:
VB代码开始:
PropertyChanged "PicHeight"
VB代码结束
PropertyChanged方法的语法是
object.PropertyChanged PropertyName
其中object是可以省略的(尽管MSDN没说),而PropertyName是属性名称,字符串。
通知容器某属性值已变更后,容器就可以用对象的新属性值同步自身的属性窗口。如果不通知容器某个属性值已经变更,容器就不知道某个对象的实例是否需要保存(通过产生 WriteProperties 事件)。一般,我们将PropertyChanged方法的调用发到Let过程的最后。
第三节:属性的初始化
很多属性都需要初始化,别提到初始化就以为是很专业的事,其实,说属性的初始化很简单,就是为属性赋一个初值。
在一个控件一次的生命周期中,Initialize事件首先发生,用最先发生,紧接着是ReadProperties事件;如果该控件是首次被创建,在执行了Initialize事件后还会执行InitProperties事件,而这个事件正是给我们用来做初始化的。
我们需要在控件创建的时候给属性PicHeight赋一个值,否则在控件创建好后,PicHeight属性的值依然为空(为空相当于为0),那么,很自然,图片框的高度就变成了0。因此,我们需要在InitProperties事件中加入代码。以PicHeight属性为例,加入的代码如下:
VB代码开始:
PicHeight = 375
VB代码结束
这时候的属性可以看成是一个变量,直接给它赋值就可以了,因为在这个赋值语句执行后,接着的就是这个属性的Let过程被执行,这样我们就达到了为图片框的高度的设定一个初值的目的。
第四节:属性的保存
其实,属性是很容易丢失的。因为,在一个控件的一生中,它被无数次的创建,加载,初始化,使用,卸载,接着重复,就在我们将Visual Basic由设计模式进入运行模式时,我们的控件就已经经历了上述的过程。别以为我们在设计模式时将控件拖入窗口,它就一直在那呆着,其实,在我们按下运行按钮的一瞬间,在窗口上的控件已经执行了Terminate事件并完成卸载,然后在执行Initialize事件被重新加载,就在这一个动作的背后,如果不将属性保存下来,就会让我么在设计模式下设置好的属性值统统丢失。
我们需要使用WriteProperties和ReadProperties事件来保存和读取属性值。在执行Terminate事件之前,还有一个事件被执行,那就是WriteProperties事件,这个事件提供了一个对象,PropBag对象(PropertyBag类的实例)来为我们将属性保存下来。在WriteProperties和ReadProperties事件中都有PropBag对象,但是在不同的是建立我们用这个对象的不同方法来实现不同的功能。
PropBag对象的WriteProperty方法用作保存属性值,应用于WriteProperties事件事件中。而PropBag对象的ReadProperty方法用作读取属性值,应用于ReadProperties事件事件中。他们的语法是:
object.ReadProperty(Dataname[,DefaultValue])
object.WriteProperty(Dataname,Value[,DefaultValue])
这里的Object不能省略,它必须是一个PropBag对象;Dataname是属性的名称,字符串类型(String,就是有双引号);DefaultValue是默认值,如果属性写入/读出的值是空的,就会用这个值写入/读出,这个参数可省略;Value是属性值,一般是属性名称(不带双引号)。同样,以PicHeight属性为例:
VB代码开始:
Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
PropBag.WriteProperty "PicHeight",PicHeight,375
' "PicHeight"是Dataname(属性名称),PicHeight是Value(属性值),请务必区分,一个有双引号,一个没有
' 375是DefaultValue(默认值)。WriteProperty 是一个方法,它没有返回值
End Sub
Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
PicHeight = PropBag.ReadProperty("PicHeight",375)
' 同样的,只是这一次不是写入,而是读出(对属性进行赋值)
' 而ReadProperty是一个函数,它有返回值,而返回值就是它的参数中描述的那个属性,这里是PicHeight 属性的值
End Sub
VB代码结束
然而,需要注意的是,只读与只写的属性都不需要使用这个,因为这些属性大多是每一次都会变化的(如hwnd属性,hdc属性),还有一些就是随用户而定的,只需写入,而不需要读出的属性。
第五节:设置默认属性
在VB中,我们可以很简单地为一个控件设置一个默认的属性,不需要编写任何的代码,只需做一下设置即可。设置也很简单。将鼠标移动到某个属性的属性过程内,选择【工具】菜单的【过程属性】菜单项,你就会再出现的对话框上部见到当前属性的名称:
这里当前的属性就是PicHeight。单击【高级】按钮。在过程标识符里面选择“(缺省)”,如下图所示:
这样我们就将PicHeight属性设定成我们PicFrame控件的默认属性。
然而,像PicHeight这样自定义的属性,客户并不知道其作用,我们就必须告诉用户,因此,我们需要一些描述来解释这个属性的用途。我们在属性窗口和对象浏览器里面都能看到大多数的属性的描述,这些描述不是系统生成的,而是要我们自己输入的。在上面的对话框里,有一个标为【描述】的文本框,在哪里,我们可以输入属性的描述。
输入如下文字:“这个属性能够设置图片框架控件(PicFrame)里的图片框的高度。”后,单击“应用”,再单击“确认”,我们就能在属性窗口和对象浏览器里看到这个描述:
在对象浏览器中,选择UPicFrame库,这是我们控件工程的名字。在PicFrame类中(这个类就是用户控件设计器,每一个控件除了有其自身的图形界面,控件本身的代码构成了一个与控件名称一样的类),我们就能够找到PicHeight属性,然后在下面看到“UPicFrame.PicFrame 的缺省成员”和关于这个属性的描述。
在一般情况下,属性都可以在属性窗口显示,但是,如果某个属性只是在运行模式下才有效(例如hwnd属性,只读,在运行时有效,它返回对象的句柄),那么他就不需要在属性窗口里出现了,我们同样可以在上面的【过程属性】对话框中进行设置。在对话框中部,有一复选框,写着“在属性浏览器中不显示”,选上它,就能让当先属性不在属性窗口中显示了。
第八节:设置Picture属性,Font属性(重点,难点啊)
这个问题嘛,我就不多说了。既然是重点难点,当然是要去找专业点的啦,又是去看我那篇附属文章的时候了:《VB6.0属性过程详解(Property过程)[第二章:属性Set过程的应用(设计如字体属性的具有弹出式对话框的属性)] 》
我们都知道ButtonBox有个Style属性,能够让这个客户选择是否可以显示图片,或是两样都有。同样,我们也可以设计一个,但是我们必须考虑一下,这个属性有三种情况:显示着图片(默认情况);不显示图片,显示文字(跟普通框架一样);两样都有,既有图片,又有文字。也就是只有这三种情况,但是如果用户输入了出这三种情况之外的值呢(假设我们以0,1,2三个数字代表上面的三种情况,而用户却输入了3)?于是我们就需要一种方式来限制用户的输入,这就是枚举属性。铺垫就说完了,还是劳烦转跳一下,去看看对应的附属文章吧:《VB6.0属性过程详解(Property过程)[第三章:枚举属性和OLE_COLOR类型(设计限定输入的属性,如DragMode属性)] 》
到这里,属性方面的内容就基本结束了,的确的重点难点啊。期待下一章,《VB6.0 ActiveX 控件开发详解 [第四章:事件的设计与引发]》
原文链接:https://www.f2er.com/vb/261027.html