Creating Custom Controls
What is the difference between a UserControl and a custom control? Not much. The UserControl jumpstarts creating custom controls because it provides you with a form-like canvas to paint controls on. However, if you want to extend the behavior of an existing control, rather than starting from scratch, you want to implement a custom control.
The most significant difference is that when you define a custom control, you will modify the Inherits statement to inherit from a specific existing control. When you inherit from an existing control, you get all of the behaviors and attributes of that existing control and anything else you add. The section "Creating a Custom Component" in Chapter 12, "Defining Attributes," demonstrates creating a custom label.
In other respects, creating a custom component is identical to creating a UserControl, except for having the UserControl designer to draw components on.
Several additional factors apply to controls in general. You do not need to implement a class library or Windows control library to create a custom control. You can create the control in a Windows application; this is a reasonable approach if the control will only be used in that application. You can also start a custom control as a UserControl and change your mind, making the same control a custom control by changing the Inherits statement. To effect the conversion, simply modify the Inherits System.Windows.Forms.UserControl to inherit from some other specific control, for example, Inherits System.Windows.Forms.Button.
The class library and Windows control library projects are there to help you get started but are not required aspects of creating controls. Keep in mind that controls are classes, and you can introduce them anywhere and at any time in your application where it makes sense to do so.
Creating a Nonvisual Component
There are several instances of user and custom controls throughout this book that demonstrate the mechanics of creating components. For this reason I have added an example of a nonvisual component in this section.
A nonvisual component is a component that generalizes the Component class. An example is the familiar Timer component. The component in Listing 16.10 wraps the events of the Application object, representing a Windows Forms application. By wrapping the events in the ApplicationEvents class, it becomes much easier to associate event handlers with Application events.
Listing 16.10 Wrapping the application object events in a component
1: Imports System.Threading 2: Imports System.Windows.Forms 3: Imports System.ComponentModel 4: 5: 6: Public Class ApplicationEvents 7: Inherits System.ComponentModel.Component 8: 9: Public Event ApplicationExit As EventHandler 10: Public Event Idle As EventHandler 11: Public Event ThreadException As ThreadExceptionEventHandler 12: Public Event ThreadExit As EventHandler 13: 14: Public Sub New() 15: AddHandler Application.ApplicationExit, _ 16: AddressOf DoApplicationExit 17: AddHandler Application.Idle, _ 18: AddressOf DoIdle 19: 20: AddHandler Application.ThreadException, _ 21: AddressOf DoThreadException 22: AddHandler Application.ThreadExit, _ 23: AddressOf DoThreadExit 24: End Sub 25: 26: Private Sub DoApplicationExit(ByVal sender As Object, _ 27: ByVal e As System.EventArgs) 28: RaiseEvent ApplicationExit(sender, e) 29: End Sub 30: 31: Private Sub DoIdle(ByVal sender As Object, _ 32: ByVal e As System.EventArgs) 33: 34: RaiseEvent Idle(sender, e) 35: End Sub 36: 37: Private Sub DoThreadException(ByVal sender As Object, _ 38: ByVal e As ThreadExceptionEventArgs) 39: 40: RaiseEvent ThreadException(sender, e) 41: 42: End Sub 43: 44: Public Sub DoThreadExit(ByVal sender As Object, _ 45: ByVal e As System.EventArgs) 46: 47: RaiseEvent ThreadExit(sender, e) 48: 49: End Sub 50: End Class
The ApplicationEvents component in Listing 16.10 inherits from Component. When you add it to a form, it will actually be added to the component tray. There are four events on lines 9 through 12 that exactly mirror the four Application events. The constructor on lines 14 to 24 adds the ApplicationEvents handlers to the Application events. When Application raises any of these events, the ApplicationEvents component receives the event and re-raises it, passing the event notification to the respondent.
We add a very modest procedural indirection by layering in a component, but we obtain maximum ease of use.
Test the ApplicationEvents component by installing the component and adding code to handle the individual events.
Specifying the Default Event
When you click on a form or a button, have you gotten around to wondering how a default event handler is selected and generated? Wonder no more. The System. ComponentModel.DefaultEventAttribute accepts the name of the event that is designated as the default event. If you add the following statement on line 5 of Listing 16.10, it will designate the Idle event as the default event for the ApplicationEvents class.
Recompile ApplicationEvents.vbproj. Now when you click the ApplicationEvents component, an Idle event handler will be generated by default. Of course, you can still pick individual events in the code designer, and the method bodies for those will be generated, too.