1. Trang chủ
  2. » Công Nghệ Thông Tin

Access 2007 VBA Programmer’s Reference phần 4 pps

115 1K 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Using VBA to Enhance Forms
Trường học Worcester Polytechnic Institute
Chuyên ngành Access 2007 VBA Programming
Thể loại sách hướng dẫn chương trình
Năm xuất bản 2007
Thành phố Worcester
Định dạng
Số trang 115
Dung lượng 2,32 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Theoption group is added to the form, and Figure 10-7 uses the DisplayAsImageIconoption in showingthe same record and attachment shown in Figure 10-6.Figure 10-7 The following code allow

Trang 1

Private Sub MyAttachment_AttachmentCurrent()

‘ update the text boxes for form view where the

‘ default view is not a split form or datasheet view

If (Me.CurrentView = AcCurrentView.acCurViewFormBrowse _And Me.DefaultView <> AcDefView.acDefViewSplitFormAnd Me.DefaultView <> AcDefView.acDefViewDataSheet) ThenMe.txtFileName.Value = Me.MyAttachment.FileName

Me.txtFileCount.Value = Me.MyAttachment.AttachmentCountMe.txtFileType.Value = Me.MyAttachment.FileType

ElseMe.txtFileName.Value = NullMe.txtFileCount.Value = NullMe.txtFileType.Value = NullEnd If

End SubWith this code, you can now view the attachment filenames and file types (extension) as you browsethrough the attachments It may seem redundant to explicitly retrieve the file type because it typicallyshows up in the filename, but the purpose here is to demonstrate how to retrieve the information

This example code starts by retrieving the values for the attachment only if the form is not displayed inSplit Form or Datasheet view (we already learned that lesson) There may be issues or unexpectedresults when trying to obtain these values from other form views, so the value is set to null for othercases In specifying the values, the left side of the equation identifies the text box that will display thevalue retrieved The right side identifies the source of the data; in this case, it is looking at the controlcalled MyAttachment Because it’s an attachment control, it has some special properties, includingFileName, FileType, and AttachmentCount

❑ FileName: The actual name of the file (MyPicture.jpegwill display as MyPicture.jpeg)

❑ FileType: Refers to file extension or type (JPEG, TXT, BMP, PDF)

❑ AttachmentCount: The number of attachments stored for that record

The event AttachmentCurrentworks similar to the form’s OnCurrentevent in that it fires when ing the focus from one attachment to another Within this event you can both extract information andspecify the way that attachments will be represented As the preceding code demonstrated, you can usethe AttachmentCountproperty to list the number of attachments It is helpful to use this count in con-junction with the icon and image displays since they do not indicate the number of attachments Theattachment control uses an image and has the following three display options:

mov-❑ acDisplayAsIcon: Displays the default icon for that file

❑ acDisplayAsImageIcon: Displays icons for txt files; actual picture for jpeg’s and bmp’s

❑ acDisplayAsPaperclip: The default setting Although this does not indicate the file type, it isthe only option that, by default, displays the AttachmentCount

Trang 2

In the next example, an option group demonstrates the three different views to display the image Theoption group is added to the form, and Figure 10-7 uses the DisplayAsImageIconoption in showingthe same record and attachment shown in Figure 10-6.

Figure 10-7

The following code allows the user to choose whether the attachment control will display as an icon,icon/image, or paper clip

Private Sub grpAttachView_Click()

Dim OptSelect as Integer

OptSelect = Me.grpAttachView.Value

Select Case OptSelect

Case 1With Me.MyAttachment.ControlTipText = “This option displays file Icons only.”

.DisplayAs = acDisplayAsIcon.Height = “450”

End WithCase 2With Me.MyAttachment.ControlTipText = “This option displays file Images and Icons.”

.DisplayAs = acDisplayAsImageIcon.Height = “1440”

End WithCase 3With Me.MyAttachment.ControlTipText = “This option displays files as a Paperclip.”

.DisplayAs = acDisplayAsPaperclipEnd With

End Select

End Sub

For options acDisplayAsIconand acDisplayAsImageIcon,you add the Heightproperty and set it

to “450”twips and “1440”twips (1 inch), respectively Why specify the height? Because the defaultheight for an attachment control that is set to acDisplayAsPaperclip is only 0.1667 inches By specify-ing the height in code, the control can grow or shrink as appropriate for the display type

Trang 3

When using code to specify the height and width in twips, do not use commas as regional settings may cause the comma to be interpreted as a decimal point Notice that the code uses 1440 not 1,440 and that the numbers are enclosed in double quotes.

While you are at it, you may also want to use the Property Sheet Format tab and change the Picture SizeMode to Zoom, which will preserve the image proportions If you are concerned about space or present-ing a compact look, you may also want to adjust the cell padding around images and icons Figure 10-8illustrates the effects of several of these settings

Figure 10-8

The attachment control has its own pop-up menu for adding, removing, opening, saving, and movingthrough attachments This is a powerful little control because it works like the OpenFiledialog box butsaves the collections of files within the record rather than in a separate table, streamlining the process ofsaving or attaching files to a record However, with the built-in control, you must select working withnone, one, or all attachments Unlike a fully functional multi-select group, it does not allow users toselect several (but not all) attachments

Combo Boxes

The combo box is a potent control that can combine the processes to search, select, and populate datainto fields (or other objects), as well as limit the values that a user may enter Many nuances affect how

I don’t know about you, but I certainly am not accustomed to calculating in twips.

So, if you are trying to specify image heights, you may appreciate knowing that a twip is 1 ⁄ 1,440 of an inch, which is the equivalent of 1 ⁄ 567 of a centimeter or 1 ⁄ 20 th of a point

Trang 4

combo boxes work; this section addresses some of the more common ones It also covers some new tures, including two new properties: Allow Value List Editsand List Items Edit Form.

fea-The combo box and list box have a lot of the same properties and functionality fea-The primary difference isthat the contents of the list box are always displayed on the form, so the list box takes more space Listbox values are also limited to the list, which can be updated programmatically by adding values to therow source, but not directly by the user Combo boxes, on the other hand, generally use the space of anormal text box and employ a drop-down list to display the number of rows specified in the properties.The row source can be set to be updateable or to allow only existing values

Before continuing, let’s clarify the difference between control source and row source The control source

is specified if the value selected in the combo box will automatically (rather than programmatically) bestored in a field The last question in the Combo Box Wizard, for instance, is about remembering or stor-ing the selected value If you choose to store the selected value in a field, that field becomes the control

source The row source provides the values that are displayed and used by the combo box It can be a

table, a query, a value list, or a field list The row source value can even be an empty string when theform opens and then be set programmatically For the most part, the row source consists of multiplecolumns with only one column visible to the user The other columns can be used to populate otherfields or as the link to other record sources For example, one column could be the field that links thecurrent form to a subform Again, referring to the wizard provides a quick demonstration of some of theproperties related to the row source When you tell the Combo Box Wizard, for instance, that the combobox will look up values in a table or query, you are specifying the row source The wizard then goesthrough the process of selecting the fields for the columns and allowing you to specify the columnwidths The wizard automatically adds the primary key if one is available

The other main element that you’ll work with is controlling the values that the user can use or add Butfirst, you’ll tackle an easy example of using a combo box to display existing data

Combo Box as a Look-Up

An excellent use for the combo box control is to display descriptive information for the user even thoughthe data that’s actually stored in the database is the value of some sort of key (primary or foreign) Forexample, you may have a form in which users allocate sales and you want the users to identify a depart-ment If that form (or even just the combo box) is bound to a table that stores the Department ID ratherthan the name of the department, you don’t want to force the user to remember or figure out whichDepartment ID to enter Instead, you can use a combo box to display a list of departments (field nameDepartment) and let the form manage how the Department ID is stored The following table lists thecombo box settings for this scenario You will also find this in frmContactsComboLookUpin this chap-ter’s database file named ComboBox.accdb

Control Source Department(this is from the table Contacts)

Row Source Select DepartmentIDand Department from tblDepartment

Trang 5

Property Value

List Items Edit Form No value (leave blank)

on the settings for the new Allow Value List Editsproperty along with the List Items EditFormproperty All this is by way of saying you should be aware that the On Not In Listevent occursbefore the settings of the new properties are even considered

It may seem as though we are hammering on just a few properties, but slight variations in how they arecombined can have a big impact on results

Allow Value List Edits

The Allow Value List Editsproperty provides a simple way for you to allow the user to change thecontents of the list without requiring the user to go into Design view to change the row source or you(the developer) to write code to change the row source Keep in mind that this property is intended forcombo boxes where the Row Source Typeproperty is set to Value List— meaning that it is notdesigned to work with other row source types, so don’t be tempted to misapply this feature

If Allow Value List Editsis set to Noand the user enters a value that isn’t in the list, the user willsee the standard Access message (see Figure 10-9) advising him to select an item from the list

Figure 10-9

Trang 6

If Allow Value List Editsis set to Yesand the user enters a value that isn’t in the list, the user willsee a message box that asks if he wants to edit the items in the list, as shown in Figure 10-10a You’ll alsonotice the extra question at the bottom of the message box, “Was this information helpful?” That mes-sage will appear if the user has enabled “Help Improve Access.” Responding to this query will clear thatmessage without closing the message box, as shown in Figure 10-10b.

Figure 10-10

If the user chooses Yes, to edit the list, the default Access Edit Items List formdisplays (seeFigure 10-11) This default form is clearly designed to capture only a simple list of values, and it isintended to be used only when the combo box Row Source Typeis set to Value List, InheritValue Listis set to Yes, and both the the Bound Columnand Column Countare set to 1 Keep inmind that this form provides no means for you to control the values that the user might enter, so termsmay be entered multiple times with different spellings In Figure 10-11, you can see that Dance has beenadded to the list from the combo box labeled “Value List With Edit:” even though “Dancing” wasalready in the list If you want more control over what the user enters for the list or if you want to pro-vide a multicolumn list, you should set the Row Source Typeto be Table/Queryand use the ListItems Edit Formproperty to specify a custom form We will discuss that approach shortly

Figure 10-11

One thing to note about using this approach to allow value list edits is that the Row Sourceproperty

of the combo box is actually updated, which in turn causes Access to respond as though the design ofthe form has changed Because of this, when the user closes the form that contains the (updated) combobox, Access will display a message box to ask if the user wants to “…save changes to the design of the

form [form name]” This is not a good message for users to see, so you may want to use the DoCmd.Save

Trang 7

acForm, Me.Namestatement to save the form in the Form_AfterUpdateevent (This is obviously not adesired scenario, and you can hope for a resolution in an early Access update.)

List Items Edit Form

As mentioned earlier, the big advantages of the new Allow Value List Editsproperty is that youdon’t have to write code in the On NotIn Listevent to capture the new value, and it provides amethod that allows the user to include the values he wants in the list But please remember (and this isworth repeating) that the new Allow Value List Editsproperty is intended only for combo boxesand list boxes with a Row Source Typeof Value List If you are using the Row Source Type ofTable/Query, you can use the new List Items Edit Formproperty to display an alternate form thatallows the user to edit the list

First you must first create a form that will maintain the data that is displayed from the row sourceofthe combo box Then you set the List Items Edit Formproperty to name of that form You could get

a little fancy with your form by programmatically taking the invalid value that the user entered in thecombo box and loading it into the appropriate field on your edit form That kind of defeats the concept

of using this new feature to avoid writing code, but then this book is about code and creating solutionsthat save time and prevent errors Coming up is one example of using code to optimize use of a couple

of the new “no code required” features

Because users can use the Navigation pane, it might be beneficial to programmatically restrict the bility to open your edit forms, such as by using the Is Loadedevent (see discussion later in this chap- ter) After all, you don’t want users to get an error message if they open a form from the Navigation pane (In the database for this section, EditValueList.accdb, the form frmMayorUpdate includes a method to prevent the form from being opened from the Navigation pane.)

capa-This example uses a form called frmContactMayorwith combo box cboCity, which uses a look-uptable to capture the city for the contact, storing the data in the field named Cityin tblContacts Forthis exercise, assume that the application needs to capture the mayor of every city Basically, this meansthat if a user enters a new city, you want the application, through a form, to require him to enter thename of that city’s mayor To accomplish this, the example uses a table of mayors called tblMayorsand

a frmMayorUpdateform to update that table You also want to think about creating an index oncity/state and requiring that to have a unique value

This example is provided in the chapter download file, EditValueList.accdb To get the process started,you’ll create (or modify) the combo box, cboCity, to have the key property values shown in the follow-ing table

When allowing value list edits by using the default Access Edit Items Listform,

it is critical that the Row Source Typeis Value Listand Inherit Value Listis set to Yes This allows Access to properly handle the update With other property configurations, users may get a warning and be asked if they want to save changes

to the design of the form After all, a change to a value list behind a control is a change to the form’s design

Trang 8

Property Value

Allow Value List Edits Yes

List Items Edit Form frmMayorUpdate

To ensure that the mayor’s name is provided, the form should use one of the other techniques you’velearned in this book For instance, you could use the Form_BeforeUpdateevent to see if the mayor’sname field has a value The example uses Form_Opento check for the value

Additionally, set the frmMayorUpdateform Pop Upproperty to Yes, and set the Modalproperty to Yes,and set Cycle to Current Record (found on the Other tab of the property sheet) You do this so that theForm_Opencode in the form frmMayorUpdatealways executes One reason for making this a modal form

is to force the user to close the form before he can enter additional data in the form frmContactMayor(orbefore he can work with other objects, for that matter) If the user is allowed to leave frmMayorUpdateopen while continuing to enter data on frmContactMayor, the next invalid entry in cboCitywon’t firethe Form_Openevent in frmMayorUpdate

At this point, you’re about ready to put the following code in the Openevent for frmMayorUpdate First you establish a constant to be used for the “calling form.” That makes this snippet more portable.Instead of replacing the name of the calling form throughout the code, you merely change the value forthe constant, cFormUsage

Const cFormUsage = “frmContactMayor”

Private Sub Form_Open(Cancel As Integer)

Dim strText As String

Dim rs As Recordset

‘ Don’t let this form be opened from the Navigator

If Not CurrentProject.AllForms(cFormUsage).IsLoaded Then

MsgBox “This form cannot be opened from the Navigation Pane.”, _vbInformation + vbOKOnly, “Invalid form usage”

Cancel = TrueExit SubEnd If

strText = Forms(cFormUsage)!cboCity.Text

If strText = “” Then

‘ If the City is empty, the user may have opened the form from the navigator

Trang 9

‘ while the other form is opened (thus it passed the above test)MsgBox “This form is intended to add Cities for the ‘“ & iForms(cFormUsage).Caption & “‘ form.”, _

vbInformation + vbOKOnly, “Invalid form usage”

Cancel = TrueExit SubEnd If

‘ If you use the following syntax to insert the new value,

‘ make sure that the user hasn’t entered an apostrophy (‘) in his text

‘ Of course there are many ways to add the recordDoCmd.SetWarnings False

DoCmd.RunSQL “INSERT INTO tblMayors (City) VALUES (‘“ & strText & “‘)“

Me.RequeryDoCmd.SetWarnings True

‘ Now point to the row just added and set the filter so the user can’t scrollSet rs = Me.RecordsetClone

rs.FindFirst “City = ‘“ & strText & “‘“

If Not rs.EOF ThenMe.Bookmark = rs.BookmarkMe.Filter = “[ID] = “ & Me.IDMe.FilterOn = True

End IfMe.Mayor.SetFocusEnd Sub

After the user indicates that she wants to edit the items in the list, the process will open frmMayorUpdatebecause it is the form specified as the value for the List Items Edit Form The Form_Openevent looks

at the cboCityfield in frmContactMayorand stores the text of that field in strText The code theninserts the City that doesn’t have a value associated with the Mayor’s name into tblMayors With therecord inserted, the code makes a copy of the recordset of the form, uses the FindFirstmethod to locatethat record, and moves to it using the Bookmarkproperty Finally it sets the focus on the Mayor namefield, so that it is easy for the user to enter the required data

Of course, you could accomplish that with a lot less code, as shown in the following snippet:

Private Sub Form_Open(Cancel As Integer)DoCmd.RunCommand acCmdRecordsGoToNew ‘ insert a new recordMe.City = Forms(“frmContactMayor”)!cboCity.Text

Me.txtMayor.SetFocusEnd Sub

This code simply goes to a new record on the form and copies the text from cboCityto the City field An advantage of this method is that you can then define the Mayor name field in tblMayorsasrequired and let Access determine if the data entered by the user is valid Then you don’t need theForm_BeforeUpdatecheck to make sure the user entered something in the Mayor name field

Now that you’ve worked through this example, take a moment to think about related business rules.This scenario has some obvious issues that would have to be addressed, such as the potential to have

Trang 10

multiple cities with the same name, the opportunity for people to use different spellings of the same city,and the need to be able to rename a city or mayor But it does provide a fairly elegant process to allowusers to add values.

If you prefer to write code (and most developers do), there is another way to allow the users to updatethe list of valid values available in a combo box It provides the means for you to validate the value andsaves you from having to create another form It’s the Not In Listevent, which you look at next

Not In List()

The Not In Listevent is triggered when the Limit To Listproperty is set to Yesand the userenters data that is not in the list It occurs independently of the settings for Allow Value List Editsand List Items Edit Formproperties, so you can use it to control how your application respondswhen invalid data is entered in a combo box

Because combo boxes are usually based on a look-up table, the following example offers the user achance to add a value that is not in the list To provide a friendlier dialogue with the user, it also demon-strates how to create a custom message box such as the one shown in Figure 10-12 As you can see, theuser tried to use “Entertainment” as the main category Because it isn’t in the list, he’s asked if he’d like

to add it

Figure 10-12

As a refresher, you can use the Limit To Listproperty to control what happens when a user entersdata into a control If the value is set to No, it places no restrictions on what is entered If the value is set

to Yes, several things can be triggered, including the following:

❑ The user is limited to what is in the list

❑ If other data is entered, users are asked to choose from the list

❑ Entering other data can trigger the NotInList event

Because combo boxes are typically based on a look-up table, the following example provides code tooffer the user a chance to add a value that is not in the list It also creates a custom message box

Private Sub cboMainCategory_NotInList(NewData As String, Response As Integer)

On Error GoTo Error_Handler

Dim intAnswer as Integer

intAnswer = MsgBox(“”“” & NewData & “”“ is not an approved category “ & vbcrlf _

Trang 11

& “Do you want to add it now?”, _vbYesNo + vbQuestion, ”Invalid Category”)Select Case intAnswer

Case vbYesDoCmd.SetWarnings FalseDoCmd.RunSQL “INSERT INTO tlkpCategoryNotInList (Category) “ & _

“Select “”“ & NewData & “”“;”

DoCmd.SetWarnings TrueResponse = acDataErrAddedCase vbNo

Msgbox “Please select an item from the list.”, _vbExclamation + vbOkOnly, “Invalid Entry”

Response = acDataErrContinueEnd Select

Exit_Procedure:

DoCmd.SetWarnings TrueExit Sub

Error_Handler:

MsgBox Err.Number & “, “ & Err.DescriptionResume Exit_Procedure

ResumeEnd SubThe NotInListevent comes with two parameters:

❑ NewData As String: Holds the value that is not found in your list

❑ Response As Integer: Provides three intrinsic constants:

❑ acDataErrContinue: Suppresses the standard error message

❑ acDataErrAdded: Suppresses the standard error message, and refreshes the entries inthe combo box

❑ acDataErrDisplay: Displays the standard error message

The NotInListevent property is literally asking, “This value is not in your table, so what should I do?”The example starts by telling Access to display a message box notifying the user that the name is not inthe list You use the vbYesNoconstant to provide buttons to get the user’s answer and the vbQuestionconstant to display the Question icon in the message box

The user can choose Yes or No If Yes (vbYes) is selected, the code adds the new value using the INSERTINTOSQL command that appends NewDatato the specified lookup table Because it is an append query,the standard append query warnings are triggered Docmd.SetWarnings Falseturns off these warn-ings, and later you use Docmd.SetWarnings Trueto turn them back on

Next, you need to set the Response, one of the intrinsic constants for the message box If the user hasresponded Yes, acDataErrAddedautomatically refreshes the combo box If the user chooses No, Response

is set to equal acDataContinue, which allows the user to continue without adding the item to the list.Either of those constants (responses) will suppress the default Access message The point here is that bycreating custom messages boxes you proivde a more informative and professional user interface

Trang 12

Field List

One of the values for Row Source Typethat hasn’t been mentioned yet is Field List Setting the RowSource Typeto Field Listmakes the values displayed in the combo box be the actual names of thefields (rather than the data contained in the field) found in the table or query specified in the row source.The Field Listvalue is most commonly used when you’re going to allow the user to select fieldnames to build a custom filter Of course, Access 2007 already provides some powerful new ways to fil-ter a form, so they may be a better alternative

But if your form has several fields, a drop-down field finder might help users quickly get to the fieldsthat they are looking for To create such a tool, first add an unbound combo box to a form In the combobox properties, set the Row Source Typeto Field List Then, in Row Source, enter the table orquery that is used for your form In the AfterUpdateevent of the combo box type the following code:

Private Sub cboSearchFields_AfterUpdate()

Dim strFieldChoice as StringstrFieldChoice = Me.cboSearchFields.ValueDoCmd.GotoControl strFieldChoice

End Sub

The user can select the field from the combo box and be taken straight to that field This code assumesthat the Nameproperty of the controls on your form are the same as the names of the fields in your RowSource However, that probably isn’t the case because you’ve implemented good naming conventions

so the controls have prefixes such as txt; in which case you’ll have to interpret the names The followingSelect Casestatement is an example of how to do that:

Private Sub cboSearchFields_AfterUpdate()

Dim strFieldChoice as StringstrFieldChoice = Me.cboSearchFields.ValueSelect Case strFieldChoice

The user will see the easy-to-interpret term CustomerFirstNamebut the code in the Select Casestatement switches the value to equal the actual Name property of the control: txtCustomerFName

Synchronizing Two Combo Boxes Using AfterUpdate()

It has become rather popular to synchronize combo boxes (often called cascading combo boxes) Thevalue selected from the first combo box updates the second combo box so that it contains only recordsrelated to the item selected in the first combo box In the following example (ComboBox.accdbin the

Trang 13

chapter download), the row source of the first combo box (cboMainCategory) is set to tlkpCategoryand the second combo box (cboSubCategory) needs the row source to display the values fromtlkpSubCategorywhere the foreign key for each subcategory matches the values of theMainCategoryIDs This is a common function when there is a one-to-many relationship between twotables In other words, each main category can have many subcategories (as one sales rep has many cus-tomers) but only certain subcategories are valid for a given main category.

One way to synchronize the combo boxes is to add the following snippet of code to the AfterUpdateevent of the first combo box (cboMainCategory):

Private Sub cboMainCategory_AfterUpdate()

‘ bind data to the second combo box based on the value selected

If IsNull(Me.cboMainCategory) ThenMe.cboSubCategory.RowSource = “”

ElseMe.cboSubCategory.RowSource = _

“SELECT SubCategoryID, SubCategoryName “ _

& “FROM tlkpSubCategory “ _

& “WHERE MainCategoryID = “ & Me.cboMainCategoryEnd If

End SubWhile the row sourceof cboSubCategoryis changed dynamically, the initial value ofcboSubCategorycan be set to equal nothing This ensures that the user cannot select a value from thesecond combo box unless there is a value selected in the first combo box If the user can scroll throughrecords on the form, you’ll also want to use this code in the form’s On Currentevent to make sure thatthe row source of the second combo box is reset every time the record changes For that, we used the fol-lowing snippet:

Private Sub Form_Current()

‘ bind data to the second combo box based on the value selected

‘ leave second combo box empty if there is nothing in the first combo box

If IsNull(Me.cboMainCategory) ThenMe.cboSubCategory.RowSource = “”

ElseMe.cboSubCategory.RowSource = _

“SELECT SubCategoryID, SubCategoryName “ _

& “FROM tlkpSubCategory “ _

& “WHERE MainCategoryID = “ & Me.cboMainCategoryEnd If

End Sub

Of course, there are a myriad other enhancements to add, such as having the drop-down lists display matically But for now, we’re focused on the functionality of synchronizing combo boxes Figure 10-13shows the property configuration for the second combo box, cboSubCategory

auto-Although only the value of the subcategory is displayed, there are two fields listed in the Row Source And if you open the Row Sourcequery builder, you will see that all three fields of the table,tlkpSubCategory,are included in the query You can understand why it is critical for the query toinclude the foreign key from tlkpCategorybecause that is the field that links the two combo boxes andprovides the filter for the second combo box list Comparing the SQL statement for the row source to the

Trang 14

query behind it is a good way to learn what makes this work so that you can apply the principles where You can also use this example to demonstrate that the value for the Column Countcan be based

else-on the Select statement in the Row Source (2 fields) or else-on the underlying query (3 fields) The criticalpart is to ensure that the bound columns and column widths match the field configuration

Figure 10-13

An alternative method for handling synchronized combo boxes is to code the row source of the secondcombo box to point to the value in the first combo box If the name of the form with the combo boxes isfrmMain, for example, instead of writing the preceding code, you could just make the Row SourceofcboSubCategorythe following:

Select SubCategoryID, SubCategoryName From tlkpSubCategories Where i

MainCategoryID = Forms!frmMain!cboMainCategory

One of the easiest ways to create that type of Selectstatement is to use the Query Builder for the RowSource After you have the correct properties, the trick is to make sure that the cboSubCategoryfield isrefreshed with the new set of valid values A simple Me.cboSubCategory.Requerystatement willwork For this example, you would put that code in the cboMainCategory_AfterUpdateevent andalso in the Form_Currentevent

The downside to this alternative method is that if you rename frmMain, you have to remember that youhard-coded the name in the cboSubCategoryrow source and that will have to be updated to use theform’s new name

Regardless of the method, you can use cascading combo boxes for a multitude of purposes This ple was simple, and it can easily be modified and incorporated into complex scenarios Remember thatyou can add additional rows to the row source and use them to auto-fill text boxes or as links to otherobjects You can also display additional fields in the combo box to assist users in selecting the correctrecord Keep in mind that the List Width can be wider than the control width, so there is ample room

exam-to briefly display data

Combo boxes are a great way to help speed up the data entry processes for your user In Access 2007,Allow Value List Editsand List Items Edit Formenable you to eliminate some of the code that

we used to capture data-entry errors Of course, you can still use the Not In Listevent to trap userinput and provide special handling The combo box is definitely worth spending some time learningabout so that you can leverage its versatility and benefits

Trang 15

BeforeUpdate Event

The BeforeUpdateevents can be used to validate user input, and as such, they are used most often onthe form or record rather than the field During data entry, users are typically allowed to fill in all theinformation for one record before they are prompted to resolve erroneous or missing But that’s notalways the case, so we’ll explain both approaches

The choice between using the Form_BeforeUpdateevent or the field_BeforeUpdateevent is oftenbased on the type of validation that’s needed In general, you use the field_BeforeUpdateevent forrequired data or when the field value does not depend on other values that are entered on the form Thefollowing code example shows a test for FieldOne, a required field

Private Sub FieldOne_BeforeUpdate(Cancel as Integer)

If Trim(FieldOne.Value & “”) = “” ThenMsgBox “You must provide data for field ‘FieldOne’.”, _vbOKOnly, “Required Field”

Cancel = TrueEnd If

End Sub

The Cancel = Truestatement prevents the user from moving from FieldOneto another field until avalue is entered in FieldOne By concatenating the empty string to the value of FieldOne, and usingthe Trimstring function, and then testing to see if the result is equal to the empty string, you take care ofthe situation in which Access returns a Null value when the user hasn’t entered any data in the field aswell as the situation where the user may have simply pressed the spacebar

You’ll want to use the Form_BeforeUpdateevent if your form is structured in such a manner that validvalues for one field depend on the value entered in another field The following code snippet shows acase where the value in FieldTwois required only when a value is entered in FieldOne You’ll recog-nize that this code does not address any aspects of preventing input unless a criterion is met That is adifferent business rule — one that is often handled by enabling and disabling controls

Private Sub Form_BeforeUpdate(Cancel As Integer)

If (IsNull(Me.FieldOne)) Or (Me.FieldOne.Value = “”) Then

‘ No action requiredElse

If (IsNull(Me.FieldTwo)) or (Me.FieldTwo.Value = “”) ThenMsgBox “You must provide data for field ‘FieldTwo’, “ & _

“if a value is entered in FieldOne”, _vbOKOnly, “Required Field”

Me.FieldTwo.SetFocusCancel = TrueExit SubEnd IfEnd IfEnd SubBecause the user may have been on a field other than FieldTwowhen she attempted to leave or save therecord, SetFocusis used to move the cursor to the field that has the error The Cancel = Truestate-ment prevents the record from being saved Otherwise, the user may get multiple error messages and be

Trang 16

able to address only the last message displayed.The Exit Sub statement isn’t essential, but it’s a goodcoding technique to include the Exit Substatement at any point where you are reporting an error And,

in many cases, you may come back to this code to add another validation check

So, your choice of Form_BeforeUpdateversus field_BeforeUpdatedepends on the type of validationperformed Use the field_BeforeUpdatewhen you want to give the user immediate feedback aboutinvalid data Use the Form_BeforeUpdatewhen you have to perform cross-field validation checks

The next example uses the On Clickevent of a command button First you need a simple form for dataentry On the Property Sheet, set the form’s properties as shown in the following table

Private Sub cmdCloseForm_Click()

Dim intAnswer As Integer

intAnswer = MsgBox(“Do you want to save this record?”, _

vbYesNo + vbQuestion, “Save or Cancel”

Select Case intAnswer

Case vbYes ‘ run through the validation

If (IsNull(Me.FieldOne)) or (Me.FieldOne.Value = “”) ThenMsgBox “You must provide data for field “FieldOne”.”, _vbOKOnly, “Required Field”

‘ direct user to empty field and exit subMe.FieldOne.SetFocus

Exit SubEnd If

If (IsNull(Me.FieldTwo.Value)) or (Me.FieldTwo.Value = “”) ThenMsgBox “You must provide data for field ‘FieldTwo’.”, _vbOKOnly, “Required Field”

Me.FieldTwo.SetFocusExit Sub

End IfCase vbNo ‘ give user a way out

Me.UndoEnd Select

DoCmd.Close acForm, Me.Name

End sub

Trang 17

This is similar to the Form_BeforeUpdateexample In this case, when the Closebutton is clicked, the user is asked if he wants to save the record If he selects Yes, the code runs through the steps in theSelect Caseprocess and stops the form’s Closeoperation only if a field is empty In that case, thefocus is set to the field in error and the code exits the procedure (Exit Sub) If the user selects No, thechanges to the record are undone using Me.Undoso that no changes are saved If the process makes itsuccessfully through the Select Casechecks or if the changes are undone, the form closes as expected.Keep in mind that if a method other than this Closebutton is used to close the form, this validationprocess will not occur Of course, you could institute processes to cover contingencies

One more point about testing for data in the field: The examples use IsNulland a test for the emptystring and trimming and concatenating the empty string Because Access trims spaces off of data entered

on the form, you’re not required to test for a single space entered in a field The code used in the field_BeforeUpdateexample is just a technique used to simplify validations as well as ensure that Accessbehaves the way you expect it to

Saving E-mail Addresses Using Textbox AfterUpdate Event

Access 2007 is smarter about the way it stores website and e-mail addresses Data in a hyperlink field isautomatically evaluated and tagged The value is tagged with httpfor websites or mailtofor e-mailaddresses

In previous versions, the Hyperlink data type stored e-mail addresses as Web addresses (http://customeremail@msn.com, for example), so when you clicked the link, it opened Internet Explorer andtried to find the website — not at all helpful (One way to fix this is to right-click the link, select EditHyperlink, and change httpto mailtoby selecting e-mail Address in the dialog box Be sure to copythe e-mail address first because you have to add it again.)

However, you want the user to add an e-mail address and have it correctly stored so that he can laterclick the link and open a new email message The following code ensures that the initial input is stored

in the correct format You can try it with a simple form to add customers and e-mail addresses; add thiscode to the AfterUpdateevent of the EmailAddressfield, txtEmailAddress

Private Sub txtEmailAddress_AfterUpdate()

If Not IsNull(Me.EmailAddress) ThenMe.EmailAddress = Replace(Me.EmailAddress, “http://“, “mailto:”)End if

End SubThe first line checks to see if there is an e-mail address If data is in the e-mail address field, it uses theReplacefunction to replace httpwith mailto The code makes this change on-the-fly without the usereven knowing

This works great for sending e-mail to only one person What if you need to send one e-mail to manypeople? Assume there is a Contacts table and that e-mail addresses are stored in a field with a Text datatype rather than as a hyperlink On your form, you can add a list box named lstContacts, as shown inFigure 10-14

Trang 18

Figure 10-14

The list box RowSourcewould equal tblContacts, so the column count and column width need to beset to display the first name, last name, and e-mail address for each record You’ll need to set the list boxproperty named Multi Select(on the Property Sheet’s Other tab), which has three options:

❑ None: Select one item at a time from the list

❑ Simple: Select blocks of items from the list by selecting one item and then holding down theShift key while highlighting the other items

❑ Extended: Select multiple items by holding down the Ctrl key and then randomly selectingitems

For this example, use Extended

Next, you need a command button named cmdSendEMail Add the following code to its On Clickevent

Private Sub cmdSendEMail_Click()

Dim emName As String, varItem As Variant

Dim emailSubject as String

Dim emailBody as String

emailSubject = “Confirmation Email”

‘note the carriage return line feeds to insert a blank line between

‘the greeting and the message body Also, there is no need for the

‘ & at the beginning of the second and third lines because we used a

‘ comma after the greeting – just before the close quote on the first line

emailBody = “Dear Customer, “ & vbCrLf & vbCrLf & _

“We are sending this email to let you know that your request has been “ & _

“confirmed Please allow 2-3 weeks for shipping.”

On Error GoTo cmdSendEMail_Click_error

‘if there are no selected items (Contacts) in the list, exit

If Me!lstContacts.ItemsSelected.Count = 0 Then

Exit Sub

For Each varItem In Me!lstContacts.ItemsSelected

‘char(34) adds quotes to Me!lstContacts

‘Then add a comma to separate the selected items

Trang 19

emName = emName & Chr(34) & Me!lstContacts.Column(4, varItem) & Chr(34) & “,”Next varItem

‘remove the extra comma at the endemName = Left$( emName, Len(emName) - 1)

‘send the messageDoCmd.SendObject acSendNoObject, , , emName, , , emailSubject, emailBody, _True, False

cmdSendEMail_Click_error:

If Err.Number = 2501 ThenMsgBox “You just cancelled the Email You’ll have to start over.”, _vbCritical, “Alert!”

ElseIf Err.Number > 0 thenMsgbox “Error sending email.” & vbCrlf & Err.Description, _vbCritical, “Send Error”

End IfEnd SubThis code starts by declaring the e-mail name (emName), subject of the e-mail (emailSubject), and thee-mail body (emailBody) as String, and a variable to use the items in the list as Variant (varItem) Next,

it initializes the subject and body variables with string text The first Ifstatement checks the count of thenumber of selected items in the list, and if the count is zero, exits the routine

The For Eachloop processes each of the items the user has selected Because Multi Selectis set toExtended, the user can use the Ctrl or Shift keys with the mouse click to select items in sequence or randomly

With tblContactsas the list box’s Row Source, each field defined in tblContactsbecomes a column

in the list box You can use the Columnproperty to access the value of each column in the list box (Thisapplies to multicolumn combo boxes as well.)

As with most arrays in VBA, the index for the Columnproperty starts at zero; thus the first field in

tblContactsis the 0 column in the list box In this example, the e-mail address is the fifth field defined in tblContacts, so in code this would be referred to as .Column(4).

When building emName, a comma separator is added for each selected item in the list box That leavesyou with an extra comma that you have to strip off after all of the e-mail addresses have been collected

With all the items collected, use DoCmd.SendObjectwith the following arguments to create the e-mail:

DoCmd.SendObject acSendNoObject, , , emName, , , emailSubject, emailBody, _True, False

Selecting acSendNoObjectmeans that the e-mail will not have an attachment because there is no object(form/report) to send After that, the commas are the place markers for the following nine arguments:

[ObjectName], [OutputFormat], [To], [Cc], [Bcc], [Subject], [Message], _[EditMessage], [TemplateFile]

Trang 20

As in the example, [ObjectName]and [OutputFormat]can be left blank; [To]will be emName(theselected items from the list box); [Cc]and [Bcc]can be left blank; [Subject]will be the

emailSubjectvariable; [Message]will be emailBody; [EditMessage]is Truejust in case you need

to edit the message; and [TemplateFile]is False

Your users now have the option to send an e-mail to one contact or many contacts by simply selectingthe recipient(s) from the list box Figure 10-15 shows an e-mail created by this example

Now Access 2007 has streamlined the process by providing the bridge between Access and PDF utilities,particularly if you download Microsoft’s free Add-in “Microsoft Save as PDF or XPS.” Instead of work-ing with complicated code, you’ll have a Save As PDFbutton on the Ribbon Along with PDF, youprobably discerned that this add-in also offers the option to save in XML Paper Specification (XPS) fileformat To take advantage of this, you’ll need to download and install the file SaveAsPDFandXPS.exe

A link to the web is provided in the Access Help files You can find it by typing XPS or PDF in the Search

text box or by going to Microsoft.com and searching for SaveAsPDFandXPS.exe

Trang 21

People will still need a PDF viewer to open PDF files But viewers are commonly available as free loads, such as the Adobe Reader Windows Vista is required to view XPS files, but here again, a viewermay become available

down-To work with PDF and XPS file formats programmatically, Access has two new constants They arenamed in the following table

To see all of the constants, open the Object Browser and search for acFormat

Both DoCmd.SendObjectand DoCmd.OutputTouse these constants in their arguments to define the mat The following code snippets show three different ways to use the PDF file format There are minordifferences in the functionality For example, you’ll notice that using OutputToallows you to specify theexport quality

for-DoCmd.SendObject acSendReport, “MyFormName”, acFormatPDF, [To], [Cc], [Bcc], _[Subject], [MessageText]

DoCmd.OpenForm “FormName”, , , , , , “OpenArgs value”

In this case, the value of the OpenArgsproperty would be “OpenArgs value”

Because the Openevent triggers whenever a form opens, you can include the OpenArgsparameter onthe OpenFormmethod to provide information that can be used even before the user sees the form

An excellent application of this is to ensure that the form is opened by code and only under specific ditions For example, you may have a form that should be opened only by certain users Because userscan open the form from the Navigation pane, it’s possible for any user to open a form — unless you

Trang 22

con-have implemented programmatic restriction One approach is to use the following code in the form’sOnOpenevent to prevent it from being opened from the Navigation pane.

Private Sub Form_Open(Cancel As Integer)

If Me.OpenArgs() <> “Valid User” Then

MsgBox “You are not authorized to use this form!”, _vbExclamation + vbOKOnly, “Invalid Access”

Cancel = TrueEnd If

DoCmd.OpenForm “frmRestricted”, , , , , , “Valid User”

Another time to use OpenArgsmight be when you automatically populate information in the formyou’re opening Suppose you have a combo box that requires the user to select a valid value If the userenters an undefined value, you may want to pop up a form to collect information about the value justentered — you might want to pass the text that the user entered to the form that you’re opening so it ispopulated in the appropriate field on the pop-up form Clearly you can utilize the OpenArgspropertyfor a variety of purposes

You may prefer the new List Items Edit Form property, discussed earlier in the chapter, over

writing your own code to pop up a form to collect a new value for the combo box.

Be aware that once the form is opened, the value of OpenArgsdoes not change on subsequent tions of the OpenFormmethod For that reason, you may want to check to see if the form is open beforeyou execute OpenForm The form’s IsLoaded property can tell you if a form is loaded

execu-IsLoaded()

There are numerous situations in which you might want to know if a form is open, or loaded The lowing scenario will help illustrate when and why to use the IsLoadedproperty In this situation, a usermay have several forms open, including frmEvent, which lists the names of everyone scheduled to par-ticipate in an event Say that one of the participants cancels, so the user opens a participant form andclicks a command button (cmdCancel) to remove that participant from the event Because the eventform is already open, the data that it displays cannot be updated without some action being taken Oneapproach is to include code behind cmdCancelon the participant form to see if the event schedule form

fol-is open by using the event IsLoaded If the form, frmEvent, is not open, the regular cancellationprocess continues If frmEventis open, the appropriate actions — such as closing the event scheduleform or requerying the event form after canceling the appointment — should also be included in thecode behind cmdCancel The snippet below is an example of code that could be used in this scenario

Trang 23

If frmEvent.IsLoaded Then frmEvent.Requery ‘add additional actions End If

Consider another example: Perhaps you want users to be able to open a form in a specific way, such asonly from a specified form, and definitely not from the Navigation pane In that case, you may also firstwant to determine if the form is already open The following example uses the IsLoadedproperty to see

if a particular form is open before it continues code execution In the code, CurrentProjectrefers tothe open database and for illustration purposes, frmEventis used again:

Private Sub cmdCancel_Click()

If Not CurrentProject.AllForms(“frmEvent”).IsLoaded ThenMsgbox “Cannot cancel while ‘frmEvent’ form is open.”, _vbInformation + VBOkOnly, “Cancel Invalid”

Exit SubEnd IfEnd Sub

As you can see, the IsLoadedproperty is a convenient and useful way to determine if a form is open

On Timer ()

The On Timerevent property can identify a process that can be triggered at timed intervals It is used inconjunction with the TimerIntervalproperty, which stipulates the frequency of the event When speci-fying the value for the TimerIntervalproperty, the time is entered in milliseconds (1/1000 of a sec-ond), so entering 5000will trigger the event at 5-second intervals

You may be wondering when you might use a timer How about to close an application’s splash screen(a form that displays for a few seconds as the application opens and then is automatically closed) or toclose a message that you only want to display for a few seconds? For instance, you could display yourcompany logo and contact number during the few seconds that it takes for an application Of course,you don’t want to leave the splash screen open for too long — four seconds seems like a reasonable time.Let’s assume that the application has an AutoExec macro that runs when the database opens and thatone of the actions includes a macro to open the splash screen

To set the timer for any form, open the form’s Property Sheet to the Event tab Scroll down to TimerInterval The property default is 0; for our example, we would change it to 4000, the equivalent of 4 sec-onds Then in the OnTimerproperty, select Event Procedureand open the IDE to add the following code

Private Sub Form_Timer()DoCmd.Close acForm, Me.NameEnd Sub

When the splash screen (our form) opens, the Timer Interval starts counting down When the timerreaches the specified interval time, 4 seconds, the code runs In this event, DoCmd.Closewill close thesplash screen This scenario has worked well in the past, but it can cause problems if the database is

Trang 24

opened in disabled mode by Access 2007 If the VBA is disabled, the timer event code never runs and thesplash screen is not closed To avoid risking that situation, you can close a splash screen by using amacro for the OnTimerevent (One of the reasons that I’m mentioning this is because you may workwith MDB files that use VBA to close a splash screen If so, you need to either replace the code or ensurethat the databases are opened fully enabled, from a trusted location and/or are digitally signed.)

To create the macro to close the splash screen, set the OnTimerproperty to a macro by clicking the sis and selecting the Macro Builder In the Action column, select Close; the lower pane displays theAction Arguments For the Object Type, select Form; from the Object Namelist, select the form that

ellip-is used as the splash screen; and for Save, select No From the Close group on the menu bar, select Save

As, on the first line enter a name for the macro, and then click OK

Another example of using the OnTimerevent is in a multi-user environment where the users are ing meetings and setting scheduled times for the clients of the Sales staff Obviously, it is crucial to knowwhen sales people are available and to not book them twice for the same time period Because severalpeople have the potential to be booking appointments at the same time, the form that displays andrecords the appointments needs to be requeried constantly For this situation, the form’s Timer

record-Intervalis set to 6 seconds (6000) and the form’s Timerevent contains the following code:

Private Sub Form_Timer()

Me.Requery

End Sub

That allows the form to update on its own rather than requiring the user to click an update button.Because you need to get additions and deletions, the form uses Requeryinstead of Refresh, whichwould retrieve only changes to existing records As shown in Figure 10-16, the main form consistentlydisplays the appointment time blocks, and the subform uses a continuous form to show the number ofavailable openings for each wave for every scheduled date The timer event runs a query to refresh thedata for the subform When an appointment is booked, the number for the respective date and wave isdecreased And, thanks to the Timerevent, the display will be updated for any user that has the sched-ule form open And, to see the details for any day, they merely double-click the record selector to openthe appointment schedule to review or change an appointment

Figure 10-16

Trang 25

Keep in mind that the timer event can create a significant performance hit And, if users are manuallyentering data, there are likely better alternatives — 2 seconds wouldn’t be adequate for data entry.However, in situations where you are merely displaying information, such as the number of appoint-ments or openings in a time slot, then a short timer interval can be an effective mechanism to udpatedata in open forms.

Late Binding

Another timing issue that can have significant impact on performance is whether the form is using early

or late binding Basically, this refers to when the record (or recordset) is created for a form or otherobject Late binding typically involves filtering, which reduces the size of the recordset and also allowsthe developer greater control of the record source

When you add fields to your form, the text boxes that are created become bound text boxes That is, thetable or query fields are bound to those text boxes and when the form opens, the data from your table orquery binds to the controls on your form to display data If the form has only a few controls or is a light-weight form, you might not notice a performance hit when the form loads But loading all the possiblerecords behind a complex form that has numerous controls, combo boxes, subforms, and complicatedcriteria can create a drastic slowdown

One option is to load the controls on demand or after receiving criteria to filter the data rather thanwhen the form opens The following code snippet uses the Form_Openevent to set the RowSourceandSourceObjectsof a combo box, list box, and subform to an empty string (“”) Obviously, if the intent

is for these to be unbound objects, it is better to save the form without record sources for those controls.But a form may inadvertently have been saved with values stored for the controls, so the followingexample demonstrates how to remove the values Speaking of saving the form, a technique commonlyused with subforms is to save the SourceObjectas an empty string to prevent the subform fromattempting to acquire data as the form opens

Private Sub Form_Open()Me.cboMyComboBox.RowSource = “”

‘OrMe.lstMyListbox.RowSource = “”

‘OrMe.fsubMySubform.SourceObject = “”

End subAfter the form opens, you will need a way to fill the RowSource Common techniques are to initiate theprocess from a command button or by selecting a value from a combo box or list box

Another method is to use the Option Group control to load data to other controls The Option Groupcontains controls that include radio buttons, check boxes, and toggle buttons Each radio button, checkbox, or toggle button (depending on the type of Option Box controls you chose) can be assigned a valuethat is set whenever that control is selected That numeric value can be used in a Select Casestate-ment to populate the list box or subform

In the next example, you’ll populate the list box with a list of forms and reports in your database byselecting a numeric value from the Option Group You can use the lists for several purposes, even as thebasis for a switchboard (menu form), as shown in Figure 10-17 To get started, you need a table to hold

Trang 26

the names of the forms and reports that are in the database Some developers use the hidden systemtables that store the object names Although that’s functional, it isn’t pretty because they return theactual object names, which the user might not recognize It is much better to provide the user with con-cise yet descriptive names rather than have them decipher terms that starts with tags such as frmor rpt.

Figure 10-17

So, create table tblMyObjectswith the following fields:

❑ userObjectName: The name of the form or report that the user will see

❑ devObjectName: The actual name of the form or report

❑ devObjectCode: The code number (created by you) assigned to that object For this example,

1 = forms and 2 = reports

Your table should resemble the one used in the query shown in Figure 10-17 Then, you’ll need to late the table with data (That will be a manual process, unless you just want to follow along using theLateBinding.accdbfile that’s included with the download for this chapter.)

popu-Next, create two queries using all of the fields in tblMyObjects Name the first query qryFormName,where devObjectCode = 1, and name the other query qryReportName, where devObjectCode = 2 Based on those criteria, qryFormNamewill retrieve all the forms and qryReportNamewill retrieve all the reports Sicne the first row contains the ID field, you need to clear the Showcheck box so thatuserObjectNameto be the first visible field To display the names alphabetically, set Sort order for thisfield to be Ascending Each query has the same four fields; the critical differences are the name of thequery and the criteria Because these queries are the foundation for several more steps, it might be pru-dent to confirm that your query looks similar to the example in Figure 10-18

You’ll use these queries to populate a list box to display forms and reports that a user might want to use Although the queries use all four fields, only the userObjectNamefield will be displayed ThedevObjectCodeis used for selecting the items and the devObjectNamecan be used to programmati-cally open the object You’ll start the process by creating an unbound form named frmSwitchboard

Trang 27

with an option group and a list box (Figure 10-17, above, showed the switchboard form with the OptionGroup named grpSwitchboardlabeled My Switchboard and the list box displaying user-friendlynames of selected forms.)

Figure 10-18

Begin by adding an option group to the form The Option Group Wizard prompts for label names On

the first line type Forms and on the second line type Reports Next, select Forms as the default option.

You then can assign values that match the criteria from the queries that you created: Formsis assignedthe value of 1 and Reportsa value of 2 You’ll use these values to write code for the Option Group’sAfterUpdateevent You can create additional buttons for other types of actions, or create customgroups of forms and reports, such as by department The final question from the wizard is what to namethe caption for the group Call it My Switchboard

Now, open the control’s Property Sheet and change the name from frame0to grpSwitchboard Youneed to add code to the AfterUpdateevent, but first create an unbound list box that the code will popu-late So, add a list box to the form but close the wizard without answering any questions That creates anunbound list box Use the Property Sheet to rename this lstItemsand to add the following code to thegrpSwitchboard AfterUdpateevent

Private Sub grpSwitchboard_AfterUpdate()Dim strRST as String ‘RowSourceTypestrRST = “Table/Query”

Me.lstItems.RowSourceType = strRSTSelect Case Me.grpSwitchboard.ValueCase 1

Me.lstItems.RowSource = “qryFormName”

Case 2Me.lstItems.RowSource = “qryReportName”

End SelectEnd Sub

Trang 28

This code sets the row source type to Table/Query for the list box lstItems, which enables you to usequeries to populate the display If grpSwitchboardhas value 1 selected, the results from qryFormNameare shown If the value 2 is selected, the data from qryReportNameis displayed If additional objects oroptions are added, they need similar code This is great for viewing a list of forms or reports, but it lacksthe capability to open them as you would from a switchboard Before that can work, you’ll need to setsome column properties on the list box so that the values from the query (row source) are available tothe command button Again, open the Property Sheet for lstItemsand on the Format tab, set the col-umn count to 3and the column widths to 1”;0”;0”.This correlates to the columns for the name theuser sees, the actual name of the object, and the code that identifies it as a form or a report.

Now, you have an option group that controls what will be displayed in the list box, and you’ve also ulated that the default is to select Forms So now, when frmSwitchboardopens, it appears that Formsare selected, but nothing is listed in the list box You rectify that by adding the following code to theform’s Openevent

stip-Private Sub Form_Open(Cancel As Integer)

‘When the form opens, load the list box with information

‘based on the default option group selection

Call grpSwitchboard_AfterUpdate

End Sub

And finally, you add functionality to open a form or report by creating a command button that will openwhichever object is selected Add the command button to the form and close the wizard withoutanswering any questions (because this will open both forms and reports, you’ll write your own code).Use the Property Sheet to rename it cmdOpenObject For the On Clickevent of cmdOpenObject, addthe following code:

Private Sub cmdOpenObject_Click()

Dim varCode as Variant

Dim varObjName as Variant

‘ Make sure that an item is selected before attempting to display it

If Me.lstItems.ItemsSelected.Count = 0 Then

MsgBox “Please select items to display.”, vbInformation + vbOKOnly, _

“List Items Required.”

Exit SubEnd If

varCode = Me.lstItems.Column(2)

varObjName = Me.lstItems.Column(1)

Select Case varCode

Case 1 ‘open the formDoCmd.OpenForm varObjName Case 2 ‘open the report in preview modeDoCmd.OpenReport varObjName, acPreviewEnd Select

End Sub

Trang 29

The process is predicated on an item being selected from the list, so this code starts with the IFment and message box Once the item is selected, you can extract the data necessary to display a form orreport To do that, start by declaring two variables — one for the devObjectCode(varCode) field andone for the devObjectName(varObjName) field (the actual name of the object) Once those variables areset, you evaluate varCodewith a Select Casestatement and use DoCmdto open a form or report usingthe name from the varObjNamevariable You would also want to include additional error trapping, such

state-as for reports with no data Figure 10-19 shows the form in Design view with the lstItemsPropertySheet open (This chapter’s download code includes this example, LateBinding.accdb.)

Figure 10-19

On Click(): Open a Form Based on a Value

on the Current Form

Opening a form based on a value stored on another form is a common practice The functionality applies

to a myriad of tasks, from listing all of the orders for one customer to gaining a bigger picture by paring the total purchases of one customer to other customers in the region It also works well fordrilling into details, such as searching a contact list and then clicking a record to see the detail informa-tion about that person

com-For this example, you drill into data to get information about the orders of one customer You’ll use twoforms, frmCustomerand frmOrder, which display information about customers and orders respec-tively You may want to use the Northwind database to work through this exercise For now, just say thatyou already have the two forms, so you just need a way to integrate their operations All it takes is acommand button (cmdShowOrders) on frmCustomerand some VBA code

The pieces that have to be tied together are the field names that correspond between frmCustomerandfrmOrder Typically, the table for each of these entities contains the field called CustomerIDwith anAutoNumber data type You also need the name of the control that is bound to the CustomerIDfield onfrmCustomer; in this case, that’s called txtCustomerID On frmCustomer, add a command button.Close the command button wizard when it opens and use the Property Sheet to name the buttoncmdShowOrders For the On Clickevent, include the following code snippet:

Private Sub cmdShowOrders_Click()

If Not Me.NewRecord ThenDoCmd.OpenForm “frmOrder”, _

Trang 30

WhereCondition:=”CustomerID=” & Me.txtCustomerIDEnd If

End Sub

If the tables do not follow good naming conventions, you may need to include brackets around the fieldnames Such is the case with the Northwind database, where the space in the field name requires brack-ets, so the line would look like the following:

DoCmd.OpenForm “frmOrder”,_

WhereCondition:=”[Customer ID]=” & Me.txtCustomerID

Using this process, you can add that code configuration to any form to have it open another form to therelated record(s) Again, the critical factor is the dependency or relationship between the record sources.You will recognize this type of relationship on the subform Property Sheet, using the Link Child Fieldsand Link Master Fields

The line If Not Me.NewRecord Thenreferences the NewRecordproperty, which is a Boolean value

It signifies whether the user is on the New Record of the form NewRecordis more than just a newrecord — it is the form’s property, because in Access, all records are added on the New Record Youcheck the NewRecordproperty because when a customer is just being entered there obviously shouldnot be any existing orders; thus there is no need to open the Orders form

The OpenFormmethod opens the form called frmOrderand issues a Whereclause to specify whichrecords to display in the form That clause is often called a filter or a query because it limits the form’srecord source to only those records that meet the criteria In this example, the criteria restrict the data tothose with customer IDs that match the value in the text box called txtCustomerID For clarification,the field name on the left of the equal symbol (=) refers to the field on the opened object The field name

on the right refers to a field on the calling object In plain English this says to open the Order form toshow the orders for the current customer on the Customer form

Suppose that each order has one invoice printed for it When you are viewing the Order record, you canprint the invoice for the order The code is nearly identical, except that a Report is being opened in PrintPreview:

Private Sub cmdShowInvoice_Click()

If Not Me.NewRecord Then

DoCmd.OpenReport “rptInvoice”, WhereCondition:=”OrderID=” & Me.txtOrderIDEnd If

Figure 10-20 shows the portion of the query grid with the criteria used to filter the records as you didpreviously with the command button cmdShowOrders The query is saved as qryCustomerOrders You

Trang 31

could use it as the record source of the form frmOrderand it would open to display the orders for thecustomer selected on the form frmCustomer.

Figure 10-20

The following code shows two ways to use queries to filter data (with one table) using one form

Docmd.OpenForm “frmOrder”, “qryCustomerOrders“, , acFormPropertySettings, acWindowNormal

The second example uses the Docmd.OpenFormstring but instead of using the WhereConditionment, it uses the FilterNameargument Using a query (qryCustomerOrders) for the FilterNameactually overrides a form’s record source (in this case tblOrders)

argu-Multiple Form Instances

There are situations in which the user needs to compare two or more records from the same table Forexample, you may need to compare Order 1 to Order 2 or Supplier1 to Supplier 2 or Supplier1 toSupplier2 and to Supplier3 This can be accomplished by opening the same form multiple times

Let’s say a database consultant is creating an Access solution for a client The consultant is in a bind andneeds to subcontract parts of the project to one of her fellow developers She checks her database to viewher contact list She opens the contact list form (frmContactList, Figure 10-21) and selects two of therecords (or contacts) from her list so that she can compare their specialties to find the best match for boththe client and project

Trang 32

Figure 10-21

The form that’s used to view the details is named frmContactDetailsand it is opened twice so thatthe consultant can do a side-by-side comparison of the candidate’s strengths as relayed by the Notesfield (see Figure 10-22) (The figure looks a bit crowded, but most screens display two complete records.)

Figure 10-22

Opening the same form multiple times is referred to as multiple instances because every time the same

form is opened, Access has to create or instantiate a new instance of the form The default process is toallow one instance of a form and merely change the record source, so it requires a little code and somepreplanning to show multiple instances of the same form

For this example, use the file TContacts.accdb(in the download for this chapter) The exercise startswith a split form, but you can achieve the same functionality using a regular form The critical part is to

Trang 33

have two forms, one for your list and the other to open multiple times to show and compare details Thisexample also demonstrates using a drill-through procedure to open the details form Again, this worksequally well for opening forms or reports.

The code can be used wherever you require this type of functionality First you create a new module and

a global variable to hold the form objects in a collection:

Option Compare DatabaseOption Explicit

Private mcolFormInstances As New Collection

In the collection name, mcolFormInstances, mis for module level and colrepresents collection.

The collection will hold the forms as they are created Next, you create a function that allows the user toopen the same form multiple times:

Function OpenFormInstance(FormName As String, Optional WhereCondition As String)

‘Declare the form nameDim frm As FormSelect Case FormNameCase “frmContactDetails”

Set frm = New Form_frmContactDetailsCase Else

Debug.Assert FalseEnd Select

If WhereCondition <> “” Thenfrm.Filter = WhereConditionfrm.FilterOn = True

End If

‘make the form visiblefrm.Visible = True

‘ Need to add a reference to the form so that it doesn’t

‘ immediately close when the form variable goes out of scope

mcolFormInstances.Add frmEnd Function

The function begins by declaring two parameters, FormNameand WhereCondition FormNamewill holdthe name of the form you need to create WhereConditionwill contain a string expression that allowsthe form to display the correct record You’ll call the function from the Contact form frmContactListand fill these parameters later

A variable is declared for the form object:

Dim frm as FormThe Select Casestatement evaluates the value in the FormNamevariable If the value equalsfrmContactDetails, then the variable frmis set to equal a new Contact Details form To use this func-tion for another form, such as to review details about the projects that a contact has worked on, justinsert another case in the Select Casestatement before the Case Else

Trang 34

An If Thenstatement evaluates the value of the WhereConditionparameter Because there’s always

a value for the WhereCondition, the form’s filter property equals its value With a value being supplied

to the filter property, you need to turn on the filter with the following line of code:

frm.Visible = True

mcolFormInstances.Add frm

Let’s go to frmContactListand examine the OpenFormInstancefunction With frmContactListopen in Design view, you use the ID field’s On Clickevent to place the OpenFormInstancefunction.You also use the ID field in the expression for the WhereConditionparameter There are at least fourways to make this function work Three of the possibilities use code, as the following examples show,and the fourth uses an embedded macro

Private Sub ID_Click()

‘1st example

Dim FormName As String

Dim WhereCondition As String

FormName = “frmContactDetails”

WhereCondition = “[ID]=” & Me.ID

‘Or

‘WhereCondition = “[ID]=” & Screen.ActiveControl

Call OpenFormInstance(FormName, WhereCondition)

Notice that you don’t use the Docmd.OpenFormto open the details form Instead, the form is ated by declaring the form object and setting it to equal the form specified in FormName The Filter prop-erties are set to accept the expression from the WhereCondition, the form is made visible, and the forminstance is added to the collection that was created to hold each instance of this form

Trang 35

instanti-As mentioned, the fourth method uses a macro This example uses the same function, OpenFormInstance,

to open multiple instances of the forms To create a new macro, select Macro on the Ribbon’s Create tab.You should have a macro object tab named Macro1 Right-click that tab and select Save Change the name

to dt(for drill-through) With the blank macro still open, click the buttons Conditionsand Macro Names

in the Ribbon Those columns, which are hidden when you first open a new macro, now display, as shown

in Figure 10-23

Figure 10-23

Start with the Action column and select StopMacro That will stop the macro from running once a dition is met Next, create named macros for each of the forms you want to drill to (The process anddetails are coming up in a moment, but at the end of the exercise, your macro should look similar to theone in Figure 10-24.)

con-Figure 10-24

Briefly, you start by naming the next macro Contacts In the Condition column, supply an expressionthat, when evaluated, will equal True If the condition is True, then perform the action specified in theAction column (RunCode) RunCoderequires arguments or action arguments to run the code In thiscase, you provide the function name OpenFormInstanceand fill in the parameters just like the ones inthe code behind

With the following steps, you create macros that provide two approaches to drill into the details

1. The first example uses the active control as the record ID to drill into data In the Macro Name

column, type Contacts For the condition, type:

Not IsNull([Screen].[ActiveControl])

Trang 36

2. The Action column has a drop-down list; select RunCode.

3. The lower pane will help prompt for action arguments Click on the ellipsis to open the

Expression Builder and then select the function that you created, OpenFormInstance

4. The Code Builder assists with syntax, but the key is to include the whereclause as you did inVBA Alternatively, you can type the arguments directly into the Arguments field The com-pleted argument will look like this:

OpenFormInstance(“frmContactDetails”,”[ID]=” & [Screen].[ActiveControl])

5. The second example matches the record ID, so name the macro ContactsByID The condition is:

to set the Property Sheet for that event to the sub-macro name Just remember that for every form that isadded to the macro, you must change the Select Casestatement in the OpenFormInstancefunction

to add a case statement for the new form, as shown in Figure 10-25

Figure 10-25

Trang 37

The macro name identifies each macro within the macro group In the Property Sheet for the ID field’s

On Clickevent, select the name of the macro instead of selecting Event Procedure The Property Sheetoffers auto-complete for macro names so as soon as you type dt, you get a list of all the named macros inthe dtmacro group See Figure 10-26

Remember that a macro group often contains several macros but it’s still called a macro; each named action or series of actions in the group is also called a macro The terminology can be a bit confusing, but macros are still likely to evolve into popular developer tools.

Figure 10-26

Displaying Data in TreeView and ListView Controls

One powerful feature of Access development is the capability to add an ActiveX control to a form orreport ActiveX controls are additional components that provide their own user interface, properties, andmethods Adding a control such as TreeView, ListView, or Calendaris a great way to add functional-ity to your applications with minimal effort on your part ActiveX controls are not without cost, how-ever They must be installed on each computer to use them In many cases, this means you must have alicense to distribute a particular control to other computers

The TreeViewcontrol is used to display data that exists within a hierarchy of information A hierarchycan involve any data contained within a one-to-one or one-to-many relationship For example, in theNorthwind database, Customers have Orders, and Orders have line items that are known as OrderDetails In this section, you’ll see how to add a TreeViewcontrol to a form and how to populate and display the information based on user interaction with it You’ll also take a look at the ListViewcontrol,and see an example of responding to an event in the TreeViewcontrol to fill the ListViewcontrol

Let’s say that you are tracking students and classes for a small college Classes at the school are rized into departments In this example, you display the departments along with their classes in aTreeViewcontrol, and the students in a given class in a ListViewcontrol

catego-To begin, create a new blank form The form does not need to be bound to a table or query because you willpopulate data in the TreeViewcontrol Once you create the form, click the Insert ActiveX Controlbutton on the Ribbon The Insert ActiveX Control dialog box displays, as shown in Figure 10-27 For thisexample, you use the TreeViewand ListViewcontrols from Visual Basic 6, SP 6 Name the controlstvwClassesand lvwStudentsrespectively

Trang 38

Figure 10-27

In the dialog box, select the desired control to add it to the form ActiveX controls have properties thatare displayed in the standard Access properties window, but many also have another set of customproperties that were added by the author of the control Figure 10-28 shows the custom property dialogbox for the TreeViewcontrol

Figure 10-28

You can include images in a TreeViewor ListViewcontrol by using another ActiveX control calledImageList ImageListis a storage area for pictures that the TreeViewuses when it displays the data.You won’t use images in this example

The next step is to populate the data in the TreeViewcontrol Each item of data in a TreeViewis called

a node and is stored in the TreeView Nodescollection The following code demonstrates the addition of

a data node to the collection of TreeViewnodes:

tvx.Nodes.Add Key:=”C105”, Text:=”Acme Supply Co.”, Image:= 1, SelectedImage:=2

Trang 39

The node is defined with a unique key of C105, a display name of Acme Supply Co., and is set to use theimage with the key of 1 by default, and the image with the key of 2 when selected If you use images, theimage indexes are defined in a corresponding ImageListcontrol.

The Keyproperty of every node is of type Text, it must start with a letter, and it must be unique.

Filling the TreeView Control

During the Form_Loadevent procedure in this example, the TreeViewis populated with one node forevery department in the Departments table, along with the classes that are in the department Here’show First you declare object variables for the TreeViewand ListViewcontrols You use these objects towork with the controls on the form

Dim objTree As TreeViewDim objList As ListViewDim objItem As ListItemDim objParentNode As NodeDim objChildNode As NodeNext, create the Form_Loadevent procedure and add the following code to use the controls on the formand provide some formatting

Private Sub Form_Load()

‘ fill the treeviewDim rsDept As DAO.RecordsetDim rsClasses As DAO.Recordset

‘ get the controlsSet objTree = Me.tvwClasses.ObjectSet objList = Me.lvwStudents.Object

‘ format the controlsWith objTree.Font.Size = 9.Name = “Segoe UI”

End WithWith objList.Font.Size = 9.Name = “Segoe UI”

End WithNote the use of the Objectproperty It is defined by Access to return the object for the control This

property enables you to use early binding for the object model in an ActiveX control Binding works the

same here as it does for other objects Essentially, early binding loads the data when a form loads andlate binding delays attaching to the data, which typically allows for a smaller recordset and takes lesstime to process

If you’ve used the ListViewor TreeViewcontrols in the past, you may notice that some tabs are ing from the custom property dialog box for the TreeViewcontrol Access 2007 no longer includes thecomponents that provide the Font property sheet, which is why you added code to format the control

Trang 40

miss-Now loop through the departments and add a node to the TreeViewcontrol:

‘ get the departmentsSet rsDept = CurrentDb.OpenRecordset( _

“SELECT * FROM tblDepartments ORDER BY Department”)

‘ loop through the departmentsWhile (Not rsDept.EOF)

‘ add the department nodeSet objParentNode = objTree.Nodes.Add(, , _

“DPT” & rsDept(“DepartmentID”), rsDept(“Department”))Here you’re adding a node for each department with a key value of DPT & rsDept(“DepartmentID”).Each node on the TreeViewcontrol needs to have a unique key value By concatenating the value of aprimary key field (DepartmentID) with a unique abbreviation for the table (DPT), you can ensure thateach node has a unique key value You’ll re-use this key later to build relationships with other nodes Forthis example, tblDepartmentsis the parent table The relationships are based on the primary and for-eign key relationships of the tables as shown in the WHEREclause of the next snippet of code

It’s time to add the related classes You loop through another recordset to get the classes for the currentdepartment, and add a new node that is a child of objParentNodeby specifying the tvwChildrelation-ship argument to the Nodes.Addmethod:

‘ get the classes in the selected departmentSet rsClasses = CurrentDb.OpenRecordset( _

“SELECT * FROM tblClasses WHERE Department = “ & _rsDept(“DepartmentID”) & “ ORDER BY ClassName”)

‘ add the classes to the treeviewWhile (Not rsClasses.EOF)

Set objChildNode = objTree.Nodes.Add(objParentNode, tvwChild, _

“CLS” & rsClasses(“ClassID”), rsClasses(“ClassName”))rsClasses.MoveNext

WendrsDept.MoveNextWend

The only thing left to do now is some cleanup:

rsClasses.ClosersDept.CloseSet rsDept = NothingSet rsClasses = NothingEnd Sub

In this example, you’re working with a small number of departments and classes so you’re filling thing when the form is loaded If you have a lot of nodes to add to the TreeView, filling all the datawhen the form loads might take a while To resolve that, you might choose to load the classes for a

every-department when a node is selected, which is known as delay loading or loading on-demand.

Along with properties and methods, ActiveX controls can also provide events such as NodeClickfor theTreeView Access does not know anything about these events, so they must be added using the Visual

Ngày đăng: 09/08/2014, 12:22

TỪ KHÓA LIÊN QUAN