When you have multiple users logged into your application, you want them to be able to communicate quickly and easily with one another. You need a simple interface for sending notes back and forth so users can check whether anyone else is editing a particular entry, compare notes on workflow, and so on. How can you implement this in Access?
You can keep your notes in a table in a shared database to which all users have access. Whenever someone writes a note to another user, that note is added as another record in this table. By using a form that makes use of the Timer event, you can monitor the status of this table from any Access application and notify users when new messages have arrived.
This solution employs two files, 10-04fe.MDB and 10-04be.MDB. Before you can try it, you'll need to link the data tables from 10-04be.MDB (the "backend" or data database) to 10-04fe.MDB (the "frontend" or application database). Linking a data table allows you to use a table from one Access database within another Access database. Start Access and load 10-04fe.MDB. Choose File Get External Data Link Tables, and select 10-04be.MDB as the Access link database. At the Link Tables dialog, select tblMessage and click OK, as shown in Figure 10-16.
Now you can test-drive this solution by sending a message to yourself. Open both frmSendMail and frmReceiveMail. Minimize the Receive Mail form. Select your username from the To combo box. If you haven't altered the default Access security settings, your username will be Admin, which should be confirmed in the From text box. Enter any message and click the Send Message button. In Figure 10-17, Peter has used frmSendMail to compose a message to Jean.
|
The Send Mail form will clear as soon as the message is sent. Within 10 seconds, the Receive Mail form will pop up with the message. Figure 10-18 shows how Jean would see the message from Peter. Click on the Mark as Read button to clear the Receive Mail form. If more than one message is waiting, you can navigate through them.
To use this technique in your own applications, follow these steps:
Identify the shared database you'll be using to hold the messages. This can be an existing shared database or a new one designed expressly for this purpose. Create a new table with the fields shown in Table 10-5. Make MessageID the primary key of this table, and save it as tblMessage.
Field name |
Data type |
---|---|
MessageID |
AutoNumber |
From |
Text |
To |
Text |
DateSent |
Date/Time |
DateReceived |
Date/Time |
Message |
Memo |
Close the shared database and open the database with which you want to send and receive messages. This is the database where you'll create the remaining objects. Import basMail and basFillUsers from 10-04fe.MDB to this database.
Create a new form with the properties shown in Table 10-6.
Property |
Value |
---|---|
Caption |
Send Mail |
DefaultView |
Single Form |
ScrollBars |
Neither |
RecordSelectors |
No |
NavigationButtons |
No |
Add two unbound text box controls and an unbound combo box control to the form, as shown in Figure 10-17. Name the first text box txtFrom. Set its ControlSource property to:
=CurrentUser( )
Name the second text box txtMessage and size it to hold the text of your message. Set the EnterKeyBehavior property for txtMessage to New Line in Field.
Name the combo box cboTo and size it the same as txtFrom. Set its combo box-specific properties to match those in Table 10-7.
Property |
Value |
RowSourceType |
acbFillUserList |
RowSource | |
ColumnCount |
1 |
ColumnHeads |
No |
ColumnWidths | |
BoundColumn |
1 |
ListRows |
8 |
ListWidth |
Auto |
Add a command button to the form, with the properties shown in Table 10-8. The &Send Message caption makes the button respond to the Alt-S accelerator key shortcut.
Property |
Value |
---|---|
Name |
cmdSend |
Caption |
&Send Message |
OnClick |
=acbSendMail( ) |
Save this form as frmSendMail.
Select File Get External Data Link Tables and link the tblMessage table you created in your shared database to this frontend database.
Create a new query based on tblMessage. Drag all the fields from the field list to the query grid. Set the query criteria as shown in Table 10-9. Save this query as qryNewMail.
Field |
Criteria |
To |
CurrentUser( ) |
DateReceived |
Is Null |
Create another new form, with the properties shown in Table 10-10.
Property |
Value |
---|---|
RecordSource |
qryNewMail |
Caption |
No mail |
DefaultView |
Single Form |
AllowAdditions |
No |
ScrollBars |
Neither |
RecordSelectors |
No |
NavigationButtons |
Yes |
OnLoad |
=acbCheckMail( ) |
OnTimer |
=acbCheckMail( ) |
TimerInterval |
10000 |
Add three bound text box controls to the form. Name the first one txtFrom, set the ControlSource to From, and size it to hold the sender's address. Name the second one txtSent, set the ControlSource to DateSent, and size it to hold the date and time the message was sent. Name the third one txtMessage, set the ControlSource to Message, and size it to hold the message text.
Add a watermark picture to the form using the additional form properties found in Table 10-11.
In the sample database, we've used a simple bitmap created with the Windows Paint program to display a message in the center of the form. This bitmap, NONEW.BMP, is included on the CD-ROM. (CD content is available online at http://examples.oreilly.com/accesscook.) You can add this bitmap to your form or create your own.
Property |
Value |
---|---|
Picture |
bitmap file |
PictureType |
Embedded |
PictureSizeMode |
Clip |
PictureAlignment |
Center |
PictureTiling |
No |
Place a Rectangle control with the same background color as the form's detail section behind all of the controls on the form. After you have positioned it and sized it to take up the entire detail section, you can move it behind the other controls by selecting Format | Send to Back.
Add a command button to the form, with the properties shown in Table 10-12.
Property |
Value |
---|---|
Name |
cmdReceive |
Caption |
&Mark as Read |
OnClick |
=acbReceiveMail( ) |
Save this form as frmSendMail.
This technique works by passing messages back and forth through tblMessage. The sending form is unbound, because when you send a message, you don't want to have to flip through all the previous messages. The acbSendMail function just takes whatever you type into the form and puts it into this table. It also uses the CurrentUser function to put your name into the From field of the table, and the Now function to time-stamp the message. The acbSendMail function is shown here:
Public Function acbSendMail( ) As Integer ' Take the message and user from the ' frmMailSend form and send it to the mail ' backend On Error GoTo HandleErr Dim db As DAO.Database Dim rstMail As DAO.Recordset Dim frmMail As Form Set db = CurrentDb( ) Set rstMail = db.OpenRecordset( _ "tblMessage", dbOpenDynaset, dbAppendOnly) Set frmMail = Forms("frmSendMail") rstMail.AddNew rstMail("From") = CurrentUser( ) rstMail("To") = frmMail.cboTo rstMail("DateSent") = Now rstMail("Message") = frmMail.txtMessage rstMail.Update frmMail.cboTo = Null frmMail.txtMessage = Null ExitHere: On Error Resume Next rstMail.Close Err.Clear Exit Function HandleErr: MsgBox Err & ": " & Err.Description, , "acbSendMail( )" Resume ExitHere End Function
Opening the recordset with the dbAppendOnly flag accelerates the process of adding a new record because it avoids reading in the existing records that the send function doesn't care about.
The cboTo combo box uses a list-filling function to fill the combo box with a list of current users in the workgroup. List-filling functions were discussed in the Solution in Recipe 7.5. This particular function fills its list using security data access objects to iterate through the collection of users in the workgroup. We defer discussion of this topic to the Solution in Recipe 10.5.
The Receive Mail form is based on a query that finds all messages directed to the current user that have nothing in their DateReceived fields. By default, new records added from elsewhere on a network do not show up on an already-opened form; you must explicitly requery the form for this to happen. The acbCheckMail function automatically performs this requery at load time and once every 10 seconds to check for new mail. The acbCheckMail function is shown here:
Function acbCheckMail( ) As Integer ' Check for new mail, and if there is any, ' restore the received mail form On Error GoTo HandleErr Dim rstClone As DAO.Recordset Dim frmMail As Form Set frmMail = Forms("frmReceiveMail") frmMail.Requery Set rstClone = frmMail.RecordsetClone If Not rstClone.EOF Then rstClone.MoveFirst frmMail.Caption = "New Mail!" If IsIconic(frmMail.Hwnd) Then frmMail.SetFocus DoCmd.Restore End If Else frmMail.Caption = "No mail" End If ExitHere: Exit Function HandleErr: Select Case Err.Number Case 3021 ' no current record, do nothing Case Else MsgBox Err & ": " & Err.Description, , "acbCheckMail( )" End Select Resume ExitHere End Function
After the form is requeried, acbCheckMail checks for new mail by looking at the RecordsetClone property of the form. This property returns an exact duplicate of the form's underlying recordset. If there are any records to be shown, this RecordsetClone will not be at its EOF, so the function changes the form's caption and, if it is currently minimized, restores the form to its full size. The function calls the Windows API function IsIconic (declared in the declarations section of basMail) to determine if the form is minimized.
We have used the form's Picture property, a rectangle, and the form's AllowAdditions property to add one more effect to the form: when the form's recordset is empty, all the controls on the form disappear and a bitmap reading "There are no new mail messages" appears on the form (see Figure 10-19).
This trick is accomplished by setting the form's AllowAdditions property to No, adding a watermark picture to the form, and adding an opaque rectangle that hides the watermark when there are records in the form's recordset. When there are no records in a form's recordset and you have set AllowAdditions to No, Access hides all of the form's controls?including the unbound Rectangle control?and prominently displays the form's watermark, if there is one.
This method uses the Access username to track mail senders and recipients. To use it in production, you'll need to activate Access security (otherwise, everyone is signed on as the Admin user at all times). To activate security, simply use Security Change Password to assign a password to the Admin user. Then you can select Users from the Security menu and create as many new users as you like. Security was discussed in more detail in the Solution in Recipe 10.1.
To test this solution with multiple users, you'll need to have several machines available on a network. Make a copy of 10-04fe.MDB for each computer, and use File Get External Data Link Tables to link the same copy of tblMessage to each one. Log in as a different user at each computer, and you'll be able to send messages back and forth.
You can adjust the performance impact of this technique by changing the TimerInterval property of frmReceiveMail. This property measures the number of milliseconds between each execution of the OnTimer event. In the sample database, the TimerInterval property is set to 10000 milliseconds, or 10 seconds; its highest possible value is 65535, or just over a minute. If you want a longer delay, you can add a static integer variable to acbCheckMail and increment it more than once before you check for new mail.
For more on working with Outlook programmatically, see Recipe 12.8 in Chapter 12.