home account info subscribe login search FAQ/help site map contact us


 
Brief Full
 Advanced
      Search
 Search Tips
To access the contents, click the chapter and section titles.

Advanced Visual Basic Techniques
(Publisher: John Wiley & Sons, Inc.)
Author(s): Rod Stephens
ISBN: 0471188816
Publication Date: 06/01/97

Search this book:
 
Previous Table of Contents Next


DataModified is fairly straightforward. It is true when there are pending changes to the document and false when there are no new changes to save. When the user successfully opens or saves a file, ExpenseReporter sets DataModified to false. The program uses Change events to set DataModified to true whenever the user changes data in a text box. For example, when the user types text into the Name field, the NameText_Change event handler executes code similar to the following:

Private Sub NameText_Change()
    DataModified = True
End Sub

IgnoreModify

In practice, using DataModified is not quite this simple. Occasionally the application needs to modify the controls that represent a document. This sort of modification does not represent a user-made change to the document. One example occurs when the user loads a file. When ExpenseReporter loads an existing expense report, it fills in all the text fields with values loaded from the file. This generates change events for every text box, but this does not mean the data has been modified. The file is simply being loaded, and initial values are being assigned to the fields.

ExpenseReporter prevents confusion in this case by resetting DataModified to false after it has loaded the document and filled in all of the fields. A trickier problem occurs when the program reformats data but does not change its value. For instance, before printing a report, ExpenseReporter makes sure that all dollar amount fields display two places beyond the decimal point. If the user enters “12.1” in the Prepaid field, ExpenseReporter changes the value to “12.10” before it prints the document. At this point the PrepaidText control’s Change event handler will detect the change and set DataModified to true even though the field’s value has not really changed. If the user opens an expense report, prints it, and then tries to exit from ExpenseReporter, the program will ask if it should save the changes. The user may rightfully be annoyed and confused because he or she has made no changes.

Table 1.1 Events That Change DataModified
Event DataModified Becomes
The user creates a new file False
The user opens an existing file False
The user saves the file False
The user changes the current document True

Even trickier problems can occur in control management code. Suppose the user loads an existing expense report and the first expense has Category value Meals. The values presented by the Description combo box depend on the value of an expense’s Category. Because the program now knows the Category of this expense, it must update the list of Description choices, which will cause the Description combo box to receive a Change event even though the data has not actually changed.

A simple solution to these problems is to create a new Boolean variable IgnoreModify. Whenever the application needs to make a change that does not represent a user-made change to the document’s data, it sets IgnoreModify to true. Change events then check this value to see if they should ignore the change or flag the document as modified. The following code shows the new Change event handler for the Name field.

Private Sub NameText_Change()
    If Not IgnoreModify Then DataModified = True
End Sub

After the program has made its nonmodifying changes, it must be sure to restore IgnoreModify to false. Otherwise, the program will never know if the document is later altered by the user.

This workaround can be particularly tricky if a routine that changes IgnoreModify is called by another routine that also sets IgnoreModify. The inner routine must not set IgnoreModify to false before it returns because the calling routine may still need the value to be true. If there is any doubt about whether one of these routines will be called by another, the routine should save the original value of IgnoreModify and restore it before returning. The SetDescrComboChoices subroutine that follows uses this technique to set the Description combo box choices based on an expense row’s Category.

Sub SetDescrComboChoices(Index As Integer)
Dim txt As String
Dim old_ignore As Boolean

    old_ignore = IgnoreModify
    IgnoreModify = True
   
    ‘ Save the current DescrCombo value.
    txt = DescrCombo(Index).Text
   
    ‘ Remove the old choices.
    DescrCombo(Index).Clear
   
    ‘ Create the appropriate choices.
    Select Case CategoryCombo(Index).Text
        Case “Travel”
            DescrCombo(Index).AddItem “Bus”
            DescrCombo(Index).AddItem “Car Mileage”
            DescrCombo(Index).AddItem “Car Rental”
            DescrCombo(Index).AddItem “Plane”
            DescrCombo(Index).AddItem “Taxi”
            DescrCombo(Index).AddItem “Train”
        Case “Meal”
            DescrCombo(Index).AddItem “Breakfast”
            DescrCombo(Index).AddItem “Lunch”
            DescrCombo(Index).AddItem “Dinner”
        Case “Hotel”
            DescrCombo(Index).AddItem “Room”
        Case Else
            DescrCombo(Index).AddItem “Gasoline”
            DescrCombo(Index).AddItem “Parking”
            DescrCombo(Index).AddItem “Toll”
    End Select

    ‘ Restore the previously selected value.
    DescrCombo(Index).Text = txt

    ‘ Restore IgnoreModify.
    IgnoreModify = old_ignore
End Sub

One final way in which IgnoreModify can cause trouble occurs if a routine exits early. For whatever reason, a routine may finish early using an Exit Sub statement. A subroutine also might catch an error using an On Error GoTo statement and then error-handling code might execute an Exit Sub statement. In these cases, the subroutine must be certain it resets IgnoreModify to its previous value before exiting. It should always reset IgnoreModify before any Exit Sub statement and before the subroutine ends normally using End Sub.

An even trickier situation occurs if the routine is called by another subroutine that contains an On Error statement. If the inner routine generates an error, control will pass immediately back to the calling subroutine. The inner routine will not have the chance to reset IgnoreModify to its original value. To handle this situation the inner routine should use its On Error GoTo statement to catch errors. The error-handling code should reset IgnoreModify and then use the Err object’s Raise method to raise the error again so the calling routine can handle it.

Sub ChangeField()
Dim old_ignore As Boolean

    old_ignore = IgnoreModify   ‘ Save IgnoreModify.
    IgnoreModify = True
    On Error GoTo ResetIgnore   ‘ Set the error trap.

    ‘ Modify field values, etc.
        :

    ‘ If all is well, the program gets here.
    IgnoreModify = old_ignore   ‘ Restore IgnoreModify.
    Exit Sub                    ‘ Exit Normally.

ResetIgnore:
    ‘ Error. Restore IgnoreModify and reraise the error.
    IgnoreModify = old_ignore
    Err.Raise Err.Number, Err.Source, Err.Description
End Sub


Previous Table of Contents Next


Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-1999 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permision of EarthWeb is prohibited.