Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.

Channels ▼


A Program Architecture for Visual Basic Development

Source Code Accompanies This Article. Download It Now.

Joachim is director of application development at Academic Advantages and a consultant with Crossbridge Connections. He can be contacted on CompuServe at 72122,144 or on the Internet as [email protected]

Program architecture lies on the fault line between the analysis of functional logic and its actual implementation as a program. While the design of a program is driven by a functional requirement, the architecture is shaped by constraints of technology. It bridges the gap between pure logic and its concrete manifestation as machine code. This makes it a fascinating topic for examination.

Program Architecture

While a program architecture provides constraints and services that appear desirable for a particular project, they are neither practical to enforce through standards nor supported by the implementation technology.

It is interesting to note that there seems to be a correlation between the versatility of an implementation technology and the need to limit its flexibility by rules imposed on its use. If you build a house from scratch, the range of possible shapes is limited only by the building material and the intended use of the structure. If the building material is very versatile, like brick and mortar, there are many different shapes and layouts that can satisfy a desired functionality. It is therefore a good idea to agree on a specific architecture even before drawing up the blueprints. On the other hand, if you use prefabricated elements, the range of choices is limited: A Quonset hut is a Quonset hut is a Quonset hut.

From assembler to OOP, the enforcement of constraints to limit complexity progressively changes from being a set of programming rules (standards), to favoring the architectural approach, to ultimately becoming part of the implementation technology itself; see Figure 1. Anywhere between assembler and OOP the architecture plays an important role in making the technology an effective and usable implementation tool.

Scope and Constraints

The factors that determine the scope of a program architecture include the program usage that the architecture supports, the implementation technology, and the processing mode.

If a program is a one-shot pop that is unlikely to ever get changed, spending time to develop a specific architecture is hard to justify. Often, however, seemingly insignificant programs grow into crucial components of major systems. In such cases, it may become necessary to rewrite the program. A more cautious approach is to use a proven architecture. This will require more overhead than just coding a program without any architectural constraints, but it proves useful if the program turns out to be more important than anticipated.

The other extreme of program usage is full integration into a complex data-processing system. No matter what the implementation technology, specific constraints and services are probably necessary to tie program elements together into a single system. These common constraints and services are at the heart of the system and may justify spending as much as 50 percent of the total system-implementation time on the architecture.

The architecture must provide services and constraints not supported by the implementation technology. For example, if a system provides unsatisfactory communication between calling modules (such as CICS), insufficient error protection (MS-DOS), or unsatisfactory printing routines (Cobol), chances are that an architecture will fill the gap. Of course, if a technology lacks certain fundamental capabilities--such as the ability to process sound files, or to distribute transactions across a network--it may be impractical to rely on an architecture for corrective actions.

Depending on the processing mode, an architecture may have to perform different duties. A batch architecture, even for a complex system, will never need to provide more than just batch services. On the other hand, an architecture for an online, cooperative system must furnish real-time services and possibly extend across different system platforms.

The scope of an architecture depends mostly on functional and systems-related criteria. Its constraints, however, are defined predominantly by the human factor.

First, the architecture has the precise function to enforce simplicity and thus achieve maintainability of even complex programs. It must blot as much complexity from the application logic as possible and impose a documentable, recognizable structure upon the logic that it serves. An architecture must also support program usability by limiting inconsistencies of the user interface within a system and across programs. Users like program access to follow a predictable and reliable pattern.

Finally, the architecture must monitor the use of resources in order to preserve the efficiency of execution. This is particularly important for online programs, as insufficient response time is a major obstacle to the system's acceptance.

In short, an architecture is the result of conflicting requirements (see Figure 2). It takes experience and much attention to detail to achieve a workable compromise. Because of the importance of an architecture to the success of a system, you should spare no effort to achieve the best solution possible for any given set of circumstances.

Architecture Components

An architecture handles data used across several programs or modules. The definition of this data and its structure is the first important component of an architecture. It is equally important to identify the program logic that should be provided by the architecture. This logic will be available from anywhere within the system and is likely to define how all system components interact with each other. Hence, program logic and data definitions are the two dimensions you have to manage to build an effective program architecture.

This is tantamount to requiring that, in order to build a good program architecture, you must know exactly what the complete system will look like and how it will be put together. While system designers generally are perceptive, they are rarely omniscient. It's a good idea then to look for a more practical approach to the design of an architecture.

Architecture Design

Bottom up or top down is the question. Do you first design the system, then devise the architecture, or do you establish the architecture first, then assemble the system to fit the architecture?

The answer isn't simple. We're likely to do neither, or rather, a little bit of both. The process is iterative, with continuous corrective measures. We establish an architecture that provides most of the services and support that we think is needed, and then we proceed with the detail. While designing and implementing the program detail, you may notice additional requirements and go back to modify the architecture. This may affect the programs already designed and written and cause additional modifications. The further along you get, the more far-reaching consequences any change to the program architecture will have. This is a strong incentive to invest a lot of time up front, especially when large programming teams are involved.

There are two notable exceptions to this approach. The first is the commercial availability of established architectures. These include the Hogan umbrella, Foundation, and others. These architectures are likely to be well proven, providing all data handling and all elements of common logic that could possibly be required. Typically, they address complex-systems situations for which adequate implementation technologies are not available. In Figure 1, this situation would be placed somewhere left of the center on the time line.

Moving to the right on that axis, the importance of the architecture itself decreases and the incidence of the implementation technology becomes more relevant. In the second exception, a sufficiently strong implementation technology is used, making the existence of an architecture incidental in the overall scheme of things. Since the architecture has ceased to be a strong component of the application, you are unlikely to invest a lot of effort into it.

Advanced programming languages provide a strong incentive for prototyping and experimentation. If any architectures are needed, they may be developed along the way. Common requirements will come into focus when we experiment with the functionality and implementation techniques.

Visual Basic

Visual Basic (VB) is a fairly advanced programming language. Like Basic, it is an interpreted language that can also be compiled. Unlike Basic, however, VB is event driven and does not adhere to the traditional procedural model.

The procedural model assumes that a central piece of logic has the ultimate control over the program. The concepts of "program start," "program end," and "mainline" are strongly associated with this model. There may be questions on how to break down the logic and manage the data, but control definitely resides within the program logic and all actions and activities are initiated from within that logic.

An event-driven structure, however, relies on distributed logic. Actions and activities are associated with objects that generate events. A screen is an object, a field on the screen is an object, a button in a field on a screen is an object, and so on. Each object generates events through some sort of interaction with the world (a mouse click, for example). This starts the execution of any logic previously associated with that event. The important difference from the procedural model is that the logic associated with this event is self-contained; no centralized piece of logic is aware of all events being triggered. The event-driven program operates like an ecology: All elements of the whole act separately, but work together to achieve a common goal. The fact that events can be started independently leads to their potential concurrence. Hence, program execution is never quite deterministic, as separate events may start at different times and interact with each other in different ways.

All these interactions can grow very complex. For this reason, an event-driven programming language has a more-solid structure and clearer constraints than any procedural language. Much of the logic and structure that traditional systems enforce via an architecture are intrinsic to the event-driven approach and thus to the programming language itself. A good example is the way data is handled. Some data is temporary within the event procedure, other data is shared across procedures or even within the whole system and remains available throughout the life of a program. The definition and management of the various types of data is inherent to Visual Basic and does not have to be provided by an architecture.

You might rightfully ask, if the structure is so strong, does VB really need a program architecture? The answer depends on what you want to do. I found VB to be fairly good at handling most structural requirements, even of a complex system with multiple screens. Where an architecture came in handy, though, was in ensuring consistency across screens.

Visual Basic places no limitations on what goes where on the screen, and no areas are reserved for any special purpose. Nevertheless, in one recent project, we wanted to provide a navigation aid to users. We decided to reserve the top area of all major screens for information about the data being processed. To look stable and reassuring, that area had to remain consistent and perfectly aligned across multiple screens. This required a special mechanism not available through the programming language.

An architecture might also be necessary because VB forms (which define a unit of logic consisting of a window, its controls, and associated logic and variables) do not have an intrinsic awareness of where they are called from. They are loaded and activated, then perform whatever logic is necessary, and relinquish control to whatever code started them. If the execution logic is supposed to vary depending on where the form was called from, some customized code needs to be devised.

Another example is the the management of asynchronous processing. Simply put, VB asynchronous processing lacks finesse. If you plan to rely on asynchronous processing in any way at all, allow yourself extra time to develop a managing mechanism that handles conflicts arising from concurrent and recursive execution and enforces dependencies.

An Example

I was recently part of a team that designed a reasonably complex, commercial information-management program encompassing about 35,000 lines of code written in Visual Basic.

When we started out, we assumed that VB would be able to support all our structural requirements. It was only during the protoyping of the screens that we realized the usefulness of a mechanism that would ensure a consistent look and feel across several of our screens.

In particular, we decided that at any given time, any of the three major screens were to provide information about the data being processed. To make things easy and natural, this information was to remain displayed in the same location--the upper portion of the screen--independently from the data window opened at any given time. User interaction was most successful when the area containing that information looked as if it were hovering above, almost as if it were no part of the screen proper. A change of data windows had to leave the information area unaffected, unless, of course, the data being displayed changed.

Figure 3 shows one of the three screens that displays this information area, located immediately below the menu bar. There is one panel containing two fields. The right field provides information about the file that is currently open. The left field provides details about the item being accessed. Since the entire application relies heavily on graphical symbols, these fields must be capable of displaying icons, in addition to character information.

The status of both fields must be clear to the user. Figure 4 shows a progression of information areas captured in various situations. When no file is open, the Item field is invisible, since no item can be selected. If a file is open, the Item field either shows the name of a selected item or conveys the request that an item is to be selected. Whenever valid information is associated with the field, the background color is yellow. If no information is selected or the information is incomplete, the background of the field is white. When an action request is stated, like <click> to select a DATABASE, this request is displayed in red on white background, and the field must be clickable. In addition, for user convenience, either field is clickable when associated with a screen that allows the selection of a database or of an item. The field always contains an image that varies depending on the situation being addressed.

All this sounds complicated. However, as a user interface it comes across quite naturally, communicating through colors, images, and characters, making the structure of the message very intuitive.

Once we defined our intentions, we had to seek a way to implement them. We explored several alternatives and found that only a few of them were suitable for implementation. I'll briefly discuss the options that we discarded.

The first (and possibly most obvious) way to manage the screens would be to display the information area in a window of its own. This window would be visible together with any of the data windows. This would require that for every screen change, not just one but two windows would need to be managed and synchronized. While this adds complexity, it can be implemented, but there are other difficulties to be reckoned with.

The menu bar and title are always displayed along the top of a window. While we chose not to use a menu bar or to suppress the title, we didn't have the option of displaying it in other than the standard system location.

If we had chosen to display the information fields in a window of their own and programmatically placed this window above the data window, the title and menu bar of the data window would be sandwiched between the two windows. This would be an entirely wrong location. To remain consistent with the general windows interface, a menu must be above the area that a user recognizes as a work area and not in the middle of it.

An alternative was to place the menu and its management within the confines of the information window. In this case, the logic of the information window would need to associate the appropriate menu choices with the active data windows and convey menu selections to the program logic of that window. This was feasible but too complex for the simple goal that we had in mind.

We were then tempted to define a window so that it seems to float on top of all other windows displayed on the screen. To do that, you use the TOPMOST option of the SetWindowPos API, which works like the Always on Top option of Windows help. We considered defining a separate information window in this manner. This solution was aesthetically pleasing, leaving the user in full control of where to drag the information window.

The flexibility of that approach, however, also carried a price: The user would have to worry about placing the information window on the screen and moving it to where data would not be covered, thus making the interface more complex rather than less.

Additionally, there was an architectural problem with this solution. The topmost window will remain topmost even if the associated application ceases to have the focus. In other words, if we Alt+Tab from our information-management application with an open topmost window into an unrelated application, the deactivated topmost window would remain visible on the screen, on top of all other windows. This can be addressed programmatically but requires extra logic for proper management. More importantly, we found that in the case of system messages or the display of any other small modal windows, deadlock situations could arise. A topmost window can hide a modal window, preventing the latter from being accessed. Yet, the modal window requires service before the topmost window can be moved. At this point, the application is in deadlock and needs to be restarted. Any preventive mechanism would be quite delicate, so we preferred to avoid the risk of locking up the system entirely. We also did not like that an API call would be needed, hence bypassing VB. This may have affected future portability of the code, so we decided to abandon this approach, as well.

As we considered the problem further, a less-sophisticated solution ultimately prevailed. Technically straightforward, it has the advantage of being very efficient and easy to use. In essence, it requires that the major screens be equipped with a predefined information area and that some common method be found to align and display data in those areas.

Contrary to the other alternatives, the information area is now part of the data window; however, the managing mechanism is centralized and interacts with each form in identical fashion. Besides managing the data displayed and the characteristics of the display fields, this mechanism also manages the relative location of the fields within the open window. By ensuring that the relative position is the same across different windows, we achieve the impression that the window changes, but the information field does not.

An Overview of Our Approach

It would have been nice if VB had stronger object orientation, because, if an object on a form were being reused elsewhere, we would only need to refer to that object at run time and use its data and logic as required. Unfortunately, this isn't possible with VB because the only objects for which new instances can be created are forms. Fortunately, a little more coding yields an equally satisfactory result.

Any of the windows that are to contain an information area require the component objects to be explicitly defined. Their location, size, content, and the like, do not have to be preset because properties and data can be manipulated at run time.

Referring again to Figure 4, you have to decide what type of objects are required. The information area consists of one panel with a three-dimensional look and two fields that contain both an image and some characters.

Functionally, the panel is optional. However, we decided that the improvement to the look and feel would be worth the added overhead. A solid gray 3-D Panel Control suited our needs.

The two fields embedded within the panel are Grid Controls displaying a single cell. The Grid is the only basic control (an object associated with a Visual Basic form) that displays character information and images with equal ease. At the same time, it intercepts Click and MouseMove events and supports the BackColor and ForeColor properties, which we can use to conveniently change the color of the fields.


At this point, we determined that each information area would consist of one 3-D Panel Control and two Grid Controls within the panels. Listing One shows the code that defines these controls. It identifies the information area with the name panTopPanel and specifies the controls grdItemName and grdFileName as contained within that panel. Since the properties of these tools will be set at run time, their initialization is not strictly necessary. It is just a sensible precaution against possible oversights.

In addition to the mere definition of the tools, code must be provided to manage the information area. Prior to window activation, the information area must be initialized; after the deactivation of the screen, the then-current information area must be preserved and passed on to the next screen. Contrary to the display controls themselves, the code for this logic does not have to be included in every form but can be shared from a central library. Figure 5 shows the modules of this architecture and their relationship with the rest of the program.

Remember that the data from an information area needs to be passed from form to form. There are various ways to accomplish this. Intuitively, you might think of copying the information at every screen change directly from one window to the next window. This avoids the use of internal storage, but it requires the copying logic to have an awareness of both the source and the target screen. In addition, both screens must be present in memory at the same time. We found that updating fields upon activation and saving fields upon deactivation would provide a more flexible and consistent approach. Therefore, we allocated a storage area for the information data.

Listing Two is the definition of the variables in which the properties of panTopPanel and its two controls are being stored. There are two interesting anomalies you may notice when looking at these definitions. The first regards the variables for the column width and row height of the two grid controls. While we know from Figure 4 that only one cell of the grid control is being displayed, the variable for the column width and height is an array of two, allowing you to store values for two separate rows and columns. There is a good reason for this: The grid is not defined as a single cell but rather as a two-by-two. Row 0 and column 0 are defined with a nominal width and height that renders them invisible. Only the cell at the intersection of row 1 with column 1 is visible. This is because, in a grid, the active cell is always highlighted, meaning that the bitmap of the content is inverted (white to black, red to green, and so on). If there is only one cell in the grid, this is the only cell that can be active, and an occasional inversion of the bitmap does not look good. Therefore, we defined the additional row and column of negligible height and width and made sure that only the invisible cells are ever activated.

Note that there's no field in which to store the images from grdItemName and grdFileName. Figure 4 shows that there are always images in the information field. They must be saved somewhere, but rather than saving them in a variable, we found it easier to write them to a temporary file. The VB language has the commands to do so. Performance is essentially unaffected by this choice, since the bit string containing the images remains in the data buffer most of the time, remaining available for immediate access. Writing the file seems to be asynchronous so it's not on the critical path to efficient performance.

Figure 5 shows that the variables that store the architecture's properties must be initialized before any display logic is executed. This is implemented in Listing Three . These settings define the defaults. Note that there are no defaults for the text and the image displayed in the information field. The grid content is initialized by the program logic of the first form executed.

All properties set through this procedure can be modified by the program code. The architecture will accept these modifications and propagate them to other windows. In reality, however, only a very few properties of the controls defined by these variables will be changed programmatically. The defaults set here define the look and feel of the information area across the program and, therefore, have to be chosen with care.

The Code

The architecture consists of three program modules: gpTopPanelSave writes the data from the information area to memory, gpTopPanelInit uses the data stored in memory to initialize the information area of a screen, and gpTopPanelItem allows controlled changes to the Item field of the information area.

Listing Four is the gpTopPanelInit code, which consists of three parts that initialize the panel and two grids, respectively. Each part is preceded by a statement that determines how much of the panel and the grids needs to be initialized. If the format is already established, only the data is restored. This eliminates unnecessary overhead without reducing functionality.

This procedure requires the form of the calling program to be passed as a parameter. It is invoked by gpTopPanelInit Me. The Me identifies the form with which the calling logic is associated. The invoking statement should be triggered by the activation of every form that contains an information area. This ensures that the information area is refreshed whenever any window with an information area receives user focus.

The grids are defined as having four cells, out of which only the bottom-right cell with the coordinates 1,1 is visible. You must be careful to use the row and col properties to position the logic on the valid cell before moving text into the grid. Also interesting, and very important, is the way we are dealing with the images. Remember that we decided not to store the images in memory variables, but to place them in temporary files. This isn't a problem with LoadPicture. However, the present logic might be engaged to initially load the information area, before any image is stored on file. You must therefore allow for enough resiliency to correctly deal with a situation where no file can be accessed. LoadPicture generates an error if a specified file is not found. Therefore, we use the dir$ command to determine whether the temporary file exists on file. If it does not, an empty string is passed to LoadPicture rather than the filename itself. An empty string does not trigger an error condition, it just causes an empty image to be generated and stored in the grid. After startup, the logic of the first form will load images into both grids.

gpTopPanelSave (see Listing Five) is the counterpart to gpTopPanelInit. Instead of restoring the properties of the information area, it saves them. We don't have to worry about nonexisting files, nor do we allow saving partial data. This simplifies the code somewhat. This procedure is invoked by gpTopPanelSave Me. In keeping with the symmetrical nature of this approach, the statement is triggered by the deactivation of a form that contains an information area.

In addition to these two procedures, we found it useful to have a third procedure to change the content of the Item field. Functionally, this isn't necessary, because the content and the definition of the information area can be changed directly from the program code. All such changes would be captured and saved by the gpTopPanelSave procedure. However, the information-area fields should be uniform, no matter when, and from where they are updated. The Item field is updated from different locations. Thus, we chose to build a common update procedure that changes or resets the Item-field text or replaces the image. It is called gpTopPanelItem (see Listing Six).

This procedure requires three parameters: the form from which it is called, an optional text, and the image to be displayed. The Text field can be either null, empty, or contain a valid string. When it is null, a red default string with the invitation to click on the field is displayed on a white background. When the text field is empty, but not null, no text is displayed, and the background is white. When it contains a valid string, the Item area is initialized to a yellow background, and the characters of text are displayed in black, using the default-character options selected by the user. There must always be a valid image, and that image is always displayed.


The approach to program architecture presented here is simple and straightforward. It allows for speedy processing: Properties and data are stored in main memory, and access is very fast. Pictures are stored in and retrieved from files, but buffer handling makes file access transparent.

All calls are very simple. Instead of a long series of parameters, we pass the whole form and let the called procedure sort out which parts to access. This makes the procedures easy to use, as well as cutting down on programming time.

Repetitive code is hidden by the calls to those procedures. The code is reused many times but exists only once and is stored in centralized procedures. This reduces redundancy, thus improving development time and simplifying maintenance.

Overall, we found that the architecture described in this article complemented the native Visual Basic services very well and provided just the enhancement needed.

Figure 1 Enforcement of constraints to limit complexity changes from being provided by standards to being embedded in the programming language.

Figure 2 Program analysis and implementation: An architecture is the result of conflicting requirements.

Figure 3 One of the three screens displaying the information area immediately below the menu bar.

Figure 4 A progression of information areas captured in various situations.

Figure 5 Modules of this architecture and their relationship with the rest of the program. Elements of the architecture are shown in blue.

Listing One

Begin SSPanel panToppanel 
   Alignment       =   0  'Left Justify - TOP
   BackColor       =   &H00C0C0C0&
   BevelWidth      =   2
   BorderWidth     =   4
   FloodShowPct    =   0   'False
   Font3D          =   3  'Inset w/light shading
   ForeColor       =   &H00000000&
   Height          =   612
   HelpContextID   =   2
   Left            =   60
   TabIndex        =   0
   Top             =   60
   Width           =   9612
   Begin Grid grdItemName 
      BackColor       =   &H00FFFFFF&
      BorderStyle     =   0  'None
      FixedCols       =   0
      FixedRows       =   0
      FontBold        =   0   'False
      FontItalic      =   0   'False
      FontName        =   "Arial"
      FontSize        =   9
      FontStrikethru  =   0   'False
      FontUnderline   =   0   'False
      ForeColor       =   &H000000FF&

      GridLines       =   0   'False
      Height          =   435
      HelpContextID   =   3
      HighLight       =   0   'False
      Left            =   120
      ScrollBars      =   0  'None
      TabIndex        =   5
      Top             =   120
      Width           =   3300
   Begin Grid grdFileName 
      BackColor       =   &H00FFFFFF&
      BorderStyle     =   0  'None
      FixedCols       =   0
      FixedRows       =   0
      FontBold        =   -1  'True
      FontItalic      =   0   'False
      FontName        =   "Arial"
      FontSize        =   9
      FontStrikethru  =   0   'False
      FontUnderline   =   0   'False
      ForeColor       =   &H00000000&
      GridLines       =   0   'False
      Height          =   435
      HelpContextID   =   4
      HighLight       =   0   'False
      Left            =   4680
      ScrollBars      =   0  'None
      TabIndex        =   4
      Top             =   120
      Width           =   3375

Listing Two

'Global variables defining the TopPanel of the data gathering and 
' management forms. TopPanel provides navigational aid to the user. 
' Upon activation or deactivation of a form the TopPanel information is 
' kept current by the architecture. If the processing status changes, 
' program logic will update the fields directly.

'      variables to store properties of TopPanel
Global gvTopPanelAlign      As Integer
Global gvTopPanelAlignment  As Integer
Global gvTopPanelAutosize   As Integer
Global gvTopPanelBackColor  As Long
Global gvTopPanelBevelInner As Integer
Global gvTopPanelBevelOuter As Integer
Global gvTopPanelBevelWidth As Integer
Global gvTopPanelBorderWidth    As Integer
Global gvTopPanelCaption    As String
Global gvTopPanelDragIcon   As Integer
Global gvTopPanelDragMode   As Integer
Global gvTopPanelEnabled    As Integer
Global gvTopPanelFloodColor As Long
Global gvTopPanelFloodPercent   As Integer
Global gvTopPanelFloodShowPct   As Integer
Global gvTopPanelFloodType      As Integer
Global gvTopPanelFont3d         As Integer
Global gvTopPanelFontBold   As Integer
Global gvTopPanelFontItalic As Integer
Global gvTopPanelFontName   As String
Global gvTopPanelFontSize   As Single
Global gvTopPanelFontStrikeThru As Integer
Global gvTopPanelFontUnderline As Integer
Global gvTopPanelForeColor  As Long
Global gvTopPanelHeight     As Single
Global gvTopPanelHelpContextID  As Long
'note "hWnd" is read only at exec time; we shall not store it
'note "Index" is read only at exec time; we shall not store it
Global gvTopPanelIndex      As Integer
Global gvTopPanelLeft       As Single
Global gvTopPanelMousePointer   As Integer
Global gvTopPanelName       As String
Global gvTopPanelOutline    As Integer
'note "Parent" is read only at exec time; we shall not store it
Global gvTopPanelRoundedCorners As Integer
Global gvTopPanelShadowColor    As Integer
Global gvTopPanelTabindex   As Integer
Global gvTopPanelTag        As String
Global gvTopPanelTop        As Single
Global gvTopPanelVisible    As Integer
Global gvTopPanelWidth      As Single

'Variables to store properties of grdFileName which is part of TopPanel
'grdFileName is a Grid and has the corresponding Variables
Global gvFilenameBackColor  As Long
Global gvFilenameBorderStyle   As Integer
Global gvFileNameCellSelected As Integer
Global gvFileNameClip As String
Global gvFileNameCol As Integer
Global gvFileNameColAlignment As Integer
Global gvFilenameCols As Integer
Global gvFilenameColWidth(0 To 1) As Long
Global gvFileNameDragIcon   As Integer
Global gvFilenameDragmode   As Integer
Global gvFilenameEnabled    As Integer
Global gvFilenameFillstyle As Integer
Global gvFileNameFixedAlignment As Integer
Global gvFilenameFixedCols As Integer
Global gvFilenameFixedRows As Integer
Global gvFilenameFontBold   As Integer
Global gvFilenameFontItalic As Integer
Global gvFilenameFontName   As String
Global gvFilenameFontSize   As Single
Global gvFilenameFontstrikethru As Integer
Global gvFilenameFontUnderline As Integer
Global gvFilenameForecolor  As Long
Global gvFilenameGridLines As Integer
Global gvFilenameHeight As Single
Global gvFilenameHelpContextID  As Long
Global gvFilenameHighlight As Integer
Global gvFilenameLeft       As Single
Global gvFileNameLeftCol As Integer
Global gvFileNameName       As String
'note "Parent" is read only at exec time; we shall not store it
Global gvFilenamePicture As Variant
Global gvFileNameRow As Integer
Global gvFilenameRowHeight(0 To 1) As Long
Global gvFilenameRows As Integer
Global gvFilenameScrollbars As Integer
Global gvFileNameSelEndCol As Integer
Global gvFileNameSelEndRow As Integer
Global gvFileNameTag        As String
Global gvFileNameText       As String
'Global goDataBase As String    'defined as global option
Global gvFilenameTop        As Single
Global gvFileNameTopRow As Integer
Global gvFilenameVisible    As Integer
Global gvFilenameWidth      As Single

'Variables to store properties of grdItemName which is part of TopPanel
'grdItemName is a Grid and has the corresponding Variables
Global gvItemNameBackColor  As Long
Global gvItemNameBorderStyle    As Integer
Global gvItemNameCellSelected As Integer
Global gvItemNameClip As String
Global gvItemNameCol As Integer
Global gvItemNameColAlignment As Integer
Global gvItemNameCols As Integer
Global gvItemNameColWidth(0 To 1) As Long
Global gvItemNameDragIcon   As Integer
Global gvItemNameDragmode   As Integer
Global gvItemNameEnabled    As Integer
Global gvItemNameFillstyle As Integer
Global gvItemNameFixedAlignment As Integer
Global gvItemNameFixedCols As Integer
Global gvItemNameFixedRows As Integer
Global gvItemNameFontBold   As Integer
Global gvItemNameFontItalic As Integer
Global gvItemNameFontName   As String
Global gvItemNameFontSize   As Single
Global gvItemNameFontstrikethru As Integer
Global gvItemNameFontUnderline As Integer
Global gvItemNameForecolor  As Long
Global gvItemNameGridLines As Integer
Global gvItemNameHeight     As Single
Global gvItemNameHelpContextID  As Long
Global gvItemNameHighlight As Integer
Global gvItemNameLeft       As Single
Global gvItemNameLeftCol As Integer
Global gvItemNameName       As String
'note "Parent" is read only at exec time; we shall not store it
Global gvItemNamePicture
Global gvItemNameRow As Integer
Global gvItemNameRowHeight(0 To 1) As Long
Global gvItemNameRows As Integer
Global gvItemNameScrollbars As Integer
Global gvItemNameSelEndCol As Integer
Global gvItemNameSelEndRow As Integer
Global gvItemNameTag        As String
Global gvItemNameText As String
Global gvItemNameTop        As Single
Global gvItemNameTopRow As Integer
Global gvItemNameVisible    As Integer
Global gvItemNameWidth      As Single

Listing Three

'Initialize variables that store properties of TopPanel
     gvTopPanelAlign = 0
     gvTopPanelAlignment = 0
     gvTopPanelAutosize = 0
     gvTopPanelBackColor = &HC0C0C0
     gvTopPanelBevelInner = 0
     gvTopPanelBevelOuter = 2
     gvTopPanelBevelWidth = 2
     gvTopPanelBorderWidth = 4
     gvTopPanelCaption = ""
     gvTopPanelDragMode = 0
     gvTopPanelEnabled = True
     gvTopPanelForeColor = &H0&
     gvTopPanelFontName = "Arial"
     gvTopPanelFontSize = 9.6
     gvTopPanelHeight = 615
     gvTopPanelHelpContextID = 0
     gvTopPanelLeft = 105
     gvTopPanelOutline = False
     gvTopPanelRoundedCorners = True
     gvTopPanelShadowColor = 0
     gvTopPanelTop = 75
     gvTopPanelVisible = True
     gvTopPanelWidth = 9345

'Initialize variables that store properties of grdFileName 
'  which is part of TopPanel
     gvFilenameBackColor = &HFFFFFF
     gvFilenameBorderStyle = 0
     gvFilenameCols = 2
     gvFilenameColWidth(0) = 1
     gvFilenameColWidth(1) = 3500
     gvFilenameDragmode = 0
     gvFilenameEnabled = True
     gvFilenameFillstyle = 0
     gvFilenameFixedCols = 0
     gvFilenameFixedRows = 0
     gvFilenameFontBold = False
     gvFilenameFontItalic = False
     gvFilenameFontName = "Arial"

     gvFilenameFontSize = 9.75
     gvFilenameFontstrikethru = False
     gvFilenameFontUnderline = False
     gvFilenameForecolor = &H0&
     gvFilenameGridLines = False
     gvFilenameHeight = 500
     gvFilenameHelpContextID = 0
     gvFilenameHighlight = False
     gvFilenameLeft = 3900
     gvFilenameRows = 2
     gvFilenameRowHeight(1) = 425
     gvFilenameRowHeight(0) = 1
     gvFilenameScrollbars = 0
     gvFilenameTop = 75
     gvFilenameVisible = True
     gvFilenameWidth = 3501

'Initialize variables that store properties of grdItemName 
'    which is part of TopPanel
     gvItemNameBackColor = &HFFFFFF
     gvItemNameBorderStyle = 0
     gvItemNameCols = 2
     gvItemNameColWidth(0) = 1
     gvItemNameColWidth(1) = 3500
     gvItemNameDragmode = 0
     gvItemNameEnabled = True
     gvItemNameFillstyle = 0
     gvItemNameFixedCols = 0
     gvItemNameFixedRows = 0
     gvItemNameFontBold = goListFontBold
     gvItemNameFontItalic = goListFontItalic
     gvItemNameFontName = goListFontName
     gvItemNameFontSize = goListFontSize
     gvItemNameFontstrikethru = False
     gvItemNameFontUnderline = False
     gvItemNameForecolor = &H0&
     gvItemNameGridLines = False
     gvItemNameHeight = 500
     gvItemNameHelpContextID = 0
     gvItemNameHighlight = False
     gvItemNameLeft = 120
     gvItemNameRows = 2
     gvItemNameRowHeight(1) = 425
     gvItemNameRowHeight(0) = 1
     gvItemNameScrollbars = 0
     gvItemNameTop = 75
     gvItemNameVisible = True
     gvItemNameWidth = 3501

Listing Four

Sub gpTopPanelInit (pForm As Form)

    'define the path for temp files
    tPath$ = App.Path  'put temp files into application directory
    If Right$(tPath$, 1) <> "\" Then tPath$ = tPath$ & "\"  
                                               'terminate path with backslash
     'panel proper
     If pForm.panToppanel.Width = gvTopPanelWidth And 
        pForm.panToppanel.Height = gvTopPanelHeight And 
pForm.panToppanel.Top = gvTopPanelTop Then
        'do nothing - panel is already established
         pForm.panToppanel.Align = gvTopPanelAlign
         pForm.panToppanel.Alignment = gvTopPanelAlignment
         pForm.panToppanel.AutoSize = gvTopPanelAutosize
         pForm.panToppanel.BackColor = gvTopPanelBackColor
         pForm.panToppanel.BevelInner = gvTopPanelBevelInner
         pForm.panToppanel.BevelOuter = gvTopPanelBevelOuter
         pForm.panToppanel.BevelWidth = gvTopPanelBevelWidth
         pForm.panToppanel.BorderWidth = gvTopPanelBorderWidth
         pForm.panToppanel.Caption = gvTopPanelCaption
         pForm.panToppanel.DragMode = gvTopPanelDragMode
         pForm.panToppanel.Enabled = gvTopPanelEnabled
         pForm.panToppanel.FloodColor = gvTopPanelFloodColor
         pForm.panToppanel.FloodPercent = gvTopPanelFloodPercent
         pForm.panToppanel.FloodShowPct = gvTopPanelFloodShowPct
         pForm.panToppanel.FloodType = gvTopPanelFloodType
         pForm.panToppanel.Font3D = gvTopPanelFont3d
         pForm.panToppanel.FontBold = gvTopPanelFontBold
         pForm.panToppanel.FontItalic = gvTopPanelFontItalic
         pForm.panToppanel.FontName = gvTopPanelFontName
         pForm.panToppanel.FontSize = gvTopPanelFontSize
         pForm.panToppanel.FontStrikethru = gvTopPanelFontStrikeThru
         pForm.panToppanel.FontUnderline = gvTopPanelFontUnderline
         pForm.panToppanel.ForeColor = gvTopPanelForeColor
         pForm.panToppanel.Height = gvTopPanelHeight
         pForm.panToppanel.HelpContextID = gvTopPanelHelpContextID
         pForm.panToppanel.Left = gvTopPanelLeft
         pForm.panToppanel.MousePointer = gvTopPanelMousePointer
         pForm.panToppanel.Outline = gvTopPanelOutline
         pForm.panToppanel.RoundedCorners = gvTopPanelRoundedCorners
         pForm.panToppanel.ShadowColor = gvTopPanelShadowColor
         pForm.panToppanel.TabIndex = gvTopPanelTabindex
         pForm.panToppanel.Tag = gvTopPanelTag
         pForm.panToppanel.Top = gvTopPanelTop
         pForm.panToppanel.Visible = gvTopPanelVisible
         pForm.panToppanel.Width = gvTopPanelWidth
     End If

    'itemname: Name grid on Panel
    If pForm.grdItemname.Top = gvItemNameTop And 
     pForm.grdItemname.Width = gvItemNameWidth And 
pForm.grdItemname.Height = gvItemNameHeight Then
        'grid is already established - rewrite only content
        pForm.grdItemname.FontBold = gvItemNameFontBold
        pForm.grdItemname.FontItalic = gvItemNameFontItalic
        pForm.grdItemname.FontName = gvItemNameFontName
        pForm.grdItemname.FontSize = gvItemNameFontSize
        pForm.grdItemname.FontStrikethru = gvItemNameFontstrikethru
        pForm.grdItemname.FontUnderline = gvItemNameFontUnderline
        pForm.grdItemname.ForeColor = gvItemNameForecolor
        pForm.grdItemname.BackColor = gvItemNameBackColor
        pForm.grdItemname.Row = 1: pForm.grdItemname.Col = 1: 
              pForm.grdItemname.Text = gvItemNameText

        temp$ = Dir$(tPath$ & "smarta01.tmp")             
                                            ' empty if File does not exist
        If temp$ <> "" Then temp$ = tPath$ & temp$        
                                 ' Dir$ does not list subdirectory.  
                                 '     Add it to name if found!
        pForm.grdItemname.Picture = LoadPicture(temp$)    
                              ' set picture to retrived info.  Empty if none

    Else  'grid not established - set grid and rewrite content
        pForm.grdItemname.BackColor = gvItemNameBackColor
        pForm.grdItemname.BorderStyle = gvItemNameBorderStyle
        pForm.grdItemname.Cols = gvItemNameCols
        pForm.grdItemname.ColWidth(0) = gvItemNameColWidth(0)
        pForm.grdItemname.ColWidth(1) = gvItemNameColWidth(1)
        pForm.grdItemname.DragMode = gvItemNameDragmode
        pForm.grdItemname.Enabled = gvItemNameEnabled
        pForm.grdItemname.FillStyle = gvItemNameFillstyle
        pForm.grdItemname.FixedCols = gvItemNameFixedCols
        pForm.grdItemname.FixedRows = gvItemNameFixedRows
        pForm.grdItemname.FontBold = gvItemNameFontBold
        pForm.grdItemname.FontItalic = gvItemNameFontItalic
        pForm.grdItemname.FontName = gvItemNameFontName
        pForm.grdItemname.FontSize = gvItemNameFontSize
        pForm.grdItemname.FontStrikethru = gvItemNameFontstrikethru
        pForm.grdItemname.FontUnderline = gvItemNameFontUnderline
        pForm.grdItemname.ForeColor = gvItemNameForecolor
        pForm.grdItemname.GridLines = gvItemNameGridLines
        pForm.grdItemname.Height = gvItemNameHeight
        pForm.grdItemname.HelpContextID = gvItemNameHelpContextID
        pForm.grdItemname.HighLight = gvItemNameHighlight
        pForm.grdItemname.Left = gvItemNameLeft
        pForm.grdItemname.RowHeight(0) = gvItemNameRowHeight(0)
        pForm.grdItemname.RowHeight(1) = gvItemNameRowHeight(1)
        pForm.grdItemname.Rows = gvItemNameRows
        pForm.grdItemname.ScrollBars = gvItemNameScrollbars
        pForm.grdItemname.Row = 1: pForm.grdItemname.Col = 1: 
                                   pForm.grdItemname.Text = gvItemNameText
        pForm.grdItemname.Top = gvItemNameTop
        pForm.grdItemname.Visible = gvItemNameVisible
        pForm.grdItemname.Width = gvItemNameWidth

        temp$ = Dir$(tPath$ & "smarta01.tmp")          
                                        ' empty if File does not exist
        If temp$ <> "" Then temp$ = tPath$ & temp$     
                                ' Dir$ does not list subdir.  Add it if found!
        pForm.grdItemname.Picture = LoadPicture(temp$) 
                                ' set picture to retrived info.  Empty if none
    End If

    'Filename: Name grid on Panel
    If pForm.grdFilename.Width = gvFilenameWidth And 
         pForm.grdFilename.Top = gvFilenameTop And 
pForm.grdFilename.Height = gvFilenameHeight Then
        'grid is already established - rewrite only content
        pForm.grdFilename.FontBold = gvFilenameFontBold
        pForm.grdFilename.BackColor = gvFilenameBackColor
        pForm.grdFilename.FontItalic = gvFilenameFontItalic
        pForm.grdFilename.FontName = gvFilenameFontName
        pForm.grdFilename.FontSize = gvFilenameFontSize
        pForm.grdFilename.FontStrikethru = gvFilenameFontstrikethru
        pForm.grdFilename.FontUnderline = gvFilenameFontUnderline
        pForm.grdFilename.ForeColor = gvFilenameForecolor
        pForm.grdFilename.Row = 1: pForm.grdFilename.Col = 1: 
                                   pForm.grdFilename.Text = gvFileNameText
        temp$ = Dir$(tPath$ & "smarta02.tmp")          
                                   ' empty if File does not exist
        If temp$ <> "" Then temp$ = tPath$ & temp$     
                                   ' Dir$ does not list subdir.  Add if found!
        pForm.grdFilename.Picture = LoadPicture(temp$) 
                                  ' set picture to retrived info. Empty if none

    Else 'grid not established - set grid and rewrite  content
        pForm.grdFilename.BackColor = gvFilenameBackColor
        pForm.grdFilename.BorderStyle = gvFilenameBorderStyle
        pForm.grdFilename.Cols = gvFilenameCols
        pForm.grdFilename.ColWidth(0) = gvFilenameColWidth(0)
        pForm.grdFilename.ColWidth(1) = gvFilenameColWidth(1)
        pForm.grdFilename.DragMode = gvFilenameDragmode
        pForm.grdFilename.Enabled = gvFilenameEnabled
        pForm.grdFilename.FillStyle = gvFilenameFillstyle
        pForm.grdFilename.FixedCols = gvFilenameFixedCols
        pForm.grdFilename.FixedRows = gvFilenameFixedRows
        pForm.grdFilename.FontBold = gvFilenameFontBold
        pForm.grdFilename.FontItalic = gvFilenameFontItalic
        pForm.grdFilename.FontName = gvFilenameFontName
        pForm.grdFilename.FontSize = gvFilenameFontSize
        pForm.grdFilename.FontStrikethru = gvFilenameFontstrikethru
        pForm.grdFilename.FontUnderline = gvFilenameFontUnderline
        pForm.grdFilename.ForeColor = gvFilenameForecolor
        pForm.grdFilename.GridLines = gvFilenameGridLines
        pForm.grdFilename.Height = gvFilenameHeight
        pForm.grdFilename.HelpContextID = gvFilenameHelpContextID
        pForm.grdFilename.HighLight = gvFilenameHighlight
        pForm.grdFilename.Left = gvFilenameLeft
        pForm.grdFilename.RowHeight(0) = gvFilenameRowHeight(0)
        pForm.grdFilename.RowHeight(1) = gvFilenameRowHeight(1)
        pForm.grdFilename.Rows = gvFilenameRows
        pForm.grdFilename.ScrollBars = gvFilenameScrollbars
        pForm.grdFilename.Row = 1: pForm.grdFilename.Col = 1: 
                                      pForm.grdFilename.Text = gvFileNameText
        pForm.grdFilename.Top = gvFilenameTop
        pForm.grdFilename.Visible = gvFilenameVisible
        pForm.grdFilename.Width = gvFilenameWidth
        temp$ = Dir$(tPath$ & "smarta02.tmp")          
                                 ' empty if File does not exist
        If temp$ <> "" Then temp$ = tPath$ & temp$     
                                 ' Dir$ does not list subdir.  Add it if found!
        pForm.grdFilename.Picture = LoadPicture(temp$) 
                                 ' set picture to retrived info.  Empty if none
    End If

End Sub

Listing Five

Sub gpTopPanelSave (pForm As Form)
    'define the path for temp files
    tPath$ = App.Path    'put temp files into application directory
    If Right$(tPath$, 1) <> "\" Then tPath$ = tPath$ & "\"  
                                               'terminate path with backslash

    'panel proper
    gvTopPanelAlign = pForm.panToppanel.Align
    gvTopPanelAlignment = pForm.panToppanel.Alignment
    gvTopPanelAutosize = pForm.panToppanel.AutoSize
    gvTopPanelBackColor = pForm.panToppanel.BackColor
    gvTopPanelBevelInner = pForm.panToppanel.BevelInner
    gvTopPanelBevelOuter = pForm.panToppanel.BevelOuter
    gvTopPanelBevelWidth = pForm.panToppanel.BevelWidth
    gvTopPanelBorderWidth = pForm.panToppanel.BorderWidth
    gvTopPanelCaption = pForm.panToppanel.Caption
    gvTopPanelDragMode = pForm.panToppanel.DragMode
    gvTopPanelEnabled = pForm.panToppanel.Enabled
    gvTopPanelFloodColor = pForm.panToppanel.FloodColor
    gvTopPanelFloodPercent = pForm.panToppanel.FloodPercent
    gvTopPanelFloodShowPct = pForm.panToppanel.FloodShowPct
    gvTopPanelFloodType = pForm.panToppanel.FloodType
    gvTopPanelFont3d = pForm.panToppanel.Font3D
    gvTopPanelFontBold = pForm.panToppanel.FontBold
    gvTopPanelFontItalic = pForm.panToppanel.FontItalic
    gvTopPanelFontName = pForm.panToppanel.FontName
    gvTopPanelFontSize = pForm.panToppanel.FontSize
    gvTopPanelFontStrikeThru = pForm.panToppanel.FontStrikethru
    gvTopPanelFontUnderline = pForm.panToppanel.FontUnderline
    gvTopPanelForeColor = pForm.panToppanel.ForeColor
    gvTopPanelHeight = pForm.panToppanel.Height
    gvTopPanelHelpContextID = pForm.panToppanel.HelpContextID
    gvTopPanelLeft = pForm.panToppanel.Left
    gvTopPanelMousePointer = pForm.panToppanel.MousePointer
    gvTopPanelOutline = pForm.panToppanel.Outline
    gvTopPanelRoundedCorners = pForm.panToppanel.RoundedCorners
    gvTopPanelShadowColor = pForm.panToppanel.ShadowColor
    gvTopPanelTabindex = pForm.panToppanel.TabIndex
    gvTopPanelTag = pForm.panToppanel.Tag
    gvTopPanelTop = pForm.panToppanel.Top
    gvTopPanelVisible = pForm.panToppanel.Visible
    gvTopPanelWidth = pForm.panToppanel.Width
    'itemname: Name grid on Panel
    gvItemNameBackColor = pForm.grdItemname.BackColor
    gvItemNameBorderStyle = pForm.grdItemname.BorderStyle
    gvItemNameCols = pForm.grdItemname.Cols
    gvItemNameDragmode = pForm.grdItemname.DragMode
    gvItemNameEnabled = pForm.grdItemname.Enabled
    gvItemNameFillstyle = pForm.grdItemname.FillStyle
    gvItemNameFixedCols = pForm.grdItemname.FixedCols
    gvItemNameFixedRows = pForm.grdItemname.FixedRows
    gvItemNameFontBold = pForm.grdItemname.FontBold
    gvItemNameFontItalic = pForm.grdItemname.FontItalic
    gvItemNameFontName = pForm.grdItemname.FontName
    gvItemNameFontSize = pForm.grdItemname.FontSize
    gvItemNameFontstrikethru = pForm.grdItemname.FontStrikethru
    gvItemNameFontUnderline = pForm.grdItemname.FontUnderline
    gvItemNameForecolor = pForm.grdItemname.ForeColor
    gvItemNameGridLines = pForm.grdItemname.GridLines
    gvItemNameHeight = pForm.grdItemname.Height
    gvItemNameHelpContextID = pForm.grdItemname.HelpContextID
    gvItemNameHighlight = pForm.grdItemname.HighLight
    gvItemNameLeft = pForm.grdItemname.Left
    gvItemNameRows = pForm.grdItemname.Rows
    gvItemNameScrollbars = pForm.grdItemname.ScrollBars
    gvItemNameTop = pForm.grdItemname.Top
    gvItemNameVisible = pForm.grdItemname.Visible
    gvItemNameWidth = pForm.grdItemname.Width
    gvItemNameColWidth(0) = pForm.grdItemname.ColWidth(0)
    gvItemNameColWidth(1) = pForm.grdItemname.ColWidth(1)
    gvItemNameRowHeight(0) = pForm.grdItemname.RowHeight(0)
    gvItemNameRowHeight(1) = pForm.grdItemname.RowHeight(1)
    pForm.grdItemname.Row = 1: pForm.grdItemname.Col = 1
    SavePicture pForm.grdItemname.Picture, tPath$ & "smarta01.tmp"
    gvItemNameText = pForm.grdItemname.Text
    'Filename: File grid on Panel
    gvFilenameBackColor = pForm.grdFilename.BackColor
    gvFilenameBorderStyle = pForm.grdFilename.BorderStyle
    gvFilenameCols = pForm.grdFilename.Cols
    gvFilenameDragmode = pForm.grdFilename.DragMode
    gvFilenameEnabled = pForm.grdFilename.Enabled
    gvFilenameFillstyle = pForm.grdFilename.FillStyle
    gvFilenameFixedCols = pForm.grdFilename.FixedCols
    gvFilenameFixedRows = pForm.grdFilename.FixedRows
    gvFilenameFontBold = pForm.grdFilename.FontBold
    gvFilenameFontItalic = pForm.grdFilename.FontItalic
    gvFilenameFontName = pForm.grdFilename.FontName
    gvFilenameFontSize = pForm.grdFilename.FontSize
    gvFilenameFontstrikethru = pForm.grdFilename.FontStrikethru
    gvFilenameFontUnderline = pForm.grdFilename.FontUnderline
    gvFilenameForecolor = pForm.grdFilename.ForeColor
    gvFilenameGridLines = pForm.grdFilename.GridLines
    gvFilenameHeight = pForm.grdFilename.Height
    gvFilenameHelpContextID = pForm.grdFilename.HelpContextID
    gvFilenameHighlight = pForm.grdFilename.HighLight
    gvFilenameLeft = pForm.grdFilename.Left
    gvFilenameRows = pForm.grdFilename.Rows
    gvFilenameScrollbars = pForm.grdFilename.ScrollBars
    gvFilenameTop = pForm.grdFilename.Top
    gvFilenameVisible = pForm.grdFilename.Visible
    gvFilenameWidth = pForm.grdFilename.Width
    gvFilenameColWidth(0) = pForm.grdFilename.ColWidth(0)
    gvFilenameColWidth(1) = pForm.grdFilename.ColWidth(1)
    gvFilenameRowHeight(0) = pForm.grdFilename.RowHeight(0)
    gvFilenameRowHeight(1) = pForm.grdFilename.RowHeight(1)
    pForm.grdFilename.Row = 1: pForm.grdFilename.Col = 1
    SavePicture pForm.grdFilename.Picture, tPath$ & "smarta02.tmp"

    gvFileNameText = pForm.grdFilename.Text
End Sub

Listing Six

Sub gpTopPanelItem (pForm As Form, pText As Variant, pImage As Control)

    pForm.grdItemname.Col = 1: pForm.grdItemname.Row = 1    
                                                'choose cell for image AND text
    'update image
    pForm.grdItemname.Picture = pImage.Picture

    If IsNull(pText) Then   'display default text
        pForm.grdItemname.BackColor = &HFFFFFF  'white
        pForm.grdItemname.ForeColor = &HFF& 'red
        pForm.grdItemname.FontName = "Arial"
        pForm.grdItemname.FontBold = False
        pForm.grdItemname.FontSize = 9.75
        pForm.grdItemname.Text = "<click> to select an ITEM"

    ElseIf pText = "" Then  'no text, image only
        pForm.grdItemname.BackColor = &HFFFFFF  'white
        pForm.grdItemname.ForeColor = &HFF& 'red
        pForm.grdItemname.FontName = goListFontName
        pForm.grdItemname.FontBold = goListFontBold
        pForm.grdItemname.FontSize = goListFontSize
        pForm.grdItemname.FontItalic = goListFontItalic
        pForm.grdItemname.Text = ""
    Else   'text and image, using character settings from user options 
           '    (go..=gENERAL oPTIONS)
        pForm.grdItemname.Col = 1: pForm.grdItemname.Row = 1
        pForm.grdItemname.Text = pText
        pForm.grdItemname.BackColor = &H80FFFF
        pForm.grdItemname.ForeColor = 0 'black
        pForm.grdItemname.FontName = goListFontName
        pForm.grdItemname.FontBold = goListFontBold
        pForm.grdItemname.FontSize = goListFontSize
        pForm.grdItemname.FontItalic = goListFontItalic
    End If

End Sub

Related Reading

More Insights

Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.