Your custom control (RolodexPanel
) will be housed within a form. The base form will be frmRolodex
, whose job will be to provide common code for all the specialized forms (e.g., frmCustomerRolodex
).
Back in the NorthWindWindows
project, add a new form, frmRolodex
. Set its size to 976,615
. Open the Toolbox and expand the NorthWindControls
Components section. Drag a RolodexPanel
onto the new form, and drag a label named lblDisplay
above it, as shown in Figure 4-6.
Everything in frmRolodex
will be shared by all its derived types. You want to factor all the elements common to the derived forms into this form, so they will be as simple (and maintainable) as possible.
You need two members:
Protected orderedBy As String Protected infoTable As Data.DataTable
The first, orderedBy
, will keep track of the sort order for the data table. The second, infoTable
, will hold a reference to a DataTable
(e.g., the Customers table).
There are three event handlers you must create: one for when the form is loaded, the second for when the RowFillEvent
is fired by the RolodexPanel
, and the third for when the ButtonSelectedEvent
is fired by the RolodexPanel
.
When the form is loaded, you'll call LoadRolodex
, a helper method, as shown in Example 4-12.
Example 4-12. Rolodex form Load event handler
Private Sub frmRolodex_Load( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
LoadRolodex()
End Sub
This method will not be implemented in the base class, but will be implemented in the derived forms:
Protected Overridable Sub LoadRolodex() End Sub Protected Overridable Sub LoadRolodex(ByVal letter As Char) End Sub
The second event handler responds to the RowFillEvent
of the RolodexPanel
, as shown in Example 4-13.
This event handler calls the helper method FillRows
, passing in the table to fill the rows from, as shown in Example 4-14.
Example 4-14. FillRows helper method
Protected Sub FillRows(ByVal infoTable As Data.DataTable) Dim column As Integer = 0 Dim row As Integer = 0 Me.RolodexPanel1.Clear() Dim loopcounter As Integer For loopcounter = 0 To Me.RolodexPanel1.NumEntriesPerPage -1 Dim offset As Integer = Me.RolodexPanel1.Vbar.Value + _ (row * 3) + column If offset >= infoTable.Rows.Count Then Exit For End If Dim dataRow As System.Data.DataRow = infoTable.Rows(offset) AddEntry(dataRow, column, row) column = column + 1 If column = 3 Then column = 0 row = row + 1 End If Next End Sub
The effect is to fill the Rolodex Panel with three rows of RolodexEntry
objects.
The FillRows
method is overloaded. The second version is called by the event handler that responds to an A-Z button being pressed.
Private Sub OnButtonSelected( _
ByVal sender As Object, _
ByVal e As EventArgs) _
Handles RolodexPanel1.ButtonSelectedEvent
FillRows(Me.RolodexPanel1.CurrentLetter, Me.infoTable)
End Sub
This version of FillRows
takes the letter to search for within the data (as well as the DataTable
containing the data), as shown in Example 4-15.
Example 4-15. Overloaded version of FillRows helper method
Protected Sub FillRows( _ ByVal letter As Char, _ ByVal infoTable As Data.DataTable) Dim offset As Integer = 0 Dim orderByName As Char = CType("A", Char) For Each dr As Data.DataRow In infoTable.Rows orderByName = dr(orderedby).ToString().ToUpper()(0) If orderByName >= letter Then Exit For End If offset = offset + 1 Next Me.RolodexPanel1.Vbar.Value = offset End Sub
Tip
For a description of how the If
statement statement works in this code, please see the step-by-step description of clicking on a letter, later in this chapter.
Finally, the code that will be shared by the LoadRolodex
override of all the derived forms is factored into the DoLoad
method of the base class, shown in Example 4-16.
Example 4-16. DoLoad method DoLoad method
Protected Sub DoLoad( _ ByVal count As Integer, _ ByVal letter As Char, _ ByVal infoTable As Data.DataTable) Me.RolodexPanel1.Vbar.Maximum = count Me.lblDisplay.Text = count.ToString() + " records " Me.RolodexPanel1.Vbar.Value = 0 FillRows(infoTable) End Sub
With the base form in place, you're ready to derive a specialized form: frmCusto-merRolodex
.
Right-click on the NorthWindWindows
project and choose Add â New Item and select Inherited Form. Name the new form frmCustomerRolodex.vb
. You are then presented with the InheritancePicker. Select frmRolodex
and press OK. A new form is created that inherits from frmRolodex
named frmCustomerRolodex
.
Notice that the panel and label are already in place (though the label may be invisible because we set its text to blank). You need access to the CustomersTableAdapter
that you created earlier. Look in the toolbox and open the section marked NorthWindWindows Components. Drag the CustomersTableAdapter
and the NorthwindDataSet
to your form. Rename the dataset instance from NorthwindDataSet1
to NorthwindDataSet
and CustomerTableAdapter1
to CustomerTableAdapter
.
You want this form shown when the user clicks All Customers from the Welcome page. Go to btnAllClick
in Welcome.vb and modify the btnAllClick
method to invoke this method if the button's text is All Customers or if the menu contains the word Customers in the text, as shown in the bold code in Example 4-17.
Example 4-17. Modifying the AllClick event handler
Private Sub btnAllClick( _
ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles btnAllCustomers.Click, btnAllSuppliers.Click, _
btnAllEmployees.Click, btnAllOrders.Click, mnuEmployeesShowAll.Click, _
mnuCustomersShowAll.Click, mnuOrdersShowAll.Click
Dim txt As String = String.Empty
If TypeOf sender Is Button Then
txt = CType(sender, Button).Text
ElseIf TypeOf sender Is ToolStripMenuItem Then
txt = CType(sender, ToolStripMenuItem).Name
End If
Dim oldCursor As Cursor = Me.Cursor
Me.Cursor = Cursors.WaitCursor
If txt.Contains("Customers") Then
Dim rolodex As frmRolodex = New frmCustomerRolodex()
rolodex.Show()
Else
MessageBox.Show(txt + _
" not yet implemented", "Not Yet Implemented", _
MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
End If
Me.Cursor = oldCursor
End Sub
Now you can go back to frmCustomerRolodex
and override the three overridable methods from the base form. The first is LoadRolodex
, which is overloaded. The code is shown in Example 4-18.
Example 4-18. Overriding the Rolodex form Load event handler
Protected Overrides Sub LoadRolodex()
LoadRolodex(CChar("A"))
End Sub
Protected Overrides Sub LoadRolodex(ByVal letter As Char)
CustomersTableAdapter.Fill( _
CType(Me.NorthwindDataSet.Tables("Customers"), _
NorthWindWindows.NorthwindDataSet.CustomersDataTable))
Dim dataTable As NorthwindDataSet.CustomersDataTable = _
CustomersTableAdapter.GetData()
Dim count As Integer = dataTable.Rows.Count
Me.infoTable = dataTable
Me.orderedby = "CompanyName"
DoLoad(count, letter, infoTable)
End Sub
In the second overload (the one that takes a letter), you call the Fill
method on the CustomersTableAdapter
, passing in the Customers table you extract from the NorthwindDataSet
variable you just added to the form.
Your only other override is of AddEntry
, shown in Example 4-19. This method is very specific to customers. It is also tightly coupled with the Customers table (it knows what values to extract) and with the RolodexCustomerEntry
(it knows what values to set). It is, in many ways, the bridge between the RolodexCustomerEntry
and its underlying table.
Example 4-19. Overriding the AddEntry method
Protected Overrides Sub AddEntry( _ ByVal dataRow As System.Data.DataRow, _ ByVal column As Integer, _ ByVal row As Integer) Dim entry As NorthWindControls.RolodexCustomerEntry = _ New NorthWindControls.RolodexCustomerEntry() Dim companyName As String = String.Empty Dim contactName As String = String.Empty Dim phone As String = String.Empty Dim fax As String = String.Empty If IsDBNull(dataRow("CompanyName")) = False Then companyName = CStr(dataRow("CompanyName")) End If If IsDBNull(dataRow("ContactName")) = False Then contactName = CStr(dataRow("ContactName")) End If If IsDBNull(dataRow("Phone")) = False Then phone = CStr(dataRow("Phone")) End If If IsDBNull(dataRow("Fax")) = False Then fax = CStr(dataRow("Fax")) End If entry.LoadValues(companyName, contactName, phone, fax) entry.Left = Me.RolodexPanel1.StartX + _ (column * Me.RolodexPanel1.XIncrement) entry.Top = Me.RolodexPanel1.StartY + _ (row * Me.RolodexPanel1.YIncrement) AddHandler entry.EntrySelected, _ AddressOf Me.RolodexPanel1.entry_click Me.RolodexPanel1.Add(entry) End Sub
The order of operations is critical here. The very best way to see this in action is to use your debugger and to set break points on the following methods:
Welcome.vb:
btnAllClick
frmCustomerRolodex
: all three methodsfrmRolodex
:frmRolodex_Load
,FillRows
(both overloads), andDoLoad
RolodexPanel
:RolodexPanel_Load
RolodexCustomerEntry
:Load_Values
When you ask to see all the customers by clicking on the All Customers button, the btnAllClick
method is called in Welcome.vb. The button is examined and since its text is All Customers, the frmCustomerRolodex
is created and shown.
When frmCustomerRolodex
is loaded, the LoadRolodex
method runs, fills the CustomersDataTable
in the NorthWindDataSet
, and then sets the member variable infoTable
to the CustomersDataTable
. The DoLoad
method is then called in the base class, frmRolodex
.
DoLoad
sets the vertical scrollbar maximum and minimum values, sets lblDisplay
.Text
, then calls FillRows
, passing in the CustomersDataTable
. FillRows
populates the three columns by extracting one row from the data table (Customers) and calling AddEntry
.
AddEntry
creates a new RolodexCustomerEntry
object and sets its lblCompanyName
, lblContactName
, lblPhone
, and lblFax
based on the data in the DataRow
.
It then sets the position (the column and row) of the entry and, most importantly, it adds an event handler for that entry. When the entry fires its EntrySelected
event, you want the event to be handled by the entry_click
method of the Rolodex Panel.
AddHandler entry.EntrySelected, _ AddressOf Me.RolodexPanel1.entry_click
The entry is then added to the panel. This process repeats for as many entries
as will fit in the form (defined as RolodexPanel.NumPageEntries
). Once completed, FillRows
is finished and the form is displayed.
When you click on an entry, it is lit up as red. The best way to see how this works is to put break points on:
RolodexCustomerEntry
:InternalClick
,SetSelectedProperties
RolodexEntry
:InternalClick
,Selected
Set AccessorRolodexPanel
:entry_click
When the user clicks on an entry, that click is captured by RolodexCustomerEntry.InternalClick
. It invokes MyBase.InternalClick
, passing in a reference to itself. The base method raises the EntrySelected
event, placing a reference to the RolodexEntry
that was clicked into the sender argument.
RolodexPanel.entry_click
handles that event and deselects every one of its controls. It then sets Selected
to True
on the one RolodexEntry
that was passed in as sender
. This invokes the Selected
accessor on that Rolodex entry, which calls SetSelectedProperties
.
SetSelectedProperties
is overridden in RolodexCustomerEntry
. When the item is not selected, its lblCompanyName
background is set to Silver
. When it is selected, the background is set to Red
.
To see what happens when a Letter button is clicked, set break points in:
RolodexPanel
:LetterButtonClick
,LoadRolodex
, andvBar_valueChanged
frmRolodex
:OnButtonSelected
,FillRows
Click on the letter T. The LetterButton_Click
method is invoked. The result of this is to invoke LoadRolodex
(passing in the letter), which sets the current letter, and then to raise the event ButtonSelectedEvent
.
That event is caught by frmRolodex
, which invokes the FillRows
method, passing in the current letter and the data table. FillRows
iterates through the rows until it finds a name that begins with a letter equal to or greater than the requested name, at which time it sets the vertical scrollbar value to the offset.
Setting the vertical scrollbar's value causes the vbar
to raise the ValueChanged
event, which you set in RolodexPanel1_Load
to be handled by vbar_ValueChanged
. That, in turn, raises the RowFillEvent
, passing in the Rolodex Panel itself).
The RowFillEvent
is handled by OnFillRows
in frmRolodex
, which calls the other FillRows
method, passing in the DataTable
. FillRows
extracts the offset from the vertical scrollbar and creates the entries, as you saw earlier, filling in the panel, as shown in Figure 4-7.
Get Programming Visual Basic 2005 now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.