Property pages look a lot like forms, and designing them is somewhat similar to designing forms. The way property pages work, however, is quite different from the way forms work.
For example, when the Property Pages dialog box creates an instance of a property page, the Initialize event is the first event the PropertyPage object gets — just as it would be for a form. However, unlike a form, the PropertyPage object doesn’t get a Load event. The key event for PropertyPage objects is the SelectionChanged event.
This topic examines the three things your PropertyPage object must do:
The SelectionChanged event occurs when the property page is displayed, and when the list of currently selected controls changes.
For example, after selecting an instance of your control and opening the Property Pages dialog box, a developer might realize that she needed to change the properties of two instances of your control. By clicking the second instance while holding down the CTRL key, she could add the second instance to the list of selected controls. Each of your property pages would then receive a SelectionChanged event.
Important You should always treat the SelectionChanged event as if your property page is being loaded for the first time. As you’ll see, changing the selection fundamentally changes the state of the property page.
The most important thing you need to do in the SelectionChanged event is to set the values of the controls that display property values for editing. For example, consider the General page for the VirtualVelociraptor control (originally shown in Figure 10.1):
Suppose that the Age property of the VirtualVelociraptor control uses the following public Enum:
Public Enum DinoAge
vvHatchling
vvJuvenile
vvAdult
End Enum
The SelectionChanged event of the property page might look like this:
Private Sub PropertyPage_SelectionChanged()
' Place the value of the DinoName property for the
' first selected control in the txtDinoName text
' box for display and editing.
txtDinoName = SelectedControls(0).DinoName
' Use the value of the Age property of the first
' selected control to select the appropriate
' option button in the Age frame.
optAge(SelectedControls(0).Age).Value = True
' (The code above depends on the fact that the
' elements of the DinoAge Enum have the values
' 0, 1, and 2.)
End Sub
Tip The Property Page Wizard will populate your property page with text box controls and check boxes (for Boolean properties), and generate default code for the SelectionChanged event.
The SelectedControls collection contains all the controls currently selected in the container the developer is working on. The collection may contain several instances of your control; if the property page is shared by more than one control in your control component, the collection may contain controls of multiple types.
Note You don’t need to worry about the collection containing controls other than your own — text boxes, for example — because the Property Pages dialog box only displays those pages that are used by all of the currently selected controls.
For the moment, ignore the possibility that multiple controls might be selected. What the code in the SelectionChanged event shown above is doing is taking the value of each property for the first control in the collection and assigning it to the appropriate control on the property page.
In the case of a single selected control, this places all of the control’s property values in fields where the user can edit them.
Instead of showing the property value of the Age property as a set of option buttons, you could use a drop-down list showing the elements of the enumeration:
The drop-down list takes up less space than the option buttons did (an advantage that grows larger as the number of possible values increases), and it shows the names of the constants that would be used in code.
The following code fragment shows how you might set up such a list.
Private Sub PropertyPage_SelectionChanged()
txtDinoName = SelectedControls(0).DinoName
' Create a drop-down list containing the values and
' names of the Enum elements for the Age
' property, and select the one that corresponds
' to the current value of the Age property.
cboAge.AddItem vvHatchling & " - vvHatchling"
cboAge.AddItem vvJuvenile & " - vvJuvenile"
cboAge.AddItem vvAdult & " - vvAdult"
cboAge.ListIndex = SelectedControls(0).Age
' (The index of each Enum element in the drop-down
' list is the same as the element's value.)
End Sub
Tip While you can choose any editable representation that makes sense for a property, remember that the more space each property takes up, the more tabs you’ll need. Minimizing the number of tabs makes the property pages for your control easier to use. For most enumerations, a drop-down list will make the most efficient use of space.
To determine whether multiple controls are selected, you can test the Count property of the SelectedControls collection to see whether it’s greater than one.
In order to deal with multiple selected control instances, it’s useful to divide the properties of your control into two groups:
One approach you might take in your SelectionChanged event is to disable the edit fields for properties of the second sort whenever multiple controls are selected. In the discussion of the ApplyChanges event, an alternate technique will be shown.
If you have multiple controls in your project, and two such controls share a property page, make sure that you provide error trapping for the code that reads the property values. If the first control selected doesn’t include all of the properties shown on the page, an error will occur when you try to read that property value.
In order to tell Visual Basic that the user has edited one or more properties on a property page, you must set the PropertyPage object’s Changed property to True. Because there’s no way to know which property a user might decide to change, you must do this for every property displayed on the page.
For example, to notify the PropertyPage of changes in the DinoName or Age properties from the previous example, you would use the following code:
Private Sub txtDinoName_Changed()
Changed = True
End
Private Sub cboAge_Change()
Changed = True
End
Note that this is exactly the same as coding PropertyPage.Changed = True
.
Notifying the PropertyPage object that values have changed enables the Apply button on the Property Pages dialog box, and causes the ApplyChanges event to occur when the Apply button is pressed, when the user changes tabs, or when the dialog box is dismissed.
Note You may wish to keep track of which properties have changed, so that you don’t have to write them all out.
The second most important event in a PropertyPage object is the ApplyChanges event. In this event you copy the edited property values back to the currently selected controls.
The ApplyChanges event occurs when the user:
The following code for the ApplyChanges event assumes that the SelectionChanged event was coded using a drop-down list for the Age property, as shown earlier.
Private Sub PropertyPage_ApplyChanges()
Dim vv As VirtualVelociraptor
' Set the DinoName property of the FIRST selected
' control only.
SelectedControls(0).DinoName = txtDinoName
For Each vv In SelectedControls
' Transfer the value currently selected in the
' drop-down list for the DinoAge property to
' all of the selected controls.
vv.DinoAge = cboAge.ListIndex
' (The code above works because the value of
' each of the three elements of the Enum is
' the same as its index number in cboAge.)
Next
End Sub
Because it generally doesn’t make sense to give all of the virtual velociraptors the same name, the DinoName property is applied only to the first selected control. The Age property, on the other hand, is applied to all the selected controls.
Note As the control author, it’s up to you to decide which properties make sense to set for multiple selected controls.
In the case shown above, there’s no chance of error in the ApplyChanges event. The text property is a simple string, and the drop-down list limits user input for the Age property to only those values that are valid.
If your property page allows the user to enter values that may be rejected by the Property Let (or Property Set) procedure, you should use error trapping in the ApplyChanges event. The simplest scheme is to use On Error Resume Next, and test Err.Number after each property that may raise an error.
When an error occurs:
Important Setting Changed = True
performs two functions. First, it re-enables the Apply button. Second, it prevents the Property Pages dialog box from being dismissed if the user clicked OK. This is the only way to prevent the dialog box from closing.