Many people want to save the position of their forms at run time so that they can restore them to their previous state. To address this problem, the WindowPosition() routine shown in Listing 33.1 provides a reusable module that can work with any form.
Listing 33.1 - WINPOS.BAS - WinPos.bas saves and stores the position of a window
'********************************************************************* ' Saves or restores the window position for a form '********************************************************************* Public Sub WindowPosition(frmName As Form, blnSavePostion As Boolean) Dim strWinPosKey As String Dim strAppName As String '***************************************************************** ' Use the product name if it exists, otherwise use the exe name '***************************************************************** strAppName = IIf(Len(App.ProductName), App.ProductName, App.EXEName) With frmName '************************************************************* ' This function is only designed for "normal" windows, so ' exit when the form is minimized or maximized. '************************************************************* If .WindowState <> vbNormal Then Exit Sub '************************************************************* ' Use the form name and a descriptive string to make it easy ' for the user to find these values in the registry '************************************************************* strWinPosKey = .Name & " Startup Position" '************************************************************* ' Save the current settings,... '************************************************************* If blnSavePostion Then SaveSetting strAppName, strWinPosKey, "Height", .Height SaveSetting strAppName, strWinPosKey, "Width", .Width SaveSetting strAppName, strWinPosKey, "Left", .Left SaveSetting strAppName, strWinPosKey, "Top", .Top '************************************************************* ' ...or restore the settings (center the form if not found) '************************************************************* Else .Height = CSng(GetSetting(strAppName, strWinPosKey, _ "Height", .Height)) .Width = CSng(GetSetting(strAppName, strWinPosKey, _ "Width", .Width)) .Left = CSng(GetSetting(strAppName, strWinPosKey, _ "Left", (Screen.Width - .Width) / 2)) .Top = CSng(GetSetting(strAppName, strWinPosKey, _ "Top", (Screen.Height - .Height) / 2)) End If End With End Sub
WindowPosition() takes a pointer to form and a save/restore flag (called blnSavePosition). The calling form simply passes in a pointer to itself for the first argument, and a Boolean for the second argument. If this argument is True, the routine saves the current form position in the registry. If it is False, WindowPosition() retrieves the last (or default) values from the registry and repositions the form.
Notice how the WindowPosition() routine uses the product name (if available) as the parent key to store the values for the window position. Next, the routine uses the form name along with the string "Startup Position" to create an easy-to-recognize key to hold the window settings. Two forms can't have the same name in the same project, so this approach ensures that users never have a name conflict in the registry. The only other important part of this code is the following line:
If .WindowState <> vbNormal Then Exit Sub
This line is important because if your resizable form is minimized or maximized, you don't want to save the form's size or move the form. If you did, you would encounter one of two possible problems:
To avoid these problems, you simply ignore the minimized and maximized cases. However, you could modify WindowPosition() to include a WindowState key in the registry. This modification would enable your version of WindowPosition() to support the restoring of windows to their minimized and maximized window states. This routine doesn't include this code, however, because you should load windows only in the vbNormal state.
While writing WINPOS.BAS, you might also include code that centers a form to the screen or to a parent (or owner) form. Initially, you might think that adding such code doesn't make much sense, because Visual Basic now includes the StartupPosition property. However, this property is limited. StartupPosition works only when a form is loaded, so if you want to center a form after loading it, you have to do it yourself. Because it is common to hide forms instead of unloading them, there are several cases in which you have to center a form yourself. The code in Listing 33.2 demonstrates how to do so.
Listing 33.2 - WINPOS.BAS - the CenterForm() Routine
'********************************************************************* ' Centers a form to the screen (default) or to a parent form (optional) '********************************************************************* Public Sub CenterForm(frmName As Form, Optional frmCenterTo As Form) With frmName '************************************************************* ' If frmCenterTo wasn't provided, then center to screen '************************************************************* If frmCenterTo Is Nothing Then .Move (Screen.Width - .Width) / 2, _ (Screen.Height - .Height) / 2 '************************************************************* ' Otherwise center to frmCenterTo (useful for dialogs) '************************************************************* Else '********************************************************* ' If the child is larger than the parent, then center ' to the screen. '********************************************************* If frmCenterTo.Width < .Width Or _ frmCenterTo.Height < .Height Then CenterForm frmName Else .Move frmCenterTo.Left + (frmCenterTo.Width - .Width) / 2, _ frmCenterTo.Top + (frmCenterTo.Height - .Height) / 2 End If End If End With End Sub
The code in CenterForm() is fairly straitforward because if the optional argument isn't provided, it centers the form to the screen. If the argument is provided, the routine quickly checks to ensure that the child is smaller than the parent. If frmCenterTo is smaller than frmName, the routine centers the form to frmName. Otherwise, the routine centers the form to the screen. Figure 33.1 shows a child form that CenterForm() has centered to its parent window.
CenterForm() also supports centering forms to their parent.
On the companion CD-ROM, LASTPOS.VBP demonstrates how to use the code in WINDOWPOS.BAS using several different types of forms. LASTPOS.FRM, shown in Listing 33.3, contains code that you can copy and paste into any project that includes WINPOS.BAS.
Listing 33.3 - LASTPOS.FRM - LASTPOS.FRM's Load() and Unload() Events
'********************************************************************* ' Sets the initial window position and size (centered on the first ' start). '********************************************************************* Private Sub Form_Load() WindowPosition Me, False End Sub '********************************************************************* ' Saves the current window position before the form is destroyed '********************************************************************* Private Sub Form_Unload(Cancel As Integer) WindowPosition Me, True End Sub
The concept is simple. If your form is loading, restore your previous position by passing False to WindowPosition. If your form is unloading, save your window position by passing True.