Jeff Webb, Mike McKelvy, Ronald Martinsen, Taylor Maxwell, Michael Regelski September 1995 Special Edition Using Visual Basic 4 - Chapter 31 1-56529-998-1 Computer Programming computer programming Visual Basic OLE database applications ODBC VB VBA API This book is an all-in-one reference that provides extensive coverage of every topic and technique for creating optimized and customized applications with Visual Basic.

Chapter 31

Advanced Form Techniques


Forms are much more than containers on which to place your controls. They are the visual glue that binds all an application's controls together. Users focus on the controls that you show them, but a change in the appearance of a form can create a completely different impression.

In this chapter, you learn how to do the following:

Exploring New Form Properties

Visual Basic 4.0 includes several new form properties. These properties give you more options for changing the appearance and behavior of your forms. You need these capabilities to create the kind of new forms that you see as part of the growing Windows standard look and feel. Prior to the release of Visual Basic 4.0, you could replicate these capabilities only by using either complicated types of code or third-party controls.

The Appearance Property

Users respond positively to forms with a three-dimensional (3-D) look and feel. Visual Basic provides a method for making 3-D forms simply by changing the Appearance property to 1- 3D.

Be careful how many controls you place on a form. A form with too many controls can begin to look unpleasant. Forms should contain logical groupings of controls. The most common methods for grouping controls are spacing and using frames.

The AutoShowChildren Property

In Visual Basic 4.0, MDI children no longer have to be visible. When the AutoShowChildren property of the MDI parent is True, the Load method does not make the form visible. Sometimes users require that applications work with more forms than can be comfortably displayed on the screen at the same time. The AutoShowChildren property enables you to keep only the currently necessary screens visible while leaving other less necessary screens invisible. With this capability, you can quickly display screens that are already loaded but not visible. This creates the impression of a fast application.

The NegotiateMenus Property

Windows is moving toward OLE technology quickly. You can expect certain features of new applications, including support of OLE 2.0. Until the release of Visual Basic 4.0, programmers had no method for creating Visual Basic applications that fully support OLE 2.0 technology. One of the biggest problems was making the menus of a form match that of the activated OLE object. The NegotiateMenus property provides a means of enabling this transformation to take place automatically. Simply by setting the form's NegotiateMenus property to True, you change all the menus when the user activates an object on that form. This new property enables support for in-place activation (also known as visual editing). This OLE 2.0 technology enables a user to double-click on an object provided by an OLE server. A user can then work with the application without switching to that different application.

Visual Basic now supports the display of multiple design time environments. This enables you to access both projects and to move code quickly from one project to another—a vast improvement over the previous version of Visual Basic.

Controlling Screen Position

Users want to feel that they are in control of the ways that their applications function. You must strive to look for anything that you can do to give them more control over how your application functions. One obvious place that you can give the user control over is the position of the forms on the user's screen.

Dialog Boxes

Positioning dialog boxes is very important, even though users do not need to be able to save the previous positions of dialog boxes. Consider the difference between the dialog box centered in figure 31.1 to that found in figure 31.2. Notice the higher-quality appearance of the dialog box shown in figure 31.2.

Fig. 31.1

The Options dialog box from Word for Windows is offset on the screen. This kind of problem can happen when a screen is designed on an 800-by-600 screen.

Fig. 31.2

The Options dialog box from Word for Windows is now centered on the screen.

One sign of a badly considered application is one that does not center its dialog boxes on the screen. If you do not enter code in the form’s load event, the form appears in the last position that you left it in the design environment. This unacceptable design technique certainly detracts from the program’s overall usefulness.

The CenterDialogPosition subroutine, shown in listing 31.1, centers the dialog box over the form. If that form is larger than the main form, the code adjusts itself accordingly to fit over the main form. This ensures that the dialog box does not appear in the incorrect position if the user sizes frmMain to smaller than the dialog box.

Listing 31.1 The CenterDialogPosition Subroutine Centers the Dialog Box over the Form

Sub CenterDialogPosition(WNdName As Form)
If (WNdName.Height > frmMain.Height) Or _
(WNdName.Width > frmMain.Width) Then
WNdName.Top = (Screen.Height - WNdName.Height) \ 2
WNdName.Left = (Screen.Width - WNdName.Width) \ 2
Else
WNdName.Top = frmMain.Top + ((frmMain.Height - _
WNdName.Height) \ 2)
WNdName.Left = frmMain.Left + ((frmMain.Width - _
WNdName.Width) \ 2)
End If
End Sub

Listing 31.1 is an example of how you can make a dialog box appear centered on top of an application’s main form. Notice that the code directly references the Left, Top, Height, and Width properties of frmMain. This code assumes that the main form of your application is named frmMain. If you give the main form another name, that name must appear in those places in the code.

The Main Form

As increasingly more users buy computers with higher-resolution monitors, the need for applications that adjust to the size of a screen increases. By default, all Visual Basic forms appear in their last design-time positions on the screen. If you design a screen using an 800-by-600 screen, you must ensure that you position the form properly for lower-resolution monitors. Further, users with higher-resolution monitors probably will want to be able to change the positions of their applications. They also will want to save the last positions or have them appear in a particular position each time that they start their applications.

To make your applications save the last position and size of a form to the Windows Registry, you must declare certain API functions and constants. The necessary API functions and constants appear in listing 31.2. Included with this code is the error-checking routine gAPIDisplayError. This routine checks for any errors that might occur when using Registry API calls. For a further explanation of these constants and API functions, see the section “Understanding Registry APIs” in Chapter 33, “Accessing the Windows API.

Listing 31.2 The API Functions and Constants Necessary to Save a Form’s Position and Size

Option Explicit
Declare Function RegCloseKey Lib "advapi32" _
(ByVal hKey As Long) As Long
Declare Function RegCreateKeyEx Lib _
"advapi32" Alias "RegCreateKeyExA"
(ByVal hKey As Long, ByVal lpSubKey As String, _
ByVal Reserved As Long, ByVal lpClass As String, _
ByVal dwOptions As Long, ByVal samDesired As Long, _
lpSecurityAttributes As SECURITY_ATTRIBUTES, _
phkResult As Long, lpdwDisposition As Long) As Long
Declare Function RegOpenKeyEx Lib "advapi32" Alias _
"RegOpenKeyExA" (ByVal hKey As Long, ByVal lpSubKey _
As String, ByVal ulOptions As Long, ByVal samDesired As Long, _
phkResult As Long) As Long
Declare Function RegQueryValueEx Lib "advapi32" Alias _
"RegQueryValueExA" (ByVal hKey As Long, ByVal lpValueName _
As String, lpReserved As Long, lpType As Long, ByVal lpData _
As String, lpcbData As Long) As Long
Declare Function RegSetValueEx Lib "advapi32" Alias _
"RegSetValueExA" (ByVal hKey As Long, ByVal lpValueName As _
String, ByVal Reserved As Long, ByVal dwType As Long, _
ByVal lpData As String, ByVal cbData As Long) As Long
'Security Mask constants
Public Const READ_CONTROL = &H20000
Public Const SYNCHRONIZE = &H100000
Public Const STANDARD_RIGHTS_ALL = &H1F0000
Public Const STANDARD_RIGHTS_READ = READ_CONTROL
Public Const STANDARD_RIGHTS_WRITE = READ_CONTROL
Public Const KEY_QUERY_VALUE = &H1
Public Const KEY_SET_VALUE = &H2
Public Const KEY_CREATE_SUB_KEY = &H4
Public Const KEY_ENUMERATE_SUB_KEYS = &H8
Public Const KEY_NOTIFY = &H10
Public Const KEY_CREATE_LINK = &H20
Public Const KEY_ALL_ACCESS = ((STANDARD_RIGHTS_ALL Or _
KEY_QUERY_VALUE Or KEY_SET_VALUE Or KEY_CREATE_SUB_KEY Or _
KEY_ENUMERATE_SUB_KEYS Or KEY_NOTIFY Or KEY_CREATE_LINK) _
And (Not SYNCHRONIZE))
Public Const KEY_READ = ((STANDARD_RIGHTS_READ Or _
KEY_QUERY_VALUE Or KEY_ENUMERATE_SUB_KEYS Or KEY_NOTIFY) _
And (Not SYNCHRONIZE))
Public Const KEY_EXECUTE = ((KEY_READ) And (Not SYNCHRONIZE))
Public Const KEY_WRITE = ((STANDARD_RIGHTS_WRITE Or _
KEY_SET_VALUE Or KEY_CREATE_SUB_KEY) And (Not SYNCHRONIZE))
'Predefined Registry Keys used in hKey Argument
Public Const HKEY_CLASSES_ROOT = &H80000000
Public Const HKEY_CURRENT_USER = &H80000001
Public Const HKEY_LOCAL_MACHINE = &H80000002
Public Const HKEY_USERS = &H80000003
Public Const HKEY_PERFORMANCE_DATA = &H80000004
' Return codes from Registration functions.
Public Const ERROR_SUCCESS = 0&
Public Const ERROR_BADDB = 1009&
Public Const ERROR_BADKEY = 1010&
Public Const ERROR_CANTOPEN = 1011&
Public Const ERROR_CANTREAD = 1012&
Public Const ERROR_CANTWRITE = 1013&
Public Const ERROR_OUTOFMEMORY = 14&
Public Const ERROR_INVALID_PARAMETER = 87&
Public Const ERROR_ACCESS_DENIED = 5&
'Data type Public Constants
Public Const REG_NONE = 0
Public Const REG_SZ = 1
Public Const REG_EXPAND_SZ = 2
Public Const REG_BINARY = 3
Public Const REG_DWORD = 4
Public Const REG_DWORD_LITTLE_ENDIAN = 4
Public Const REG_DWORD_BIG_ENDIAN = 5
Public Const REG_LINK = 6
Public Const REG_MULTI_SZ = 7
Public Const REG_RESOURCE_LIST = 8
Public Const REG_FULL_RESOURCE_DESCRIPTOR = 9
Public Const REG_RESOURCE_REQUIREMENTS_LIST = 10
'Options
Public Const REG_OPTION_VOLATILE = 0
Public Const REG_OPTION_NON_VOLATILE = 1
Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Variant
bInheritHandle As Long
End Type
Sub gAPIDisplayError(Code&)
Select Case Code&
Case ERROR_BADDB
MsgBox "Corrupt Registry Database!"
Case ERROR_BADKEY
MsgBox "Key name is bad"
Case ERROR_CANTOPEN
MsgBox "Cannot Open Key"
Case ERROR_CANTREAD
MsgBox "Cannot Read Key"
Case ERROR_CANTWRITE
MsgBox "Cannot Write Key"
Case ERROR_ACCESS_DENIED
MsgBox "Access to Registry Denied"
Case ERROR_OUTOFMEMORY
MsgBox "Out of memory"
Case ERROR_INVALID_PARAMETER
MsgBox "Invalid Parameter"
Case Else
MsgBox "Undefined key error code!"
End Select
End Sub

Listing 31.3 is a generic routine that positions the main MDI form of a project on the screen. First, gSetMainFormPosition checks whether a position and size is set in the Windows Registry. If no such settings are in the Windows Registry, the main form appears maximized on a 640-by-480 resolution screen or centered on a higher-resolution screen. If the Registry has saved settings, gSetMainFormPosition applies them to the main form.

Listing 31.3 An MDI Form-Positioning Routine

Sub gSetMainFormPosition(frmMain As Form)
Dim lReturn As Long
Dim hlstrWindowState As String
Dim hlstrTopPosition As String
Dim lTopPosition As Long
Dim hlstrLeftPosition As String
Dim lLeftPosition As Long
Dim hlstrFormWidth As String
Dim lFormWidth As Long
Dim hlstrFormHeight As String
Dim lFormHeight As Long
Dim hWndWindowState As Long
Dim lpcbData As Long
lpcbData = 255
lReturn = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Que\MainForm", 0&, _
KEY_ALL_ACCESS, hWndWindowState)
If lReturn = 2 Then
hlstrWindowState = ""
Else
hlstrWindowState = Space$(lpcbData)
lReturn = RegQueryValueEx(hWndWindowState, "WindowState", _
0&, REG_SZ, hlstrWindowState, lpcbData)
If lReturn <> ERROR_SUCCESS Then
gAPIDisplayError lReturn
Exit Sub
End If
End If
Select Case Val(hlstrWindowState)
Case 0
lpcbData = 255
hlstrTopPosition = Space$(lpcbData)
lReturn = RegQueryValueEx(hWndWindowState, "TopPosition", _
0&, REG_SZ, hlstrTopPosition, lpcbData)
lTopPosition = Val(hlstrTopPosition)
lpcbData = 255
hlstrLeftPosition = Space$(lpcbData)
lReturn = RegQueryValueEx(hWndWindowState, "LeftPosition", _
0&, REG_SZ,
hlstrLeftPosition, lpcbData)
lLeftPosition = Val(hlstrLeftPosition)
lpcbData = 255
hlstrFormWidth = Space$(lpcbData)
lReturn = RegQueryValueEx(hWndWindowState, "FormWidth", _
0&, REG_SZ, hlstrFormWidth, lpcbData)
lFormWidth = Val(hlstrFormWidth)
lpcbData = 255
hlstrFormHeight = Space$(lpcbData)
lReturn = RegQueryValueEx(hWndWindowState, "FormHeight", _
0&, REG_SZ, hlstrFormHeight, lpcbData)
lFormHeight = Val(hlstrFormHeight)
frmMain.Move lLeftPosition, lTopPosition, lFormWidth, _
lFormHeight
Case 1
frmMain.WindowState = vbMinimized
Case 2
frmMain.WindowState = vbMaximized
Case Else
frmMain.Width = 640 * Screen.TwipsPerPixelX
frmMain.Height = 480 * Screen.TwipsPerPixelY
If frmMain.Width + 100 > Screen.Width Then
frmMain.WindowState = vbMaximized
Else
frmMain.Top = (Screen.Height - frmMain.Height) \ 2
frmMain.Left = (Screen.Width - frmMain.Width) \ 2
End If
End Select
lReturn = RegCloseKey(hWndWindowState)
End Sub

gSetMainFormPosition uses several Registry API calls to work properly. RegOpenKeyEx identifies the key that contains the setup information. RegQueryValueEx provides the setting found under each value under the key. You must always remember to use RegCloseKey to close a key at the end of a Registry operation.

gSaveMainFormPosition (see listing 31.4) saves the main form's current position, size, and state to the Windows Registry. RegCreateKeyEx provides the window handle of the key that contains the information. Notice that you use this call whether the key exists or not. This API call simply opens a key in the same way as RegOpenKeyEx if that key already exists. You save the new value under the key using RegSetValueEx. You should never forget to close a key when you are done with one of these Registry operations that use RegCloseKey.

Listing 31.4 The gSaveMainFormPosition Routine

Sub gSaveMainFormPosition(frmMain As Form)
Dim hWndQue As Long
Dim hlstrWindowState As String
Dim hlstrTopPosition As String
Dim hlstrLeftPosition As String
Dim hlstrFormWidth As String
Dim hlstrFormHeight As String
Dim lpClass As String
Dim Security As SECURITY_ATTRIBUTES
Dim lpdwDisposition As Long
Dim lReturn As Long
lpClass = Space$(255)
lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Que\MainForm", _
0&, lpClass, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, Security, _
hWndQue, lpdwDisposition)
If lReturn <> ERROR_SUCCESS Then
gAPIDisplayError lReturn
End If
hlstrWindowState = Trim$(Str$(frmMain.WindowState))
lReturn = RegSetValueEx(hWndQue, "WindowState", 0&, REG_SZ, _
hlstrWindowState, Len(hlstrWindowState))
If lReturn <> ERROR_SUCCESS Then
gAPIDisplayError lReturn
End If
hlstrTopPosition = Trim$(Str$(frmMain.Top))
lReturn = RegSetValueEx(hWndQue, "TopPosition", 0&, REG_SZ, _
hlstrTopPosition, Len(hlstrTopPosition))
If lReturn <> ERROR_SUCCESS Then
gAPIDisplayError lReturn
End If
hlstrLeftPosition = Trim$(Str$(frmMain.Left))
lReturn = RegSetValueEx(hWndQue, "LeftPosition", 0&, REG_SZ, _
hlstrLeftPosition, Len(hlstrLeftPosition))
If lReturn <> ERROR_SUCCESS Then
gAPIDisplayError lReturn
End If
hlstrFormWidth = Trim$(Str$(frmMain.Width))
lReturn = RegSetValueEx(hWndQue, "FormWidth", 0&, REG_SZ, _
hlstrFormWidth, Len(hlstrFormWidth))
If lReturn <> ERROR_SUCCESS Then
gAPIDisplayError lReturn
End If
hlstrFormHeight = Trim$(Str$(frmMain.Height))
lReturn = RegSetValueEx(hWndQue, "FormHeight", 0&, REG_SZ, _
hlstrFormHeight, Len(hlstrFormHeight))
If lReturn <> ERROR_SUCCESS Then
gAPIDisplayError lReturn
End If
lReturn = RegCloseKey(hWndQue)
End Sub

The following steps show you how to make this code work together:

  1. Open new project and rename this project as 31Proj02.
  2. Add a new code module and rename it as WINAPI32.
  3. Add the declaration text, constants, and error routine to the General Declarations section of WINAPI32.
  4. Add a new code module and rename it as Settings.
  5. Add the routines gSetMainFormPosition and gSaveMainFormPosition to the Settings module.
  6. Add an MDI form and rename it as frmMain.

    Private Sub MDIForm_Load()
    Call gSetMainFormPosition(Me)
    End Sub
    Private Sub MDIForm_Unload(Cancel As Integer)
    Call gSaveMainFormPosition(Me)
    End Sub

  7. Add the Form_Load code to the load event and the unload code to the unload event of frmMain.

MDI Children

MDI applications can place MDI children anywhere within a form. Exactly where the MDI child appears depends on its purpose—and hopefully the user’s choices. Unlike the main form, there is really no all-encompassing default place to put all the MDI children. The only safe default choice is to place a child in the upper-right corner of the MDI parent.

To make your applications save the last position and size of a form to the Windows Registry, you must declare certain API functions and constants. The necessary API functions and constants appear in the WINAPI32 to the MDI form-positioning code. Included with this code is the error-checking routine gAPIDisplayError. This routine checks for any errors that might occur when using Registry API calls. For a further explanation of these constants and API function, see the section “Understanding Registry APIs” in Chapter 33, “Accessing the Windows API.

gSetChildFormPosition (listing 31.5) positions child windows within the MDI parent. This routine is very similar to gSetMainFormPosition, with a few important differences. First, the default position of frmChild is in the upper-right corner of the MDI parent. Second, gSetChildFormPosition sets only the left and top properties of frmChild. This works quite well for nonresizeable MDI children. This code needs to change for window children with resizeable borders.

Listing 31.5 The gSetChildFormPosition Routine

Sub gSetChildFormPosition(frmChild As Form)
Dim lReturn As Long
Dim hlstrWindowState As String
Dim hlstrTopPosition As String
Dim lTopPosition As Long
Dim hlstrLeftPosition As String
Dim lLeftPosition As Long
Dim hWndWindowState As Long
Dim lpcbData As Long
lpcbData = 255
lReturn = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Que\ChildForm", _
0&, KEY_ALL_ACCESS, hWndWindowState)
If lReturn = 2 Then
hlstrWindowState = ""
Else
hlstrWindowState = Space$(lpcbData)
lReturn = RegQueryValueEx(hWndWindowState, _
"WindowState", 0&, REG_SZ, hlstrWindowState, lpcbData)
If lReturn <> ERROR_SUCCESS Then
gAPIDisplayError lReturn
Exit Sub
End If
End If
Select Case Val(hlstrWindowState)
Case 0
lpcbData = 255
hlstrTopPosition = Space$(lpcbData)
lReturn = RegQueryValueEx(hWndWindowState, "TopPosition", _
0&, REG_SZ, hlstrTopPosition, lpcbData)
lTopPosition = Val(hlstrTopPosition)
lpcbData = 255
hlstrLeftPosition = Space$(lpcbData)
lReturn = RegQueryValueEx(hWndWindowState, "LeftPosition", _
0&, REG_SZ, hlstrLeftPosition, lpcbData)
lLeftPosition = Val(hlstrLeftPosition)
frmChild.Move lLeftPosition, lTopPosition
Case 1
frmChild.WindowState = vbMinimized
Case 2
frmChild.WindowState = vbMaximized
Case Else
frmChild.Top = 0
frmChild.Left = 0
End Select
lReturn = RegCloseKey(hWndWindowState)
End Sub

gSetChildFormPosition uses several Registry API calls to work properly. RegOpenKeyEx identifies the key that contains the setup information. RegQueryValueEx provides the setting found under each value under the key. You must always remember to use RegCloseKey to close a key at the end of a Registry operation.

gSaveChildFormPosition (listing 31.6) saves the current position of the child form to the Windows Registry. RegCreateKeyEx provides the window handle of the key containing the information. Notice that you use this call whether the key exists or not. This API call simply opens a key in the same way as RegOpenKeyEx—if that key already exists. You save the new value under the key by using RegSetValueEx. You should never forget to close a key when you are done with one of these Registry operations that use RegCloseKey.

Listing 31.6 The gSetChildFormPosition Routine

Sub gSaveChildFormPosition(frmChild As Form)
Dim hWndQue As Long
Dim hlstrWindowState As String
Dim hlstrTopPosition As String
Dim hlstrLeftPosition As String
Dim lpClass As String
Dim Security As SECURITY_ATTRIBUTES
Dim lpdwDisposition As Long
Dim lReturn As Long
lpClass = Space$(255)
lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Que\ChildForm", _
0&, lpClass, REG_OPTION_NON_VOLATILE, _
KEY_ALL_ACCESS, Security, hWndQue, lpdwDisposition)
If lReturn <> ERROR_SUCCESS Then
gAPIDisplayError lReturn
End If
hlstrWindowState = Trim$(Str$(frmChild.WindowState))
lReturn = RegSetValueEx(hWndQue, "WindowState", 0&, REG_SZ, _
hlstrWindowState, Len(hlstrWindowState))
If lReturn <> ERROR_SUCCESS Then
gAPIDisplayError lReturn
End If
hlstrTopPosition = Trim$(Str$(frmChild.Top))
lReturn = RegSetValueEx(hWndQue, "TopPosition", 0&, REG_SZ, _
hlstrTopPosition, Len(hlstrTopPosition))
If lReturn <> ERROR_SUCCESS Then
gAPIDisplayError lReturn
End If
hlstrLeftPosition = Trim$(Str$(frmChild.Left))
lReturn = RegSetValueEx(hWndQue, "LeftPosition", 0&, REG_SZ, _
hlstrLeftPosition, Len(hlstrLeftPosition))
If lReturn <> ERROR_SUCCESS Then
gAPIDisplayError lReturn
End If
lReturn = RegCloseKey(hWndQue)
End Sub

The following steps show how to make this code work together:

  1. Open new project and rename this project as 31Proj03.
  2. Rename Form1 as frmChild.
  3. Add the code module WINAPI32 from 31Proj02.
  4. Add a new code module Settings from 31Proj02.
  5. Add the routines gSetChildFormPosition and gSaveChildFormPosition to the Settings module.
  6. Add frmMain from 31Proj02, as follows:

    Private Sub Form_Load()
    Call gSetChildFormPosition(Me)
    End Sub
    Private Sub Form_Unload(Cancel As Integer)
    Call gSaveChildFormPosition(Me)
    End Sub

  7. Add the Form_Load code to the load event and unload code to the unload event of frmChild.

Using MDI Design Types

The Windows world has gone MDI (Multiple Document Interface). Most of the applications that you see today use some kind of MDI design. All MDI applications have several common elements. MDI applications consist of a container parent object and children that appear within that parent. Many different kinds of MDI applications have emerged over the years. These different kinds of MDI applications all fall into several general categories: Classic, WorkSpace, WorkBook, Project, and Property Sheet.

The Classic Design

The Classic MDI application works with the members of one data type. Most of the current commercially available Windows applications fit within this category. Applications belonging to this category include programs like Microsoft Word for Windows, Lotus AmiPro, and WordPerfect. Even the MDI example that ships with Visual Basic, MDINote, is an example of this typical MDI design. Figure 31.3 shows what the MDINote application sample looks like.

Fig. 31.3

The look and feel of a Classic MDI application.

The most distinguishing characteristic of the Classic MDI design is that it displays only one type of data object. Microsoft Word for Windows 6.0 (WinWord) is a perfect example of a Classic MDI application. Figure 31.4 shows Word on the screen with three open documents. All the MDI children belong to the same data type, the word processing document.

Fig. 31.4

WinWord with three documents open, demonstrating a Classic MDI design.

The WorkSpace Design

For a long time, the Classic design was really only one kind of MDI application type. As the Windows environment became more popular, companies started to create their own MDI application types. These applications have requirements that do not all fit into the nice, neat picture of one parent with one child type.

Company programers began to create applications using a design that Microsoft calls the WorkSpace design. A WorkSpace application distinguishes itself from other MDI designs by displaying multiple data objects in different children. Figure 31.5 shows an example of a WorkSpace application for the Chicago environment. In this case, the application is a word processor. Notice that two types of objects are on the screen. The folder object represents templates. The text document icon stands for a word processing document.

Fig. 31.5

A word processing application that shows the WorkSpace design.

The process for creating a Visual Basic MDI WorkSpace application is simple. Begin with an MDI parent and add two or more MDI children. You must customize the different MDI children to display different kinds of necessary information. For example, you might choose to create one MDI child for displaying the contents of an invoice and a second to display the individual detail items of an invoice.

The WorkBook Design

MDI applications enable the user to view different kinds of information simultaneously. Sometimes so many children are on the parent that the screen become confusing to the user. In some cases, the user needs access to different kinds of information, but does not need to see all of it at the same time.

The WorkBook design improves on the WorkSpace design to create an application that uses the notebook metaphor. A WorkBook organizes the different kinds of information into a notebook-like display. This kind of display typically uses folder tabs to provide the user with access to the different kinds of information. Figure 31.6 shows Excel 5.0, which is a perfect example of a WorkBook application design. Notice the tabbed children within the main form.

Fig. 31.6

Microsoft Excel 5.0 demonstrating the WorkBook application design.

Some WorkBook designs, like that of Microsoft Excel 5.0, enable the user to work with multiple WorkBooks at the same time. All the user has to do is cycle back and forth between the different WorkBooks. Each of these WorkBooks can contain any of the child objects. In Microsoft Excel 5.0, a WorkBook can contain spreadsheets, pictures, charts, and macro sheets.

The process of creating an effective WorkBook design requires the use of a tab control such as the new 32-bit tab control that ships with Visual Basic 4.0. Several other third-party tab controls are on the market. You can create this kind of design without using a tab control, but such efforts seldom work very effectively in everyday use. The WorkBook design functions better when you need to work with the information as well as organize it.

The Project Design

Another kind of application that works with multiple groupings of information is similar to a WorkBook application but does not use the folder-like tab look and feel. Instead, the presentation looks like a blank screen with a status bar at the bottom. You access the different functions of the application through making choices on this status bar. Windows 95's status bar is the most common example of this Project design.

Figure 31.6 shows what Project applications might look like on the screen. This figure shows Windows 95 using the new interface. Notice the use of the tabs at the bottom of the screen to represent running applications. If you want to organize information instead of working with it, this kind of application style works quite well.

Fig. 31.7

The Windows 95 status bar displaying a Project application.

The Property Sheet Design

The Property Sheet design uses the same tabular look and feel as the WorkBook design. Property Sheets are typically modal dialog boxes that contain so much information that they cannot fit on one screen. For this reason, you use the tabbed look and feel to organize the information by categories.

Figure 31.8 shows a dialog box formatted in the Property Sheet design. In this case, the dialog box is Microsoft Word for Windows 6.0’s Options dialog box. Notice how the tab names organize the information into distinct groupings based on what the user wants to do. This design has a distinct advantage over putting the names for each of these categories on the menu structure. You do not have to go to multiple screens to modify similar information.

Fig. 31.8

Microsoft Word for Windows 6.0’s Options dialog box demonstrating the Property Sheet design.

You save space and simplify the form’s appearance by organizing the information in the property sheet design. Consider what this dialog box would look like if you tried to put all the information on the screen at the same time. Such a dialog box probably would not fit on the screen and also be confusing to read.

Combinations of Styles

The different categories of MDI styles that you have studied in this section are by no means the only ones available. Windows is a living environment that is in a constant state of change based on the needs of users. Toolbars and status bars were not a part of the original versions of Windows applications. Now it is difficult to remember a Windows application lacks both of these attributes.

Toolbars and status bars became a part of the Windows interface because users wanted them. A programmer came up with the idea, which took off from there. Users liked what they saw and asked for it in the other applications that they used.

There is no reason that you cannot combine the different elements of the styles of design described in this chapter to create a new one. Each company's needs differ, so if you think that you need a new way to do things, try it. Other users might like your innovation and before you know it, it will become part of the existing style.

Be careful how you stray from the different styles that you see in other applications. The continuity of Windows interface is what makes many users more comfortable with Windows. When you need to break with the standard, make sure that you do so for good reasons.

From Here...

Forms are the canvases on which you paint your screens. You must be careful to remember that how and where you place your canvases is as important as what you place on them. A form’s position on the screen affects whether the user perceives an application as well or poorly constructed. The overall design of how the forms interact with each other affects how easily a user can work with an application. Always keep these factors in mind.

To learn more about related topics, see the following chapters:


© 1996, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company.