Beginning ActiveX - Part 2
Last week I introduced you to the basics of ActiveX controls and some of the theory behind them. Hopefully during the last week you have had a play with them and may even have a few Usercontrols running riot on your hard disk. Well, it's time to get them under control so that we can add some cool features to them.
First, lets have a look at getting a bit more arty with your properties and adding some colour and pictures.
Colours are stored in long-type variables, but you may have noticed that if you declare a property as long, you don't get the smart colour picker provided by VB.
The way to do this is in fact extraordinarily easy: all you need do is declare the property as type OLE_COLOR. And it goes a little something like this:
Public Property Get BackColor() As OLE_COLOR
BackColor = UserControl.BackColor
Public Property Let BackColor(ByVal New_BackColor As OLE_COLOR)
UserControl.BackColor = New_BackColor
If you found that exciting, then you had better take a few deep breaths: it just gets better!
Do you remember back in depths of time (well, last week actually) that I mentioned in the first article that there was a third type of property procedure. It is the Property Set procedure, and it is used instead of the Property Let procedure for object variables. This is because the object variables that will store the object internally within the control will only store a reference to the object, not an actual replica. To differentiate this from an ordinary variable, VB uses the Set procedure. So what, you ask. Well, you may or may not know that both pictures and fonts are stored in objects. You probably will know that they have their own custom dialogs.
To use these dialogs, all we need do is declare the property as type Picture or Font, add the set procedure, then fire away. Just note that you must set the internal variable or, in this example, the internal control's property:
Public Property Get Font() As Font
Set Font = lblText.Font
Public Property Set Font(ByVal New_Font As Font)
Set lblText.Font = New_Font
As you can see, none of this is all that complicated. Let's have a look now at how to make some read-only properties.
The simplest way to prevent the user from writing to a property is to not include any code in the let/set procedure. This provides a way for you to easily feed back information to the developer and end-user.
Although you may not find that particularly useful, you may want to create controls that are read-only at run-time. Run-time is when the control is finally being used within a project, as opposed to design-time, which is when the control is being used in the design environment.
This can be done using the Usercontrol's AmbientProperties object. This object provides a property that returns true when the project is in run-time mode, the UserMode property. You can check this property at the beginning of the let/set procedure and then raise an error if there is a problem:
Public Property Get MultiLine() As Boolean
MultiLine = m_MultiLine
Public Property Let MultiLine(ByVal New_MultiLine As Boolean)
If Ambient.UserMode Then
m_MultiLine = New_MultiLine
This code only allows the property to be set at design-time. If the program tries to set it at run-time, the error 'Property is read-only at run-time' is raised.
The AmbientProperties object provides many more properties relating to the container of the control. These include the ways in which the container suggests the control does things. Although most of these can be ignored, some, such as the UserMode property, may be useful.
Similar to the AmbientProperties object is the Extender object. The Extender object holds properties of the control that are actually controlled by the container of the control rather than by the control itself. VB provides a host of useful properties on top of the ones that it is necessary for the container to provide. Because of this, you must be careful to make sure that the container of your control will always be VB, or that it at least supports the properties that you are accessing.
Enumerations, usually known as enums allow you to make full use to VB's auto-suggest features. They allow you to group constants together, so that VB will automatically suggest and complete when you set the value.
The enum must first be defined in the declarations section. You can manually specify values for each member, as shown for the Right member, or let VB automatically number them. The value assigned is either zero (if it is the first member), or 1 greater than the value of the immediately preceding membername (Phew!-Ed).
Public Enum eDirection
Right = 1
To use this enum, just declare the property as the name of the enum:
Public Property Get Direction() As eDirection
Direction = m_Direction
Public Property Let Direction(ByVal New_Direction As eDirection)
m_Direction = New_Direction
That is all there is to it. Use of this feature often seperates a very good control from a professional looking control. You can use it on the borderstyle and backstyle porperties, as with VB's controls.
We have been using the Usercontrol object all the way through, but we are yet to see some of the cool things that we can do with it. Most of the changes to the Usercontrol will be made at the control's design-time. I will now take you on a whirlwind tour of what some of the more obscure properties do.
The Alignable property, when set to true, automatically creates a new property for the control, the align property. This allows the container to position the control in a similar way to toolbars and it also means that you control can be placed on MDI parent forms.
The CanGetFocus property allows the designer to prevent the control from getting focus at run-time, making it an entirely graphical control. When this is set to false, the only standard controls that you can use are the label, shape, image, line and timer controls.
The ControlContainer property defines whether the control can act as a container of other controls in a similar fashion to frames and picture boxes.
The DefaultCancel property adds the Default and Cancel properties to the control. In the same way that a command button with cancel set will have the click event raised when it detects the escape key being pressed. This allows you to create a quick way out for the user.
You can detect whether you must show that the control is the default control by checking the DisplayAsDefault property of the AmbientProperties object.
The InvisibleAtRuntime property enables you to create controls like the timer control that are not visible on the form at run-time.
Lastly, the ToolboxBitmap property lets you set an icon that appears in the toolbox. Although Microsoft recommends 32x32, I find that 23x23 or 24x24 works better, as 32x32 must be shrunk to fit it in.
Although this seems a bit of a paradox, VB allows you to set properties about each member of the control's interface. To do this open up the Procedure Attributes Dialog by clicking Tools|Procedure Attributes..., and you will be presented by this dialog:
From here, you can set a description that appears in the object browser and the box below the properties window. You can also set a Help Topic ID for the help file specified in the project's help file for this member. Since computers work in numbers, they like to have a procedure ID. This links the procedure with a table of procedure that the container might expect to find. This allows the integration with containers that might not normally be able to access the properties, events and methods properly.
You can also specify which of the default and custom property pages this member uses if it is a property. These are a bit too much for this article, but may be covered in a later one. The Property Category is also only available to properties. This allows you to specify which category the property is put in when properties window is in "Categorized" mode. "Hide this member" allows you to hide the member from the prying eyes of VB's object browser. "Don't show in Property Browser" does a similar thing, just hiding the property from the property browser.
Just because the are hidden, doesn't mean that you can't use them in code. Lastly, "User Interface Default" determines which property is highlighted in the property browser or which event is displayed in the Code window when you double-click the control. Naturally there can only be one default property and one default method.
Similar to the User Interface default is setting the Procedure ID to "(Default)". This allows you to specify a 'value' method and property. These are the property and method that are referred to when you just reference the object. For example, with a label, the caption property has been set to the default 'value' procedure:
Label1.Caption = "Hello"
This is the same as:
Label1 = "Hello"
Another use of the Procedure ID is to add a really professional touch: an AboutBox. By specifying a method for use as the AboutBox member, another item is added in the property browser, called AboutBox. This is followed by the ellipsis (...), and when you click on them, it calls your AboutBox method. For example:
Public Sub AboutBox()
MsgBox "Scrolling Text Demonstration Control" & _
vbNewLine & "From VB-World.net (http://www.vb-world.net)"
Well done! You can now class yourself as a VB-World ActiveX geek (if you want).
Hopefully I have made clear this often confusing topic. Before long, you will be creating
your very own supercool, all singing, all dancing ActiveX controls. If you think that you
are absolutely amazing and that you have created the best ActiveX control that will bring
peace to the world, send it to us, and you may find you name appearing beside it in our
Just as a little taster of what you can do, you can download
a simple control I cobbled together. It is a scrolling label, using only topics covered
in the past two articles.
However, I must admit that I did cheat a bit. I used the ActiveX Control Interface
Wizard. Since we seem to have had lots of body metaphors so far, I will describe it thus:
The AXCIW creates the muscles on the framework of bones; all you have to do is add the
vital organs. It works by mapping members of the constituent controls on the Usercontrol
to the members of the Usercontrol. You can easily add your own code onto the code that it
provides. Have a play with it: it should reduce the time to produce an ActiveX control
I guess that I had better stop there. You are probably getting bored of me, and you
probably can't wait to get on and make your own controls.
Until next time,