Another technique that is valuable for creating custom controls is to embed other controls. In a sense, the UserControl does this. However, when a UserControl is used as the base class, by default it only exposes the properties of the UserControl class. Instead, you may want to use a control such as Textbox or Grid as the starting point, but embed a Button in the Textbox or Grid to obtain some new functionality.
The embedding technique relies on the fact that in Windows Forms, all controls can be containers for other controls. Visual Basic developers are familiar with the idea that Panels and GroupBoxes can be containers, but in fact a TextBox or a Grid can also be a container of other controls.
This technique is best presented with an example. The standard ComboBox control does not have a way for the user to reset to a “no selection” state. Once an item is selected, setting to that state requires code that sets the SelectedIndex to -1.
This exercise creates a ComboBox that has a button to reset the selection state back to “no selection.” That enables the user to access that capability directly. Now that you have done several controls in examples, rather than proceed step by step, we’ll just show the code for such a ComboBox and discuss how the code works:
Public Class SpecialComboBox Inherits ComboBox Dim WithEvents btnEmbeddedButton As Button Public Sub New() Me.DropDownStyle = ComboBoxStyle.DropDownList ' Fix up the embedded button. btnEmbeddedButton = New Button btnEmbeddedButton.Width = SystemInformation.VerticalScrollBarWidth btnEmbeddedButton.Top = 0 btnEmbeddedButton.Height = Me.Height - 4 btnEmbeddedButton.BackColor = SystemColors.Control btnEmbeddedButton.FlatStyle = FlatStyle.Popup btnEmbeddedButton.Text = "t" Dim fSpecial As New Font("Wingdings 3", Me.Font.Size - 1) btnEmbeddedButton.Font = fSpecial btnEmbeddedButton.Left = Me.Width - btnEmbeddedButton.Width - _ SystemInformation.VerticalScrollBarWidth Me.Controls.Add(btnEmbeddedButton) btnEmbeddedButton.Anchor = CType(AnchorStyles.Right _ Or AnchorStyles.Top Or AnchorStyles.Bottom, AnchorStyles) btnEmbeddedButton.BringToFront() End Sub Private Sub btnEmbeddedButton_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles btnEmbeddedButton.Click Me.SelectedIndex = -1 Me.Focus End Sub Private Sub BillysComboBox_DropDownStyleChanged(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.DropDownStyleChanged If Me.DropDownStyle <> ComboBoxStyle.DropDownList Then Me.DropDownStyle = ComboBoxStyle.DropDownList Throw New _ InvalidOperationException("DropDownStyle must be DropDownList") End If End Sub End Class
As in the first example in the chapter, this example inherits from a built-in control. Thus, it immediately gets all the capabilities of the standard ComboBox. All you need to add is the capability to reset the selected state.
To do that, you need a button for the user to press. The class declares the button as a private object named btnEmbeddedButton. Then, in the constructor for the class, the button is instantiated, and its properties are set as necessary. The size and position of the button need to be calculated. This is done using the size of the ComboBox and a special system parameter called SystemInformation.VerticalScrollBarWidth. This parameter is chosen because it is also used to calculate the size of the button used to drop down a combo box. Thus, your new embedded button will be the same width as the button that the regular ComboBox displays for dropping down the list.
Of course, you need to display something in the new button to indicate its purpose. For simplicity, the preceding code displays a lowercase “t” using the WingDings 3 font (which all Windows systems should have installed). This causes a left-pointing triangle to appear, as shown in Figure 16-9, which is a screen shot of the control in use.
Figure 16-9
The button is then added to the Controls collection of the ComboBox. You may be surprised to learn that a ComboBox even has a Controls collection for embedded controls, but all controls in Windows Forms have one.
Finally, the Anchor property of the new button is set to maintain the position if the SpecialComboBox is resized by its consumer.
Besides the constructor, only a couple of small routines are needed. The click event for the button must be handled, and in it the SelectedIndex must be set to -1. In addition, because this functionality is only for combo boxes with a style of DropDownList, the DropDownStyleChanged event of the ComboBox must be trapped, and the style prevented from being set to anything else.