Rob, The ComputorTutor Does VB.Net 2010
“Custom Controls”
by Rob Spahitz
Last time, we continued exploring Visual Basic with databases. Although there are many things we can do with databases, much of this was already covered as form design issues in my Access tutorials earlier this year. This week we explore the idea of custom controls in VB.
Note: These columns are written using VB.Net 2010. However, most of the features will work just fine with VB.Net 2008 or even earlier versions. To see how to install VB.Net 2010, check my July 31, 2009
column, and to retrieve project files, check my server at
www.dogopoly.com/ce.
My ToolBoxAs you work with object-oriented programming languages, you'll quickly find that GUI (Graphical User Interface) design makes form creation a breeze. You start with a form, drop a few labels, text boxes and buttons on it, do some minor programming, and you have a very functional prototype ready for presentation to a bunch of Capital Investment specialists. Of course, prototypes are merely a visual presentation of what the finished product will look like. Somewhere you need to add additional functionality (like serious calculations, database connectivity, and fancy animations). Along the way, you may realize that the simplicity of adding ToolBox items to a form is a pleasure but also a limitation. To really polish a finished product, you will probably need to create your own tools, either as a collection of existing tools or as entirely custom tools.
Let's start with a very simple idea. Most of you are probably familiar with the ComboBox (often called the DropDown ListBox.) It's called a ComboBox because it is a combination of other controls. Specifically, it combines a TextBox, a ListBox and a Button control into a single control that gives you the functionality of the three pieces inside one unit. If this control did not exist and you wanted to ask a user to specify some text, you would probably present a TextBox. However, if the text could only match a predefined set of choices (like a list of States), you'd have to do a bit of validation (probably against a database table) either during data entry or after the data entry is completed.
For example, to validate during data entry, you could check every letter upon entry and make sure that there is a state that begins with those letters. If you do this, there are several ways you can handle incorrect information. You could ignore invalid keystrokes; you could immediately notify the user with a MessageBox, label, sound or other warning; or you could mark the error so you can tell the user later.
Case in point: Suppose you're trying to validate data entry in a TextBox against one of the 50 United States. Upon entry of "c" you would check the database and see that there are three valid choices (California, Colorado and Connecticut), so you simply move on. If the text is followed by "o" you would find that there are two valid choices, and you'd move on. If this were followed by "m" you would realize that there are no valid choices that start with "com." If you immediately notify the user in a way that forces a response (such as a MessageBox saying "That is not a valid State name"), your user could immediately fix the problem. However, many people make minor mistakes while typing and often immediately notice the mistakes (like pressing an "m" key instead of the adjacent "n" key). By notifying the user about a mistake that was already spotted, your user will become frustrated. Even if it were not immediately spotted, your message would interrupt the user's flow of data entry, causing an annoyance.
Further, if you gave a non-interruptive message (like a beep), the user may just start to get annoyed about these messages. There's a lot of psychology behind this. It's sort of like telling someone "you're wrong," "you're wrong," "you're wrong." Eventually, many people get a complex from this. A better approach is to minimize the number of such messages or, ideally, create a situation where the user cannot make a mistake! If you do validation only once per field (or even only once per screen), users become much more tolerant of their own mistakes and are less likely to blame the developer for making a bad program.
Anyway, an alternate solution to the above problem is to use a different control. Rather than a TextBox that requires entry, you could offer a ListBox that has all the choices already available. Not only will the user never enter an invalid choice (although a "wrong" choice could still be picked), but the user will also see all of the choices up front, reducing the chance of misspelling a choice by mistake or lack of knowledge.
So the ListBox is a much better alternative than a TextBox. Why not use it all the time? Well, two reasons. One is that it takes a lot of space to make it work well. Typically you want to make it large enough to show at least three choices. In the case of 50 states, probably at least five and maybe 10 choices would be better. That means you're taking up the equivalent of about five or 10 TextBoxes worth of space. Sometimes that's not a problem, but typically, you have lots of things going on in a screen and that space is precious.
The other reason is that sometimes you list only a few key choices in the ListBox and offer the user a chance to add additional choices. This could be the case if you offered a list of countries. You'd probably show the 20 or 30 countries that you think will be used most often, but allow for others to be typed; further, with the world changing as it does, there may be new choices that arrive after you deliver your database, and you'd like your users to be able to enter additional choices without your need to send a new database of options every time there's a world change.
What's the solution? Well, a combination of a TextBox and a ListBox would be a better answer than either one separately. It gives the flexibility and compactness of a TextBox while still offering the simplicity and completeness of a ListBox. The next question would be how to design such a combination control to make it work. Obviously, the makers of the ComboBox chose to give you something that looks like a TextBox with a button on the right edge that allows you to show a ListBox until you select a choice, then transfer the choice into the TextBox. It's a rather elegant solution that most of us now take for granted as a useful way to enter data. This is called a conglomerate control, since it uses other controls to get the job done. Without this, you'll have to do all the work yourself.
Labeled TextBoxSo let's go create our own. First, let's work out some specifications. One common problem in many GUI development projects is that you place controls on a form, and then move them around until you get them just right. This is not a problem, except that often collections of controls are designed to work together. Specifically, most TextBoxes have corresponding labels to indicate what the TextBox data should look like. If you saw a screen of five TextBoxes, you'd have no idea what to enter. If you saw adjacent labels like Name, Street, City, State and Zip Code, you'd have a good idea what to enter into the boxes.
As a developer, you'd want to organize your TextBoxes for user-friendliness. For example, maybe place the Name box, then the Street box below it, followed by the other three boxes in order. If you later realized that the spacing between the boxes wasn't quite right, you not only have to move the boxes, but also the corresponding labels. If you like things to look professional, you need to be careful that the alignment of the labels to the boxes is identical on each collection. VB offers some features to do alignment, but that still becomes a tedious task when you have to do it for each label-box combination. Also, if you're careful, you can select multiple controls and move them as a single unit. However, if your mouse slips or you accidentally grab an extra control, you end up having to either re-do or fix things. As developers, we've often accepted this as the "cost of doing business." However, there is a better way. Custom controls!
Let's make a control that combines a Label and TextBox. Open Visual Basic and make a new project called MyControl. What we will do is start with a standard Windows Forms Application and build up on it. That will give us a form where we can place and test the Custom Control. So far this is nothing special.
Next, we need to add a User Control to the project. Go to menu Project/Add User Control, and you should get a dialog box we've seen before. However, instead of something like a new Windows Form, we get the User Control pre-selected, as seen in Figure 1.
Figure 1. Adding a User Control to a Project. Change the default name from UserControl1.vb to LabelTextBox. Note that the extension (.vb) is optional and will automatically get added if omitted.
When you click on the Add button, a new item is added to your project, and you see something that looks like a form without borders, as seen in Figure 2.
Figure 2. Blank User Control.As it turns out, a VB User Control is almost identical to a form in how you set it up. However, how you use it can be quite different. So at this point, we want to add a Label and a TextBox. But before you add them, first you must understand the difference between a User Control and a Form. Although both are containers, a form is usually used to present a nice collection of controls to a user, organized in a way that feels comfortable. Conversely, a User Control is something that should look and act like a ToolBox item.
When you look at items in your toolbox, you notice that they are typically very small when you add them to a form. For example, a default-sized TextBox takes up only 100 pixels across and 20 pixels down. However, the default User Control is 150 by 150. We'll fix that. If we don't, then when we add the User Control to a form, it will be the default size unless the user of the control (initially us) makes it smaller.
Now, when you add other controls to a User Control, they will appear in the same relative location when you put this new control onto a form. That means that if we add a TextBox to the middle of this control, it will be surrounded by space when added to a form. If you look at most Controls in your Toolbox, very few have any extra spacing around them. Further, if they resize, the entire area of the control gets resized. As an example, if you make a ComboBox wider or narrower, the button on the right always stays on the right no matter how wide you make it. We'll need to do the same in our control.
To avoid the "padding" in our control, let's put the pieces as compactly as possible into the space provided. To start, add a Label control to the top-left corner of our User Control and a TextBox to the upper-right corner of the User Control, then drag the left edge of the TextBox so that it starts at the right edge (width) of the label. You should see something like Figure 3. (I selected both controls so you can see about where they are.) In my case, the label was 39 pixels wide and the TextBox started at position 49, so I had to widen it 10 pixels to the left. Either way, make sure that the right edge of the TextBox is on the right edge of the User Control.
Figure 3. Label and TextBox in a User Control.The next thing to do is to change the height of the User Control so that it matches the height of the tallest item (or the item that projects lowest). In this case, the TextBox is 20 pixels high, so I adjusted the size of the User Control to match, as seen in Figure 4.
Figure 4. Resized User Control.Now, because the user may decide to resize our control, we need to also resize ours at the same time. Select the TextBox and change the Anchor property so that it is anchored on all four sides (Top, Bottom, Left, Right). This will allow it to stretch if the User Control is stretched. Likewise, Anchor the label, but don't anchor the right side, since we want it to remain next to the left edge of the TextBox.
Almost done, but first a note. Since the TextBox is now designed to allow you to change only the Height if the Multiline property is True, we should either enable this or lock the User Control so the user cannot change the Height (with the MaximumSize Height property). However, this can automatically change when the user changes the Font or Font Size, so that requires extra effort. For now, I'll ignore this issue and leave it up to the reader to experiment with it.
New ToolBox ItemFinally, our User Control is ready. Oh wait! Don't we need to add code? Well, yes and no. It will actually work as is, but there will be some deficiencies, as we'll see. Adding some code will make it a much more "developer-friendly" control.
One more thing. While we're "developing" a User Control, it is not ready for use on a form and you won't see it in the ToolBox (or at least not the version being worked on). To make it available, we simply switch to the Form and perform a Build from the menus. After doing so, you see a new group in your Toolbox labeled MyControl Components (named after the project that contains the User Control). Inside, you see the control you just made, as seen in Figure 5.
Figure 5. New User Control in Toolbox.Now, drag the User Control (well, really the LabelTextBox we just made) onto the form just like you would any other control. If you widen it or narrow it, the TextBox portion of the control should resize with it. And if you want to move the Label and the TextBox together, it merely requires one click-and-drag motion. Furthermore, at this point, the two pieces are integrated and you cannot use them separately (at least not without going back to the User Control's design area). A sample of several on the form is shown in Figure 6, including one that has the Height changed where the TextBox did not resize because of the issue mentioned above.
Figure 6. Form with several LabelTextBoxes.Try running the form and notice that you can't tell that there is a User Control anywhere on the form, and the TextBoxes are fully functional.
Next week, we'll make a User Control with custom graphics and learn how to let the properties of this control interact with the form.
Rob has been in the computer industry for over 25 years and is currently a part-time teacher, offering classes in Excel, Access, Visual Basic, and a variety of other technical tools. He has loved ComputorEdge
since 1990 and can be contacted at .
Looking for a great boardgame? Grab a copy from DOGOPOLY.com and have a dog-gone great time.