Access Form ControlPart II: Using the FormInfo ClassIn Part I we discussed the FormInfo class and how it allows you to control the placement and appearance of your Access forms. We covered its features and how to use them to set, save, and retrieve its coordinates - among other things. This time we'll delve even deeper and take a hands-on approach to manipulating your forms. Much of the functionality for the FormInfo class is based on Windows API calls. Some of these API calls are beyond the scope of this article, but we'll explain in the following sections how much of the code in the FormInfo class works. If you're interested, you can open the class, follow along with the discussion here, and peruse the code we bypass at your leisure. Setting up the Form Property As we mentioned in Part I, before you can use the FormInfo class, you must "bind" it to an open form. You do that by setting the Form property of your FormInfo object:
Internally, this code causes Access to call the Form Property Set procedure (see FIGURE 1).
FIGURE 1: Access calling the Form Property Set procedure. You can find the HandleError procedure in the FormInfo class module. It simply displays a message box with the current error information. The code is surrounded with compile-time directives that normally cause it to be "compiled out." If you need to debug any sort of odd behavior, you may want to set the #DEBUGGING constant at the top of the module to True. As you can see, the Form Property Set procedure not only stores a reference to the form you've passed in, it also performs some one-time information gathering. The GetScreenInfo procedure (also in the FormInfo class module) is the most interesting, and one we'll focus on. Retrieving Information about the Screen The GetScreenInfo procedure retrieves several values that are required if you're going to programmatically work with the screen in Access. This procedure retrieves two pairs of values:
The GetScreenInfo procedure, shown in FIGURE 2, uses the following API functions to do its job:
FIGURE 2: The GetScreenInfo procedure.
FIGURE 3: Using the ReleaseDC API function to release the device context retrieved with GetDC. Retrieving Border Information After calling GetScreenInfo, the Form Property Set procedure next calls the GetClientOffsets procedure (in the FormInfo class module). Because you may want to relate screen coordinates to coordinates of controls on forms, you need some way to factor in the width of the top and left borders of a form. FIGURE 4 demonstrates why the class needs to determine this information: Access provides coordinates of controls on forms relative to the inside border of the form's client area, so you'll need the sizes of the top and left borders of a form to be able to relate screen and form coordinates. The GetClientOffsets procedure does this work for you in a round-about way. The code uses two Windows API functions to fill in the mptClientOffset module-level variable:
Given these two sets of coordinates, the code in GetClientOffsets can subtract the outside coordinate (retrieved using GetWindowRect) from the inside (further toward the bottom-right corner and larger) coordinate (retrieved using ClientToScreen), to determine the width of the form border. FIGURE 5 contains the full procedure.
FIGURE 5: Using coordinates to determine the width of the form border. Although the FormInfo class uses these API calls in other ways, once you've seen how these procedures work, the rest should be easier to understand. The following sections cover the details of specific properties and methods of the FormInfo class. Removing a Form's Caption Bar As part of an application, you may need to remove a form's caption bar. Although Access allows you to remove the entire border, this may not be what you need for a particular look. Removing the control menu and the minimize and maximize buttons, and setting the form's caption to a single space, will almost work - but it still leaves the thick bar above the form. Removing the form's caption bar relies on changes to the form's window style. When any application creates a new window, it sets up some information about the style of that window. The Windows API provides functions to retrieve and set the style information, and you can change many of the window styles even after the window has been created. The presence or absence of the caption bar is one of those modifiable styles, and the code in FIGURE 6 works by changing the form's window style when called from the form's Open event.
FIGURE 6: The ShowCaptionBar Property Let procedure. Changing the Window Style To change the window's style, follow these steps:
To retrieve and set the style value, you can call the Windows API functions GetWindowLong and SetWindowLong. In each case you tell Windows which particular value you're getting or setting by passing the constant GWL_STYLE. To tell Windows to turn off the caption bar, you need to change the value returned from the call to GetWindowLong. Windows treats the 32-bit value as a set of 32 binary flags, each controlling one attribute of the window where each can have a value of 0 (False) or 1 (True). For example, the window style value contains a bit controlling the display of the caption bar, the minimize and maximize buttons, and the control menu. The only one of these over which Access doesn't give you control is the display of the caption bar. To change one of the settings, you use the And or Or bitwise operators. The And operator takes any two values and returns 1 in any of the positions that was non-zero in both values; it returns 0 in any of the positions where either or both were 0. The Or operator sets any position to 1 if either of the corresponding positions is 1, and 0 otherwise. Therefore, to force a specific bit to be on, you use the Or operator with a number that has all zeros except in the particular bit you care about, where you have a 1. (This works because any value Ored with 0 isn't changed, but any value Ored with 1 is set to 1.) FIGURE 7 shows how using the Or operator with the WM_SYSMENU constant would force a single bit to be on. To force a bit to be off, use the And operator with 1s in all the bits except the one you care about, where you have a 0. (This works because any value Anded with 1 isn't changed, but any value Anded with 0 is set to 0.) To control whether you're turning bits on or off, you can use the Not logical operator, which flips all the bits of a value from 0 to 1 or from 1 to 0. FIGURE 8 shows how using the And operator with the WM_SYSMENU constant would force a single bit to be off. Therefore, given that the constant WS_CAPTION contains the correct bit settings to turn on the display of the caption bar, you could Or it with the value returned from GetWindowLong to force the display on. To turn it off, you And it with NOT WS_CAPTION. This leaves all the bits alone except the one controlling the caption bar display, which is set to 0. When you make this change and call SetWindowLong, Windows redisplays the window without the caption bar. The code in FIGURE 9 executes the steps necessary to retrieve and set the window style value.
FIGURE 9: This code retrieves and sets the window style value. Resizing the Window Unless you do a little more work, the form will look rather odd at this point. Because you haven't told Windows to redraw the form, Access becomes confused; if you're turning off the caption bar, the caption will still show, but Windows won't know it's there. You must resize the form without the caption bar. This section of code requires three Windows API functions:
This code requires some calculations, such as figuring out the height of the old caption and subtracting that from the current height of the window. Subtracting the height of the caption bar from the current height of the form should leave you with a form that's the correct height. Again, see FIGURE 6 for the details. (The GetCoords function, used in the procedure, calculates the coordinates of the form using API calls discussed already.) Showing and Hiding the System Menu For all intents and purposes, this code is almost exactly the same as the code that shows and hides the caption bar. However, there is one big difference: When you hide the system menu, Access doesn't necessarily know that it needs to repaint the border of the form. You need to tell it that the form's border requires repainting, and you can do that by sending a message to the form. The SendMessage API function allows you to send messages to any particular window, given its window handle, and if you send a form the WM_NCPAINT message, the window will repaint its border for you. FIGURE 10 shows the entire ShowSystemMenu Property Let procedure. Its workings should be familiar based on the previous section's discussion. Look for the call to SendMessage, near the end of the procedure, which forces the window to repaint. (Try commenting out that line of code to see what happens if you don't call it.)
FIGURE 10: The ShowSystemMenu Property Let procedure. Saving and Restoring Form Locations The SaveCoords and RetrieveCoords methods of the FormInfo class allow you to save and restore form coordinates to the Windows registry. VBA provides four procedures that make it possible to read and write values to a specific location in the registry:
These procedures are extremely limited. They can work only with subkeys under this particular subkey:
Using SaveSetting and GetSetting SaveSetting allows you either to write to an existing subkey, or to create a new one and write data there. The general syntax is this:
Use its parameters as described in FIGURE 11.
FIGURE 11: SaveSetting parameters. GetSetting works just about the same way. Here is its syntax:
Use its parameters as described in FIGURE 12.
FIGURE 12: GetSetting parameters. Wrapping up the Registry Functions Once you know how to read and write information in the registry, you can follow the RetrieveCoords and SaveCoords methods of the FormInfo class. The SaveCoords method is straightforward: It simply calls SaveSetting for each value the procedure needs to save, passing the AppName you've supplied, the name of the form, a constant representing the coordinate to be saved, and the current value of the coordinate. FIGURE 13 shows the entire procedure.
FIGURE 13: An example of the SaveCoords method. The RetrieveCoords procedure does the same sort of work, but instead of calling the SaveSetting procedure, it calls GetSetting instead. In this case, the code doesn't do anything if the form was maximized or minimized when its coordinates were saved. If you're interested, look at the RetrieveCoords procedure in the FormInfo class module. Conclusion By now, you've seen what's in the FormInfo class module and how to use the methods and properties it contains. You've seen how many of the procedures work, although we've not discussed every single line of code. If you're interested, you may find it interesting to study the GetCoords and GetParentCoords procedures private to the class. These procedures calculate the coordinates of windows and the coordinates of windows' parents. You may also want to investigate the Center method, which uses the GetCoords and GetParentCoords procedures to calculate the coordinates at which to place a centered form. Whether or not you study the source code in the FormInfo class module, you'll most likely find the functionality it provides useful in your Access applications. Because Access gives you so little information about the size and location of its forms, you're sure to find many uses for the FormInfo class and its properties and methods. This article is modified and excerpted from Access 2000 Developer's Handbook, Volume I (Ken Getz, Paul Litwin, and Mike Gilbert; SYBEX 1999) with the permission of the publisher. Ken Getz, a senior consultant with MCW Technologies, splits his time between programming, writing, and training. Ken is co-author of several books for developers, including Access 97 Developer's Handbook [SYBEX, 1997] (with Paul Litwin and Mike Gilbert) and VBA Developer's Handbook [SYBEX, 1997] (with Mike Gilbert). He also co-wrote the training materials, travels around the United States teaching, and recorded training videos on Access 97 and VB6 for Application Developers Training Company. Ken is currently at work on Access 2000 Developer's Handbook and Visual Basic Language Developer's Handbook, both from SYBEX. In addition, Ken is a Contributing Editor for Microsoft Office & Visual Basic for Applications Developer magazine. Mike Gilbert is Technical Product Manager for Office Developer Edition and VBA Licensing at Microsoft. He is also co-author of VBA Developer's Handbook and Access 97 Developer's Handbook (with Ken Getz and Paul Litwin), both for SYBEX. In addition, Mike is a Contributing Editor for Microsoft Office & Visual Basic for Applications Developer magazine. Paul Litwin is a senior consultant with MCW Technologies, focusing on application development employing Access, Visual Basic, Visual InterDev, and SQL Server. He has written/co-written several books, including the Access 2000 Developer's Handbook [SYBEX, 1999]. Paul also trains developers for Application Developers Training Company and speaks regularly at industry conferences. You can reach him at plitwin@mcwtech.com. |