Microsoft's .NET Framework supports an interesting new feature called Visual Inheritance. As you will recall from Chapter 5, inheritance allows you to build new classes that extend the functionality of their base class. In Windows Forms, Visual Inheritance allows you to create new forms by inheriting the appearance and functionality of existing forms. In the derived form, you can see the controls on the base form as well as add new controls. This is all accomplished using the Inherits keyword. For example, SomeNewForm can inherit the appearance, controls, events handling, etc. from SomeBaseForm by coding: Public Class SomeNewForm Inherits SomeBaseForm ... End Class In the following two subsections, we will discuss the development of a base form and a form that inherits the appearance and behavior of the base form. Characteristics of the Base Form The form that will serve as the base class for visual inheritance can be in the same project as the derived form or can be imported from another project. It can even be written in a different .NET language. In this section, we will examine the Messages program. It is a VB.NET Class Library project and contains a form that will serve as our base class. Messages , shown in Figure 12-6, displays a list of status messages in a list box. Figure 12-6. Designing a base class form for visual inheritance. Step 1: Create a Project with the Base Form Because the form we are building has no practical use on its own (it displays status information of something else), we have decided to place it in a VB.NET Class Library project. However, we could also have chosen a Windows Application as the project type. We must now design both the appearance and functionality of the form. Table 12-5 summarized the form's properties. Table 12-5. Property Values for Visual Inheritance Demo Control Type | Name | Other Properties | Panel | panMessages | | Label | lblMessages | Text: Messages | Button | btnClear | Text: Clear | Listbox | lstMessages | | Form | MessageForm | Text: Form1 | Step 2: Determine the Controls Customizable by the Derived Class As with all other applications involving inheritance, the access modifier that we specify greatly impacts which class members are accessible to the derived form. The Windows Forms Designer adds variables and code to a form when you drag a control from the toolbox, place it on the form, and configure its properties. The variables that represent the instances of the control are automatically defined using the Friend access modifier. In the Messages program, the following variables were defined at the end of step 1: Friend WithEvents panMessages As System.Windows.Forms.Panel Friend WithEvents lblMessages As System.Windows.Forms.Label Friend WithEvents lstMessages As _ System.Windows.Forms.ListBox Friend WithEvents btnClear As System.Windows.Forms.Button As you recall from Chapter 5, VB.NET defines several access modifiers for class members that impact the member's accessibility from derived classes. These modifiers include: -
Public members are always accessible. -
Protected members are accessible only to a derived class's implementation. -
Friend members are accessible only from within the assembly that contains the class. -
Private members are not accessible outside the class definition. In order for a derived form to be able to interact with controls in a base form, the controls must be defined using an access modifier that makes them accessible: -
If the base class form is in a different assembly, controls must be defined as Public . -
If the base class form is in the same assembly as the derived form, controls may be defined using any modifier except Private . In our Messages program, we will allow derived classes to control the label and button. Access to the list box will be provided via a publicly accessible method. Therefore, we must make the following changes to the control declarations: Private WithEvents panMessages As _ System.Windows.Forms.Panel Protected WithEvents lblMessages As _ System.Windows.Forms.Label Private WithEvents lstMessages As _ System.Windows.Forms.ListBox Protected WithEvents btnClear As _ System.Windows.Forms.Button Step 3: Implement the Base Form's Behavior We must now add basic behavior to our form. In our case, we will add two methods , ClearMessages and AddToMessages , that allow access to the list box. We will also implement the Click event handler for btnClear . Public Sub ClearMessages() lstMessages.Items.Clear() End Sub Public Sub AddToMessages(ByVal message As String) lstMessages.Items.Add(message) End Sub Private Sub btnClear_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles btnClear.Click lstMessages.Items.Clear() End Sub Step 4: Building the Project Finally, we must build the application. Because we are implementing a class library project, we will be building a DLL. The code is not immediately executable, so we will now shift our discussion to the development of the derived form. Characteristics of the Derived Form To use visual inheritance, a form must use the Inherits keyword to inherit features from another form. This means that it will inherit all of the control variables as well as the form behavior that has already been developed. In this section, we will examine the Windows application CheckoutRegister . It will have a form that is derived from MessageForm in the Messages class library. That is, it uses a listbox to display status information. The CheckoutRegister application maintains a list of items that are available for sale. It allows users to calculate the sales total of items that are purchased. A user enters the item number and clicks the Lookup button. The application searches the list of items and, if found, displays the description and price of the item. The user can then enter the number of units that are being purchased and click the Add Item button. This places the item total in the messages listbox. This process continues until the user clicks the Total Sales button, which displays the total due for all items purchased. The Clear button will be used to start a new sale. (See Figure 12-7.) Figure 12-7. A form that uses visual inheritance. We have built the Items class to represent those items that are for sale. It implements the IComparable interface and overrides the Equals method (inherited from Object ) because we will use an ArrayList to manage the inventory and will be searching it to find items. Public Class Items Implements IComparable Private m_ItemNo As Integer Private m_Description As String Private m_Price As Decimal Public Sub New(ByVal itemNo As Integer, ByVal _ description As String, ByVal price As Decimal) m_ItemNo = itemNo m_Description = description m_Price = price End Sub Public ReadOnly Property ItemNo() As Integer Get Return m_ItemNo End Get End Property Public ReadOnly Property Description() As String Get Return m_Description End Get End Property Public ReadOnly Property Price() As Decimal Get Return m_Price End Get End Property Public Overloads Function CompareTo(ByVal obj As _ Object) As Integer Implements IComparable.CompareTo Dim it As Items = CType(obj, Items) If it.ItemNo < Me.ItemNo Then Return -1 ElseIf it.ItemNo = Me.ItemNo Then Return 0 Else Return 1 End If End Function Public Overloads Overrides Function Equals(_ ByVal obj As Object) As Boolean Dim it As Items = CType(obj, Items) Return it.ItemNo = Me.ItemNo End Function End Class Step 1: Create a Project that Uses Visual Inheritance We begin by building a Windows application named CheckoutRegister . When it is created, a blank form named Form1 is generated for you. Remove Form1 from the application by right-clicking on the form in the Solution Explorer window and choosing Delete. We will replace this form with a form that uses virtual inheritance. Now add the code for the Items class discussed previously. Now we are ready to build a form that inherits from MessageForm . Because this form is in a different assembly, we must first add a reference to that assembly. (This would not be necessary if the base form were in this project.) To add a reference to the Messages project, right-click the CheckoutRegister project in the Solution Explorer and select Add Reference. (See Figure 12-8.) Figure 12-8. Adding a reference to another .NET assembly. In the References dialog box, switch to the Projects tab and click the Browse button to find Messages.dll . Click OK to add the reference. (See Figure 12-9.) Figure 12-9. Specifying the .NET assembly containing the base class. Step 2: Adding an Inherited Form To add the startup form of the CheckoutRegister application, we must now add an Inherited Form to the project. Right-click on the CheckoutRegister project and select Add Inherited Form. Name the form CheckoutRegisterForm (see Figure 12-10). Figure 12-10. Adding an inherited form. In the Inheritance Picker dialog, select MessagesForm from the Messages project as the form to inherit from (see Figure 12-11). This will generate the following code: Public Class CheckoutRegisterForm Inherits Messages.MessageForm ... End Class Figure 12-11. Specifying the inherited form's base class. You must now design the form to resemble Figure 12-12. You should notice that the new form already looks just like MessageForm . You may resize it and the message panel stays docked at the bottom of the form. You should notice that the inherited buttons have a special icon in their upper-left corner, indicating they are inherited. Controls that were nonprivate in the base form can be manipulated. The label and button have resize handles to indicate you can manipulate them, change their properties and so on; however, the private panel and listbox are grayed out. Figure 12-12. Designing the derived form. Table 12-6 summarizes the controls you must add to the form and their properties. Table 12-6. Property Values for CheckoutRegisterForm Control Type | Name | Other Properties | Label | lblItemNo | Text: Item No | Textbox | txtItemNo | Text: (blank) | Button | btnLookup | Text: Lookup | Label | lblDescription | Text: Description | Textbox | txtDescription | Enabled: False Text: (blank) | Label | lblPrice | Text: Price | Textbox | txtPrice | Enabled: False Text: (blank) | Label | lblQuantity | Text: Quantity | Textbox | txtQuantity | Text: (blank) | Button | btnAddItem | Text: Add Item | Button | btnTotalSale | Text: Total Sale | INHERITED LABEL | lblMessages | Text: Receipt | Form | CheckoutRegisterForm | AcceptButton: btnLookup Text: Checkout Register | Step 3: Coding the Inherited Form To get our CheckoutRegister application working, we must add behavior to interact with the items that are for sale. To begin with, we will add an ArrayList to the form and initialize it to include a small inventory. We will also add a variable to represent the running total of the current sale. Public Class Register Inherits Messages.MessageForm Private inventory As New ArrayList() Private currentTotal As Decimal ... Public Sub New() ... inventory.Add(New Items(101, "Hammer", 21.95)) inventory.Add(New Items(102, "Plyers", 14.95)) inventory.Add(_ New Items(103, "Flat screwdriver", 11.95)) inventory.Add(_ New Items(104, "Philips screwdriver", 11.95)) inventory.Add(New Items(105, "Wrench", 15.95)) inventory.Add(New Items(106, "Awl", 12.95)) inventory.Add(New Items(107, "Saw", 43.95)) inventory.Add(New Items(108, "Hacksaw", 26.95)) inventory.Add(New Items(109, "Wire cutters", 21.95)) inventory.Sort() End Sub ... End Class When the user clicks on the Lookup button, we must use the item number that was entered in the textbox to locate the corresponding item in the inventory array. Private Sub btnLookup_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles btnLookup.Click Try Dim itemNo As Integer = _ Convert.ToInt32(txtItemNo.Text) Dim searchItem As New Items(itemNo, "", 0) Dim foundAt As Integer = _ inventory.BinarySearch(searchItem) If foundAt >= 0 Then Dim foundItem As Items = _ CType(inventory(foundAt), Items) txtItemNo.Text = foundItem.ItemNo txtDescription.Text = foundItem.Description txtPrice.Text = foundItem.Price Else Throw New Exception("Item not found!") End If Catch errObj As Exception MessageBox.Show(errObj.Message) End Try End Sub We must add code in the Click event handler of btnAddItem to add price information to the messages listbox. We can access the listbox via the inherited method AddToMessages . Private Sub btnAddItem_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles btnAddItem.Click Try Dim qty As Integer = _ Convert.ToInt32(txtQuantity.Text) Dim price As Decimal = _ Convert.ToDecimal(txtPrice.Text) Dim lineTotal As Decimal = qty * price Dim s As String s = String.Format(_ "{0,8:c} {1} unit(s) of item {2}({3}) at {4:c}", _ lineTotal, qty, txtItemNo.Text, _ txtDescription.Text, price) AddToMessages(s) currentTotal += lineTotal txtItemNo.Text = "" txtDescription.Text = "" txtPrice.Text = "" txtQuantity.Text = "" Catch errObj As Exception MessageBox.Show(errObj.Message) End Try End Sub We must also add code in the Click event handler of btnTotalSale to put the current total in the messages listbox. Private Sub btnTotalSale_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles btnTotalSale.Click Dim s As String s = String.Format(_ "TOTAL SALE: {0,8:c} ", currentTotal) AddToMessages(s) End Sub As you saw, visual inheritance is based upon the widely utilized object-oriented principles of inheritance and extension. By recognizing common design elements across a set of forms, you can greatly reduce the amount of repetitive coding by designing forms that can be extended using visual inheritance. |