2.3. Modify the Display with Events


Let's modify the spec to say that the Suppliers form will come up in display mode (with editing disabled), and the user will have the ability to make a menu choice to edit the form, and then save or cancel the edits.

Figure 2-25. Customer Details updated


To accomplish this, you'll want to add a menu to the form, and an indication (perhaps in the form title bar) as to which mode you are in: Read, Edit, or Unsaved. In Read mode, the text boxes and grid will be disabled. In Edit mode the controls will be enabled. Once you've made changes to the form, but not yet saved them, you'll be in Unsaved mode. The advantage of distinguishing between Edit and Unsaved mode is that if Cancel is selected or there is an attempt to close the form, you can put up a reminder that the changes have not been saved.

To begin, add a menu strip control to frmSuppliers.vb, as shown in Figure 2-26.

Figure 2-26. Add Editing Menu to frmSuppliers


The code in the frmSuppliers_Load event handler, as it now stands, loads the data from the database. You need to change it to first disable the text boxes and the datagrid, and then add event handlers to detect when the user makes changes.

The new implementation of frmSuppliers_Load is shown in Example 2-9.

Example 2-9. New Suppliers form Load event handler
 Private Sub frmSuppliers_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load     Me.SuppliersTableAdapter.FillByCompanyName( _     Me.NorthwindDataSet.Suppliers, Me.m_CompanyNameParameter)     Me.ProductsTableAdapter.Fill(Me.NorthwindDataSet.Products)    Dim ctrl As Control    Dim txtbox As TextBox = Nothing    Dim dgv As DataGridView = Nothing    For Each ctrl In Me.Controls       If TypeOf ctrl Is TextBox Then          txtbox = CType(ctrl, TextBox)          txtbox.Enabled = False          AddHandler txtbox.ModifiedChanged, AddressOf TextBoxChanged       ElseIf TypeOf ctrl Is DataGridView Then          dgv = CType(ctrl, DataGridView)          dgv.Enabled = False          AddHandler dgv.CellValueChanged, AddressOf DataGridChanged       End If    Next    Me.Text = formName + " Read only" End Sub 

You can't set every control to be disabled because you don't want to disable the menu!


Add a class member named formName and set that to the invariant text for the form title.

 Public Class frmSuppliers     Private ReadOnly formName As String = "Suppliers" 

The last line of Example 2-9 sets the form's title to Suppliers Read only. When the mode changes, you'll modify this string to keep the user up to date on the current mode.

Let's examine the For Each loop in Example 2-9 a bit more closely. You start by iterating through all of the controls in the form's Controls collection. You can't know what type of control you have, so you define your variable to be of type Control:

 Dim ctrl As Control 

You are looking for TextBox and DataGridView controls (the two types of controls you want to modify) so you create references to those types, which you will use if you determine that the actual type of the control is one of these two types:

 Dim txtbox As TextBox Dim dgv As DataGridView 

As you examine each control in turn, you check to see if it is of type Textbox If so, it is safe to cast that object to a TextBox and then set the Enabled property.

 If TypeOf ctrl Is TextBox Then     txtbox = CType(ctrl, TextBox)     txtbox.Enabled = False 

The next step is to set the method that you want all text boxes to invoke when their contents are changed:

 AddHandler txtbox.ModifiedChanged, AddressOf TextBoxChanged 

AddHandler adds an event handler to the text box. It takes two arguments: the event you want to handle (in this case, ModifiedChanged which is fired whenever the modified state of the text box is changed) and the address of the method to invoke. The net effect is that whenever the text box is changed, then the TextBoxChanged method will be called.

If the control is not a Text box, you test to see if it is a DataGridView, and if so, you disable it and set its event handler:

 ElseIf TypeOf ctrl Is DataGridView Then     dgv = CType(ctrl, DataGridView)     dgv.Enabled = False     AddHandler dgv.CellValueChanged, AddressOf DataGridChanged End If 

Notice that with a DataGridView you are responding to a different event: CellValueChanged. You'll come back to these methods in a moment.

At this point Visual Studio 2005 may be complaining because you have not yet added the event handler methods, though you have referred to them. You can ignore this complaint for now.


Add the Click event handler for the Edit menu item. To do so, click on Edit, then in the Properties window, click on the lightening bolt and then double-click next to the Click event. Visual Studio 2005 will create a skeleton for your event handler that you will fill in, as shown in Example 2-10.

Example 2-10. Edit item Click event handler
 Private Sub EditToolStripMenuItemEdit_Click( _ ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles EditToolStripMenuItemEdit.Click     Dim ctrl As Control     For Each ctrl In Me.Controls         ctrl.Enabled = True     Next     Me.Text = formName + " Ready to edit" End Sub 

When the user clicks Editing Edit, the event handler iterates through the controls and enables every control. This is safe, because you dont mind enabling the menu items and labels and other controls you previously ignored. You also change the title of the form to "Ready to edit."

When the user makes a change to any of the text boxes, the ModifiedChanged event fires, and as you saw earlier, the TextBoxChanged method is invoked. The job of this method is to keep track of changes in the data, and to set the title to "Edited, not saved."

To track whether any value has been changed, create a member variable:

 Private m_Dirty As Boolean = False 

This will be useful later, when the user clicks Cancel: you can test if any values have been changed just by checking this one Boolean value.

Next, create event handlers so that when either the TextBoxChanged or the DataGridChanged events fire, a helper method DataChanged, will be called that sets the m_Dirty flag to true, and sets the text for the form to "Edited, not saved." You need to type in the code for these, as shown in Example 2-11, rather than using Visual Basic to create event handlers. You associate them with the event at runtime by calling AddHandler, as you saw above.

Example 2-11. DataGrid and TextBox changed event handlers and the DataChanged helper method
 'event handler Private Sub DataGridChanged( _ ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.DataGridViewCellEventArgs)     DataChanged(  ) End Sub 'event handler Private Sub TextBoxChanged( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs)     DataChanged(  ) End Sub 'helper method Private Sub DataChanged(  )     Me.m_Dirty = True     Me.Text = formName + " Edited, not saved." End Sub 

Using the DataChanged helper method "factors out" common code from both event handlers, so that you do not have the same code in two places. This makes maintaining the program much easier.

You may wonder why you didn't just set DataChanged as the event handler method for the two events. The answer is that the two events require methods with different signatures. The ModifiedChanged event requires a method that takes as its second argument an object of type System.EventArgs, while CellValueChanged takes as its second argument an object of type DataGridViewCellEventArgs


All that is left is to handle the Save and Cancel events. If the user clicks Cancel, you want to check and see if the m_Dirty flag has been set true (indicating that the user has made some changes that might be lost). If so, you'll show a warning dialog box. If the user insists on the Cancel (saying yes at the warning), then you'll reload the original data (just as you did in Form_Load), disable the appropriate controls, and set the form title back to read only (ReadOnly).

Because resetting the data is done both in Form_Load and in Cancel, it's useful to factor that code out to a common method that can be called from either event handler, as shown in Example 2-12.

Example 2-12. LoadFromDB helper method
 Private Sub LoadFromDB(  )     Me.SuppliersTableAdapter.FillByCompanyName( _     Me.NorthwindDataSet.Suppliers, Me.m_CompanyNameParameter)     Me.ProductsTableAdapter.Fill(Me.NorthwindDataSet.Products) End Sub 

Thus, the start of the frmSuppliers_Load event handler goes from:

 Private Sub frmSuppliers_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load     Me.SuppliersTableAdapter.FillByCompanyName( _     Me.NorthwindDataSet.Suppliers, Me.m_CompanyNameParameter)     Me.ProductsTableAdapter.Fill(Me.NorthwindDataSet.Products) 

to the simpler:

 Private Sub frmSuppliers_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load    LoadFromDB(  ) 

Because you want to take the same action of disabling the controls and setting the form title back to read only whether the user clicks Cancel or Save, you'll factor that work out to a common method as well, as shown in Example 2-13.

Example 2-13. StopEditing helper method
 Private Sub StopEditing(  )     Dim ctrl As Control     For Each ctrl In Me.Controls         If TypeOf ctrl Is DataGridView Or TypeOf ctrl Is TextBox Then             ctrl.Enabled = False         End If     Next     Me.Text = formName + " Read only" End Sub 

Example 2-14 shows the the source code for the Cancel button event handler.

Example 2-14. Cancel button Click event handler
 Private Sub CancelToolStripMenuItemCancel_Click( _ ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles CancelToolStripMenuItemCancel.Click     Dim doCancel As Boolean = True     If Me.m_Dirty = True Then         Dim result As DialogResult = _            MessageBox.Show( _               "You have unsaved work. Are you sure you want to cancel?", _               "Risk of losing unsaved changes", _               MessageBoxButtons.YesNo, _               MessageBoxIcon.Warning)         If result = DialogResult.No Then             doCancel = False         End If     End If     If doCancel = True Then         LoadFromDB(  )         StopEditing(  )         m_Dirty = False     End If End Sub 

You first test to see if the m_Dirty flag is true. If so, you show a message box with the warning text and the Yes and No buttons. The result is stored in a DialogResult variable, which you can then test against the enumerated constant DialogResult.No. Assuming this test fails (and the user clicked Yes), or if the m_Dirty flag was not true, you are now ready to reload the original data (by calling LoadFromDB) and disable the controls (by calling StopEditing).

Finally, if the user clicks Save, you will call EndEdit on the DataConnector, and Update on the TableAdapter (as shown earlier in this chapter). Finally, you will call StopEditing to return to read only mode. Code for handling the Save item on the menu is shown in Example 2-15.

Example 2-15. Save menu item Click event handler
 Private Sub SaveToolStripMenuItem_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles SaveToolStripMenuItem.Click    Me.SuppliersBindingSource.EndEdit(  )    If m_Dirty = True Then       Dim tbChanges As Data.DataTable = _          Me.NorthwindDataSet.Suppliers.GetChanges(  )       If Not tbChanges Is Nothing Then          Me.SuppliersTableAdapter.Update(tbChanges)       End If       tbChanges = Me.NorthwindDataSet.Products.GetChanges(  )       If Not tbChanges Is Nothing Then          Me.ProductsTableAdapter.Update(tbChanges)       End If    End If    StopEditing(  ) End Sub 

The complete source for this form is shown in Example 2-16.

Example 2-16. Complete source code for frmSuppliers
 Public Class frmSuppliers    Private m_CompanyNameParameter As String    Private m_Dirty As Boolean = False    Private ReadOnly formName As String = "Suppliers"    Public WriteOnly Property CompanyNameParameter(  ) As String       Set(ByVal value As String)          m_CompanyNameParameter = value       End Set    End Property    Private Sub frmSuppliers_Load( _    ByVal sender As System.Object, _    ByVal e As System.EventArgs) Handles MyBase.Load       LoadFromDB(  )       Dim ctrl As Control       Dim txtbox As TextBox       Dim dgv As DataGridView       For Each ctrl In Me.Controls          If TypeOf ctrl Is TextBox Then             txtbox = CType(ctrl, TextBox)             txtbox.Enabled = False             AddHandler txtbox.ModifiedChanged, AddressOf TextBoxChanged          ElseIf TypeOf ctrl Is DataGridView Then             dgv = CType(ctrl, DataGridView)             dgv.Enabled = False             AddHandler dgv.CellValueChanged, AddressOf DataGridChanged          End If       Next       Me.Text = formName + " Read only"    End Sub    Private Sub StopEditing(  )       Dim ctrl As Control       For Each ctrl In Me.Controls          If TypeOf ctrl Is DataGridView Or TypeOf ctrl Is TextBox Then             ctrl.Enabled = False          End If       Next       Me.Text = formName + " Read only"    End Sub    Private Sub LoadFromDB(  )       Me.SuppliersTableAdapter.FillByCompanyName( _       Me.NorthwindDataSet.Suppliers, Me.m_CompanyNameParameter)       Me.ProductsTableAdapter.Fill(Me.NorthwindDataSet.Products)    End Sub    Private Sub EditToolStripMenuItem_Click( _    ByVal sender As System.Object, _    ByVal e As System.EventArgs) Handles EditToolStripMenuItem.Click       Dim ctrl As Control       For Each ctrl In Me.Controls          ctrl.Enabled = True       Next       Me.Text = formName + " Ready to edit"    End Sub    'event handler    Private Sub DataGridChanged( _    ByVal sender As System.Object, _    ByVal e As System.Windows.Forms.DataGridViewCellEventArgs)       DataChanged(  )    End Sub    'event handler    Private Sub TextBoxChanged( _    ByVal sender As System.Object, _    ByVal e As System.EventArgs)       DataChanged(  )    End Sub    'helper method    Private Sub DataChanged(  )       Me.m_Dirty = True       Me.Text = formName + " Edited, not saved."    End Sub    Private Sub CancelToolStripMenuItem_Click( _    ByVal sender As System.Object, _    ByVal e As System.EventArgs) Handles CancelToolStripMenuItem.Click       Dim doCancel As Boolean = True       If Me.m_Dirty = True Then          Dim result As DialogResult = _             MessageBox.Show( _                "You have unsaved work. Are you sure you want to cancel?", _                "Risk of losing unsaved changes", _                MessageBoxButtons.YesNo, _                MessageBoxIcon.Warning)          If result = DialogResult.No Then             doCancel = False          End If       End If       If doCancel = True Then          LoadFromDB(  )          StopEditing(  )          m_Dirty = False       End If    End Sub    Private Sub SaveToolStripMenuItem_Click( _    ByVal sender As System.Object, _    ByVal e As System.EventArgs) Handles SaveToolStripMenuItem.Click       Me.SuppliersBindingSource.EndEdit(  )       If m_Dirty = True Then          Dim tbChanges As Data.DataTable = _             Me.NorthwindDataSet.Suppliers.GetChanges(  )          If Not tbChanges Is Nothing Then             Me.SuppliersTableAdapter.Update(tbChanges)          End If          tbChanges = Me.NorthwindDataSet.Products.GetChanges(  )          If Not tbChanges Is Nothing Then             Me.ProductsTableAdapter.Update(tbChanges)          End If       End If       StopEditing(  )    End Sub End Class 

When you run this application, and find a Supplier, the controls are initially disabled, as shown in Figure 2-27.

If you click on Editing Edit, you enter Edit mode, and the title changes to "Ready to edit," as shown in Figure 2-28.

If you change any field the Title changes again to "Edited, not saved," as shown in Figure 2-29.

If you click on Cancel, because you have made changes, the warning dialog will come up, as shown in Figure 2-30.

Figure 2-27. Suppliers form opens in read only mode


Figure 2-28. Edit mode


Figure 2-29. Edited, not saved


Figure 2-30. Warning dialog for canceling with unsaved work


If you click Yes here, the changes will be undone and you'll be returned to the original read only mode. If you click No, you'll remain in "Edited, not saved" mode.

Finally, if you click Save, the changes are saved, the database is updated, and you are returned to read only mode, with the new data reflected.

Mission accomplished.



Programming Visual Basic 2005
Programming Visual Basic 2005
ISBN: 0596009496
EAN: 2147483647
Year: 2006
Pages: 162
Authors: Jesse Liberty

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net