George
Tasker
gtasker@compuserve.com
August 29, 1999
One
of the things I like to do least, but is one of the most essential parts of
developing software, is documentation. I’d much rather write code than writing
about the application I’ve developed. To make matters worse, the demands for
increasingly more graphic on-line help means that not only to I have to write
about the application, but I also have to provide images of the application’s various
components. This can mean spending time capturing images of forms, pasting them
into Paint Brush or some other graphic application, then laboriously editing
them. All too often, I find myself having to repeat the process because I made
a mistake in the editing process.
From
the user’s perspective, however, graphical help greatly eases the learning
curve. Without it, the differences between a command button and an option
button are meaningless. Failing to provide solid graphically based help can mean
that you spend even more time supporting the application.
In
order to address these issues I developed Obj2Bmp. What it allows you to do is
select either a form or object reference, then create a bitmap in one of three
color options (16, 256, or 24 bit). Additionally, when selecting the object,
you may be able to enable or disable its appearance. This, however, on the code
in the form which controls its appearance.
The
first thing you’ll need to do is create a directory for the Obj2Bmp files and
copy them there. Once this is done, in VFP open the Obj2Bmp project, and
compile it. In order for the application to work properly, it needs to be
compiled under the version and service pack of VFP you are using.
Before
using the application, you may want to set the FOXTOOLS.FLL file as the
library. The application, in order to do what it does must have this library
loaded. If you do not, the application will prompt you for the location of the
library. If you fail to indicate where it is, the application will terminate.
Once
the application has been created, start it using the following:
oObjPicker = NULL
DO Obj2Bmp.app WITH oObjPicker
This
will return an object that will be global in scope. This is necessary since,
should the object go out of scope, the form will be released.
Figure
1 was created using Obj2Bmp.
Figure
1 shows the Obj2Bmp form. The images used to display the individual elements of
the form should be familiar. If an object exists on the form, but is not
visible, it does not appear in the treeview.
The
combobox beneath the treeview controls which form’s objects are displayed in
the list. When the application initially starts, the Select Object form will
usually be the form displayed. If, however, one or more visible toolbars (other
than VFP’s) are present, they will appear. The combobox displays the title bar
of the available forms or toolbars. The list displays the individual objects,
including the form or toolbar, by name.
To
create a bitmap, click on the desired object in the treeview. This can be the
form or any object shown. Container objects will display the plus sign (like
ColorOpts) when they can be expanded. You may select any individual object that
is displayed by the treeview.
If
you wish, you may click on the Disabled check box to toggle the object’s
enabled property. Items that are disabled on the form may be enabled by
un-checking it. The converse is also true.
To
capture the object, click the capture bitmap. The object will be captured in a
bitmap, using the object’s name as the primary part of the filename.
To
close the form, either click the close button in the upper right of the form,
or the Close command button.
The
Refresh command button is (unfortunately) a holdover from earlier versions and
may not appear in future releases. It reloads the combo box. This
functionality, however, will occur whenever the form is activated.
As
you might expect, using a modal form might cause some problems. The same holds
true for forms with AlwaysOnTop set to .T. or, in VFP 6.0, AlwaysOnBottom set
to .T.. There is a solution, however, that does not require any modification to
the form prior to running.
If
the form is saved as a class, simply create the form from the class in the
Command Window and do not issue a call to the form’s Show method.
If
the form is normally started with a DO FORM command, issue the following, or
similar command from the Command Winndow.
In
both cases, Obj2Bmp will display the form in the manner appropriate (modeless
and with neither AlwaysOnTop or AlwayOnBottom set to .T.)
Other
cases that may cause problems may include top-level-forms or forms that may be
moved to the Windows desktop. Unfortunately, because these properties are
read-only at runtime, nothing may be done to resolve this by the application.
Unfortunately,
it was necessary for me to hard code the necessary structures required to
produce 16 and 256 color bitmaps. These structures are based on the default
Windows’ color palette. If the colors do not appear properly for these color
modes, this is the reason. This doesn’t apply to 24-bit color.
VFP
5.0 cannot capture forms without a title bar. The reason for this is that a
caption is required in order to get the form’s window handle. In VFP 5.0, part
of the requirement for producing forms without a title bar was to remove the
caption. In VFP 6.0 this is not an issue, since setting the TitleBar property
to zero removes the title bar, but leaves the caption unaffected. If a form
fails to appear in the list it is because the caption is missing.
Overlapping
objects may not properly be captured. An illustration of this is the label that
overlaps the option group that selects the color mode. If you create a bitmap
of the option group, you’ll note that only the bottom portion of the label
appears. The application does not attempt to capture overlaps such as this.
You
may experience problems with the application correctly locating the top or left
edge of an object. One or more of the object’s containers being positioned in
such a way that the top or left location of the container is a negative number
would cause this. While the application attempts to properly resolve this, and,
to date, this has not appeared, it is a possibility.
If
multiple forms having the same caption are found, they are renamed. In order to
properly retrieve the window handle, a form’s caption must be unique in the
collection.
Objects
within containers can, for the most part, be selected. One notable exception to
this is a grid. You may only select the entire object in this case. Headers,
columns, and the contained controls are not available.
The
target location of all bitmaps is in the current default directory. Further,
the name of the file is determined by the name of the object. At this time, no
plans exist for allowing selection of either the location or the file name
used. If a file already exists in the current directory, you are prompted and
given the opportunity to give the file a different name.
If
a top level is selected and that form is maximized, you may get an image that
is off by one or two pixels. This problem is being investigated and, hopefully,
will be resolved in a future release. This does not affect objects on the form.
Forms
whose ShowWindow property is set at 2 (As Top Level Form) can cause problems if
a bitmap already exists with the same name as the one being created. The reason
for this is that the dialog that advises of the duplicate file brings the VFP
main window back on top. Since the bitmap is not as yet created, an image of
the VFP screen may appear. This may be resolved in future releases. At this
time, however, it is not. If you get this dialog when working with a Top Level
form, re-name or delete the existing bitmap and then try to create the bitmap
again.
The
BmpMaker class can be used to capture entire forms independently of the
application. The resulting bitmap can then be printed using the ShellExecute()
API call. In order to do this, you first must retrieve the window handle using
Foxtools. Assuming that oForm is the name of the form to be captured, and
Foxtools is already loaded, the syntax to get the window handle would be:
lnhwnd = WhTohWnd(_WFindTitl(oForm.Caption))
Next,
create an instance of the class:
SET CLASSLIB TO BmpMaker ADDITIVE
oMaker = CREATEOBJECT(“BmpMaker”)
RELEASE CLASSLIB BmpMaker
lcfile = oMaker.MakeBitmap(lnhWnd)
oMaker = NULL
This
will return the name of the 16-color bitmap in the current default directory.
The name of this file will be a generated by the class and will be unique.
Optionally, you may set the cFileName property of the oMaker object to whatever
name you desire prior to calling the MakeBitmap() function. If you wish to
create a bitmap with a higher color resolution, pass one of the following as a
second parameter to the function:
4 –
16 Colors
8 –
256 Colors
24
– 24 bit color
Note,
you should take whatever steps are necessary to assure that the form is active
and not obscured by any floating toolbar or form with an AlwaysOnTop property
set to .T.
The
used the following code to print the bitmap. In this example lcfile is the name
of the file to print.
DECLARE INTEGER
ShellExecute IN Shell32;
INTEGER hwnd, STRING @lpOperation,;
STRING @lpFile, STRING @lpParameters,;
STRING @lpDirectory, SHORT nShowCmd
lcop = ‘print’
lcparms = “”
* Note this
function is part of Foxtools in 5.0
lcdir =
JustPath(lcfile)
= ShellExecute(0,
@lcop, @lcfile, @lcparms, @lcdir, 0)
This will cause the bitmap to be printed by whatever application (usually Paint) that it’s associated with. The zero parameter as the show command will cause the application not to be visible. The potential problem with this method is that large bitmaps may be cut off.
The FileExists object uses a method I demonstrated in a FoxPro Advisor article. If you look at the form, you’ll note that no icon is shown. However, when the form is displayed, the standard Windows Question Mark icon appears. The Init method of the class contains the code to create the icon, along with the definition of the question mark (IDI_QUESTION). Besides the question mark, the following values may be used.
#define IDI_APPLICATION 32512
#define IDI_HAND 32513
#define IDI_EXCLAMATION 32515
#define IDI_ASTERISK 32516
#define IDI_WINLOGO 32517
One of the things that I initially didn’t like was the way the treeview would “jump” despite the fact that LockScreen was set to .T.. After some research, I discovered the reason why this occurs. Windows can only lock the state of one window at a time, and the treeview is a different window. The code in the Init and the click event of the Refresh command button shows how to lock the state of an OLE control. However, because each situation is different, what works here may not work in every case. It is highly recommended that Spy++ be used to determine what level in the child window hierarchy the OLE is, and that this information be used to create a solution specific to the individual problem.
Modifications
August 14, 1999
Code to handle modal forms, as well as the AlwaysOnTop and AlwaysOnBottom issues, was added to the new method ShowForm.
August 29, 1999
Corrected bug connected with the selection of the “Cancel” command button being chosen when a file already exists.
Modified the program to take into account Top Level forms with menus.
Acknowledgements
I’d like to acknowledge those kind folks who served as my beta testers on this project: Nancy Folsom, John Koziol, Ed Rauh, Barbara Paltiel, David Frankenbach, and Cetin Basoz. Without them, and their input, I might never have completed this project. I’d also like to thank Monte Murdock for uncovering a bug and reporting it to me.
Standard Disclaimer and Stuff
Obj2Bmp is freeware. No warranties, guaranties are either intended or implied. You may freely distribute any or all of it. You do so, however, at your own risk. Further, while I’ll supply support (at gtasker@compuserve.com), I cannot promise that all problems will be able to be resolved. Furthermore, so and so forth, blah, blah and blah, and the rest of all that legalize that usually appears in print so small you need a microscope.