Pass It On
Most of the XEditor properties and methods are simply the methods and properties of the internal txt control. It’s a simple (but tedious) matter to initialize and write all these properties. Let’s look at a simple one:
Property Get BackColor() As OLE_COLOR
BackColor = txt.BackColor
End Property
Property Let BackColor(ByVal clrBackColor As OLE_COLOR)
txt.BackColor = clrBackColor
PropertyChanged "BackColor"
End Property
This in itself isn’t enough to make the BackColor property work properly. You have to save the property value in the UserControl_WriteProperties event so that it will be saved as part of its containing form:
PropBag.WriteProperty "BackColor", .BackColor, vbWindowBackground
You also have to read the property from its saved state on the form in the UserControl_ReadProperties event:
.BackColor = PropBag.ReadProperty("BackColor", vbWindowBackground)
That’s easy. Now you just have to pass through all the other standard properties and methods of the RichTextBox control. This isn’t my idea of fun, but fortunately you have a wizard to handle the details. The sidebar “Wizard or Secretary?” on the following page details my philosophy and practice with regard to wizards.
Wizard or Secretary?
Wizard. The name raises certain expectations. In fact, you’ll find that most wizards work more like secretaries. They’ll do your repetitive typing for you, but only if you give them very explicit instructions. If you give them bad instructions, they can type a lot of bad code fast and it will take you many hours to undo the damage. Programmers who have used the Wizards in Visual C++ or the Experts in Borland C++ know what I’m talking about. Visual Basic programmers didn’t have to worry about wizards in previous versions because the language made it easy to design forms and dialog boxes that would have been created by wizards in less friendly languages. But Visual Basic version 5 has lots of wizards for creating all sorts of things—including controls.
I don’t want to say that wizards are a bad idea. I’m in the wizard business myself (see Bug Wizard, Global Wizard, and Collection Wizard) and I’m sympathetic to many of the problems that wizard designers encounter. But I prefer language features to wizards. My wizards, for example, work around problems that the language ought to handle, such as assertions, global classes, and collections. I’d like to see a language statement that could simply reuse an existing control.
(I don’t care if it works through inheritance or delegation.) You would write only the code to modify or extend the internal control—no wizard needed. I haven’t seen a language that can do this. Perhaps I’m asking too much.
In the meantime, there’s the ActiveX Control Interface Wizard. All I can say about this wizard is that I don’t trust it (or any other wizard, including the ones I write). It can help you create bad controls very quickly and very easily. It can help you create good controls less quickly and less easily.
In my experience, the most important part of using a wizard is to get all your members in a row before you start. If you don’t know exactly what properties and methods you’re going to delegate, you’ll probably find the wizard’s suggestions incomplete. The Object Browser is a useful way to get the complete list of members that you might want to delegate from the internal control as well as from UserControl.
The second thing about wizards is that you should always check their work. Very few wizards are polite enough to let you choose the coding conventions. That’s because it takes twice as much work to write configurable wizards. It’s much easier to force users to accept your preferences. I plead guilty here for my own wizards, but at least you get the source code to mine. It’s also not unknown for wizards to generate code that is inefficient or just plain wrong. If you’re as picky about your code as I am about mine, you’ll want to have the last word. I had the last word on my control code, and in most cases, you can’t tell whether I used a wizard or not.
Passing properties through appears to be simple, but there are as many exceptions as there are rules. Here are a few of the special cases:
-
Read-only properties need a Property Get procedure, but not a Property Let or Set procedure. Some people prefer to define the Property Let and have it raise its own error, but I’m satisfied with the default
error. The hWnd property is an example.
-
Properties that are objects require a Property Set rather than Property Let procedure. Font and MouseIcon are examples.
-
Design-time read-only properties such as Appearance and ScrollBars can’t be passed through. Although your XEditor control has a design time and a run time, the RichTextBox control to which you are delegating never has a design time that can be addressed with code.
You can set the ScrollBars property of the internal txt control on the UserControl designer, but when you try to change the setting in UserControl_ReadProperties, it’s too late—the delegated control is in run time. You’re hosed—unless you want to do tricks with the window style bits. The technique is similar to the one discussed in Chapter 6. You can check out the details in the ScrollBars property on EDITOR.CTL.
-
Some properties make sense only at run time. You select these in the Procedure Attributes dialog box and check Don’t show in Property Browser. No code required.
-
Extender properties such as Left and Top are provided by the host. You don’t know where your control is unless the host (through the
Extender object) tells you. Fortunately, you don’t have to do anything to get these properties.
-
The UserControl provides properties such as Width and Height. You have to adjust the delegated RichTextBox control to be the same size as the UserControl in the UserControl_Resize event.
Delegating properties can get messy, but methods are easy. Most are one-liners:
Public Sub Refresh()
txt.Refresh
End Sub
Passing properties through is an annoyance to get past. The interesting part is enhancing and extending properties.