Each and every ActiveX control already has some built in properties. Open the code window in your control and select UserControl from the drop-down window. Notice the various event procedures on the left drop-down box. We will add some code to a few of these now to get our control set up.
1. Please add the following code to the predefined UserControl_Initialize
event procedure:
Private Sub UserControl_Initialize()
editStatus = nowStatic
End Sub
We want to initialize the current editStatus
of our control whenever it is initialized. The initialize event fires whenever our control either goes into design-time or run-time.
2. Now add this piece of code in the UserControl_InitProperties
event:
Private Sub UserControl_InitProperties()
m_recordSource = m_def_recordSource
m_connectionString = m_def_connectionString
End Sub
The InitProperties
event is fired only once - when the ActiveX control is first drawn on the host form. When it is drawn, the default properties for the RecordSource
and ConnectionString
are set to the default values, which we earlier fixed as ""
. At this point, the user of our control has a new instance and can set these properties to open any table desired.
3. Here's the next chunk of code – please add it to UserControl_Resize
event:
Private Sub UserControl_Resize()
Width = UserControl.ScaleX(m_def_Width, vbPixels, vbTwips)
Height = UserControl.ScaleX(m_def_Height, vbPixels, vbTwips)
Set m_form = UserControl.Parent
End Sub
Our control gets resized whenever it is drawn on the host form, or whenever the user attempts to physically resize it by stretching or shrinking the sizing handles. By setting the Width
and Height
properties of our control to our size constants we set up in the general area of the control, this is an exercise in futility for the user. If the user attempts to stretch or shrink our control, it snaps right back to the size we defined. This is also a perfect place to Set
our m_form
object variable to the name of the parent - which is the host of our control. Remember that since this is an object variable, we must use Set
, because we are setting a reference to the object variable. A simple assignment of m_form = UserControl.Parent
will generate our friend, the Run-time error '91':
Remember, when using objects, we must always set a reference to them.
4. Another built-in event procedure is the Terminate
event. When the user closes the form that is hosting our ActiveX control, this event gets fired. It is good practice to Set
the references to both our adoRecordset
and adoConnection
to Nothing
. This ensures that all memory is freed up. In reality, this will occur without us having to explicitly do this, but again, we don't want to rely on the default behavior of VB 6.0. It's much better to do this ourselves.
To achieve this, add the following code to the UserControl_Terminate
event:
Private Sub UserControl_Terminate()
On Error Resume Next
If Not adoRecordset Is Nothing Then
Set adoRecordset = Nothing
End If
If Not adoConnection Is Nothing Then
Set adoConnection = Nothing
End If
Err.Clear
End Sub
The last two built in properties we will add code to are the ReadProperties
and WriteProperties
. These are critical to the operation of our control. When a control is initialized, the ReadProperties
event is fired. This is what initializes our internal control variables. VB takes care of actually storing and retrieving the information for us. We simply must tell VB which properties to read and write, and it does the rest for us.
5. Add the following code to the UserControl_ReadProperties
event:
Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
m_recordSource = PropBag.ReadProperty("RecordSource", _
m_def_recordSource)
m_connectionString = PropBag.ReadProperty _
("ConnectionString", m_def_connectionString)
End Sub
The ReadProperties
event attempts to read the value stored in the name of the property. So in our code we are setting the private variable m_recordSource
. The property is stored with the name "RecordSource"
. If a value has been previously set for this, it is read and assigned to m_recordSource
. If the property has not yet been set, as in a new control that is drawn on a form, the default value is read. So if a value is present, it is retrieved and assigned to our variable, otherwise the default value we defined in the general section is assigned.
The reciprocal event is the WriteProperties
event. Whenever a property gets changed, this event is called and the new value is stored in the PropertyBag
for us. These two events, ReadProperties
and WriteProperties
are responsible for making the properties of our ActiveX control persistent. So when a programmer adds a value for a RecordSource
, it is written to the PropertyBag
. Then when the program is run, the control is destroyed and recreated in the running program. As it gets initialized, the RecordSource
property is read from the property bag and assigned to our internal variable. This is how the magic of property persistence occurs, even though our control is constantly getting destroyed and brought back to life through the cycle of design and run.
6. Add the code below to the UserControl_WriteProperties
event:
Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
Call PropBag.WriteProperty("RecordSource", _
m_recordSource, m_def_recordSource)
Call PropBag.WriteProperty("ConnectionString", _
m_connectionString, m_def_connectionString)
End Sub
The first parameter is the property to be written. This property is in quotation marks. If there is a value for the property, it gets written as the value. Otherwise, the default value gets written. So in the first example, if the user has defined a record source and it is different from the default value, this gets written as the current value. Otherwise, the default value gets written.
For optimization purposes, VB compares both the value of the variable (such as m_recordSource) and the default value (such as m_def_recordSource). If they have the same value, the property does not get written. If they are different, VB knows that the value has been changed. In our example, it means the user entered a RecordSource for our control. In this case, the new value gets written and it is now persistent.
7. Now take a minute and add two final properties, RecordSource
and ConnectionString
. These will be the only two properties that the user must enter to use our control. Choose Tools-Add Procedure from the main VB menu. Ensure the properties are Public so the user can access them from outside of our control:
8. Adding the properties will just add templates to your control. You will have to do some tailoring of both the parameters and return values. Add the following code and please take special care that the parameters and return values are exactly as shown below:
Public Property Get RecordSource() As String
RecordSource = m_recordSource
End Property
Public Property Let RecordSource(ByVal New_RecordSource As String)
m_recordSource = New_RecordSource
PropertyChanged "RecordSource"
End Property
9. Now add a ConnectionString
property to our class. Please add the following code:
Public Property Get ConnectionString() As String
ConnectionString = m_connectionString
End Property
Public Property Let ConnectionString(ByVal New_ConnectionString As String)
m_connectionString = New_ConnectionString
PropertyChanged "ConnectionString"
End Property
The Let
part of the property is fired when the user changes the value. So when the user changes the RecordSource
property, the Let_RecordSource
event gets fired. The new entry is passed in as the parameter New_RecordSource
and is assigned to the internal variable m_recordSource
. However, since this property has changed, we must inform VB so that it gets written to the PropertyBag
. We simply add PropertyChanged "RecordSource"
and it will be taken care of for us. Take care to ensure the name of the property, in this case "RecordSource"
is spelled the same in the Let
, ReadProperties
, and WriteProperties
event procedures. This is the string literal that VB uses to identify this specific property for reading and writing.
OK, now let's add some finesse to our property pages.