Visual Foxpro shows its Glass!
Vista is almost upon us with its new API’s and Aero Glass theme. It has already been shown that VFP can run in Vista and Craig Boyd has shown here how to achieve some of the glass effects of Aero in a VFP form. If you haven’t, check out: here:
http://www.sweetpotatosoftware.com/SPSBlog/PermaLink,guid,ded0ffae-4c8f-41b9-918f-b5adfe36f140.aspx
Many of us will not be updating as yet till maybe SP1 or even later since a newer more powerful computer would be needed and who has that much cash lying around in addition to the cost of Vista itself. I have searched the internet and found some sites that claim to emulate glass in XP. I even downloaded a sample in .Net that actually does show a glass effect but the form was so slow that it was impossible to be used practically. But hey, Visual FoxPro is fast. We know that. So I decided to try my hand at it.
What if I could take a form, cut out portions of it, and make the visible parts transparent and place an opaque form with objects behind it. I could then manage its placement, movement etc. On a screen it would take on the appearance of a glass form. So let’s go…
(You can download the complete source as well as the article from the attachment link at the end of the post)
Creating Mainform.scx
I start with a normal form. The form has these properties set:
BackColour = RGB(64,64,64)
BorderStyle = 0 - No Border
ShowWindow = 2 - As top level form
TitleBar = 0 – Off
The form also has some custom properties and Methods which can be seen from the sample included. The form Name is MainForm
I then add some skin elements as shown below
(Which is stretched) and
for the top. In the very centre I place a shape and set its background colour to RGB(0,255,0) (bright green)
To get the rounded corners I place 4 images at the 4 corners which look like this, 5pixels by 5 pixels.(enlarged):
for the top left and
for the top right.
Similar ones are placed at the bottom but are flipped.
The final screen looks like this:

Craig Boyd has some very cool code for skinning a form. Have a read here:
http://www.sweetpotatosoftware.com/SPSBlog/PermaLink,guid,9a91dea3-6413-42e9-aeff-f0097937474d.aspx
I use this method for skinning my form.
My form has a custom method – CreateSkinDefinition and has this code from Craig:
LOCAL lnFormWidth, lnFormHeight, lnXCoord, lnYCoord, lnStartPixelBlock, ;
rgbGreenScreen, llContiguousPixels, lcSkinDefinition
lnFormWidth = THIS.WIDTH
lnFormHeight = THIS.HEIGHT
STORE 0 TO lnXCoord, lnYCoord, lnStartPixelBlock
*rgbGreenScreen = THIS.point(1, lnFormHeight - 1) && Whatever color is in the bottom left corner is considered transparent
rgbGreenScreen = RGB(0,255,0)
llContiguousPixels = .F.
lcSkinDefinition = ""
thisform.cFormdef = lcSkinDefinition
FOR lnYCoord = 1 TO lnFormHeight
WAIT WINDOW AT 10,10 "One Moment Please..." + PADL(TRANSFORM(INT(100 * (lnYCoord/lnFormHeight)))+ "%", 8) NOWAIT
FOR lnXCoord = 1 TO lnFormWidth
IF THIS.point(lnXCoord, lnYCoord) = rgbGreenScreen OR lnXCoord = lnFormWidth OR lnYCoord = lnFormHeight
IF llContiguousPixels
llContiguousPixels = .F.
lcSkinDefinition = lcSkinDefinition + TRANSFORM(lnStartPixelBlock) + ":" + TRANSFORM(lnYCoord) + ":" + TRANSFORM(lnXCoord) + ":"
ENDIF
ELSE
IF !llContiguousPixels
llContiguousPixels = .T.
lnStartPixelBlock = lnXCoord
ENDIF
ENDIF
ENDFOR
ENDFOR
WAIT CLEAR
* SAVE it
LOCAL lcFile
lcFile = PUTFILE("Save skin definition as:", SYS(2015) + ".txt")
IF !EMPTY(lcFile)
SET SAFETY OFF
STRTOFILE(lcSkinDefinition, lcFile, 0)
SET SAFETY ON
MESSAGEBOX("Skin definition was successfully saved to file.", 64, "Skin Definition Saved")
RETURN
ENDIF
This will create a skin definition for my form. Basically any area of the form that is RGB(0,255,0) will be included in the definition. This I save as mainform.txt
Once the skin definition has been created, I can delete all the green areas – The 4 corner bitmaps and the central green shape.
I then create a new method called SkinDef which has this code:
TEXT TO lcSkin NOSHOW
....
....
ENDTEXT
RETURN lcSkin
I copy and paste the entire contents of the mainform.txt between the TEXT … ENDTEXT commands.
In the Init of the form I have this code which does all the magic of skinning the form:
Local lnWindowRegion
* cuts out unwanted parts of the window - Thanks Craig Boyd
lnWindowRegion = This.readskindefinition()
SetWindowRgn(This.HWnd, lnWindowRegion, .T.)
* Make it transparent
Declare SetWindowLong In Win32Api As _Sol_SetWindowLong Integer, Integer, Integer
Declare SetLayeredWindowAttributes In Win32Api As _Sol_SetLayeredWindowAttributes Integer, String, Integer, Integer
_Sol_SetWindowLong(Thisform.HWnd, -20,524288) && 0x00080000)
_Sol_SetLayeredWindowAttributes(Thisform.HWnd, 0, 170, 2)
If I run the form the green areas have been cut out and the form is transparent with rounded corners:

As you can see from the sample form, there are invisible buttons placed over the “X” for closing and the “-“ for minimizing the form. I won’t show this code here as this post will get too long. The code can easily be seen by opening the object code window. There is also code in a transparent container placed above the title bar for moving the form.
Once I am satisfied with the final result I add the code below in the Init.
* this code should be run only after the childform has been created
* open the child form with objects
Do Form childgreenform Noshow With This
* same caption as parent
Thisform.oChild.lblFormCaption.Caption = This.lblMain.Caption
* reorder the windows
Thisform.reorderWindows()
The first section of code calls the readskindefinition() method which is a custom method that reads in the skin definition created earlier. The second section makes the window transparent. Then come the calls to open a child window, set its caption and a call to reorderWindows(). I’ll explain that later.
Code for reading in the skin def is:
*readskindefinition
* creates the form region
#DEFINE RGN_OR 2
LOCAL lnXCoord, lnYCoord, lnStartPixelBlock, hOverallRegion, hPartRegion, llFirstPart, lnCounter, lnTotalElements
hOverallRegion = 0
STORE 0 TO lnXCoord, lnYCoord, lnStartPixelBlock, hPartRegion
llFirstPart = .T.
lnTotalElements = ALINES(aryRegionCoord, ThisForm.SkinDef(), 1, ":") - 3
lcDecimalsWas = SET("Decimals")
FOR lnCounter = 1 TO lnTotalElements STEP 3
lnStartPixelBlock = VAL(aryRegionCoord(lnCounter))
lnYCoord = VAL(aryRegionCoord(lnCounter + 1))
lnXCoord = VAL(aryRegionCoord(lnCounter + 2))
hPartRegion = CreateRectRgn(lnStartPixelBlock, lnYCoord, lnXCoord, lnYCoord + 1)
IF llFirstPart
hOverallRegion = hPartRegion
llFirstPart = .F.
ELSE
CombineRgn(hOverallRegion, hOverallRegion, hPartRegion, RGN_OR)
DeleteObject(hPartRegion) && Free up the memory
ENDIF
ENDFOR
ENDIF
RETURN hOverallRegion
There is also code for handling the minimizing, closing etc of the form. Please check it all out. Too much to explain here.
That’s the Main outer shell completed. At this time I can save the complete form as a class and subclass it whenever I need. For this article I have just used the form “as is” so that the code is easy to see. Just open the form in the Class Browser and export the code to see the complete code in one place. This is what the form now looks like in the IDE:

The Child Form
Now what I need is another form with objects that I can place behind this form. The objects will be visible through the cut-out section and can be selected.
I won’t go into the details of the complete code for this form as the code can be seen by opening the methods or completely by opening the form in the Class Browser. Basically there is only one corner (top left) green bitmap and the form background is made green. In the exact same position as the Green shape was on the MainForm, I place another shape and colour this RGB(64,64,64). In this area I arrange all my controls. Note that if creating a class from this form the objects will be placed later.
The form now looks like this:

and after the skin definition is inserted similar to the above form and run, it looks like this. The green areas have been cutout:

You can see where the parts have been cut-out. There is no need to make this transparent though.
Once you are satisfied, and the skin definition pasted in, the green parts can be reset/removed, leaving a form like this. I name the form ChildForm.
Here it is in the IDE complete with objects:

Now I add the extra code and methods to it and save it as a class, ready for subclassing and adding objects. I will now explain the main technique and associated code.
The Technique
Remember that I have a semi transparent form with an area cut out, and a similar sized form with the border and parts below the title bar cut out. If I place the main form exactly over the child form, then the illusion will be complete. Parts of the child form will be visible through the mainform. Also each form carries an instance of the other so reference can be made to objects from either.
First in the parent form I add this code:
* open the child form
Do Form childgreenform Noshow With This
I open the child form hidden and pass an instance of the Mainform to it.
The child form has this code in its INIT:
LPARAMETERS oMainform
Then code to set the Region (cut out) then this:
Here the instance of the mainform passed is stored in a form var.
IF VARTYPE(oMainform) = "O"
WITH This
.omainform = oMainform
.Left = oMainform.Left
.Top = oMainform.Top
.Visible = .T.
ENDWITH
oMainForm.announce(ThisForm)
ENDIF
I then call the custom method Announce() in mainform passing an instance of the child form. The sole purpose of this Announce method is to accept the instance and store it in its custom property. Now each form stores an instance of the other and can call into each others methods and set each others properties.
Now since 2 forms are running at the same time, there will be 2 icons in the taskbar that will screw things up. Not only does this give the game away but each form can be activated independently causing unpleasant results. As we know, a Toolbar, though having the properties of a form will not show its icon in the task bar. That is what this next bit of code does. It converts the VFP childform into a toolbar thus removing its icon from the taskbar.
* hide this window from taskbar
DECLARE SetWindowLong In Win32Api Integer hWnd, Integer style, Integer flags
DECLARE GetWindowLong In Win32Api Integer hwnd, Integer style
DECLARE INTEGER ShowWindow IN user32 INTEGER hwnd,INTEGER nCmdShow
WS_EX_TOOLWINDOW = 128
SW_HIDE = 0
SW_SHOW = 5
GWL_EXSTYLE = -20
hWnd = ThisForm.hWnd
ShowWindow(hWnd, SW_HIDE)
bb = GetWindowLong(hWnd, GWL_EXSTYLE)
SetWindowLong(hWnd, GWL_EXSTYLE,WS_EX_TOOLWINDOW)
ShowWindow(hWnd, SW_SHOW)
Next the caption of the child form is made the same as the mainform. Since I have an instance I can refer to it easily.
* same caption as parent
Thisform.oChild.lblFormCaption.Caption = This.lblMain.Caption
Finally is a method to set this window behind the mainform. Have a look at the code to see how it is done.
* reorder the windows
Thisform.reorderWindows()
So to summarise, the first section of code calls the readskindefinition() method which is a custom method that reads in the skin definition created earlier and cuts out regions of the form. The second section makes the window transparent if needed. Then come the calls to open a child window, set its caption and converts itself to a Toolbar style window.
There is also code to position this form exactly where the other form is by setting its Top and Left properties.
The Unload() event has this code:
IF VARTYPE(This.oMainform) = "O"
This.oMainForm.Release
ENDIF
*This.oChild.omainform = .null.
This.oMainForm = .NULL.
It is important to release any instance of itself in the mainform passed earlier or the form will not close. Mainform also has similar code to release the instance of itself stored in the child form.
Only one more method to explain in the child form, and that is the MoveMe() method:
LPARAMETERS lnDifferenceX,lnDifferenceY
THISFORM.Move(THISFORM.LEFT + m.lnDifferenceX, THISFORM.TOP + m.lnDifferenceY, Thisform.Width, Thisform.Height)
RETURN
Mainform has code in it to allow it to be moved. In that code is a call to this method passing the pixels to move by. This has the effect of moving this form whenever the mainform is moved.
One more point. The Titlebar image of the child form has code in its MouseEnter event to Activate the aminform. This is to allow the switching of windows using the form controls.
Moving back to Mainform:
In the Resize event of mainform:
* Handle here the minimizing and opening
IF This.WindowState = 1
This.oChild.Visible = .F.
ELSE
IF This.WindowState = 0
This.oChild.Visible = .T.
ENDIF
ENDIF
This code will minimize/ restore the childform whenever this action occurs on the main form.
In the Unload() I release its instance stored in child form and close the child form with this code:
IF VARTYPE(This.oChild) = "O"
This.oChild.Release
ENDIF
*This.oChild.omainform = .null.
This.oChild = .NULL.
Mainform has one more method that is called to make it topmost, and place the childform next in the zorder.
*positions window as topmost
DECLARE INTEGER SetWindowPos IN user32;
INTEGER hwnd, INTEGER hWndInsertAfter,;
INTEGER x, INTEGER y, INTEGER cx, INTEGER cy,;
INTEGER wFlags
SWP_NOSIZE = 1
SWP_NOMOVE = 2
SWP_NOSENDCHANGING = 1024
SWP_NOOWNERZORDER = 512
SWP_NOACTIVATE = 16
SWP_NOREDRAW = 8
HWND_TOP = 0
HWND_BOTTOM = 1
HWND_NOTOPMOST = -2
HWND_TOPMOST = -1
* set child
swpStyle = SWP_NOSIZE+SWP_NOMOVE+SWP_NOSENDCHANGING+SWP_NOOWNERZORDER+SWP_NOACTIVATE+SWP_NOREDRAW
bb = SetWindowPos(This.oChild.hWnd,HWND_TOPMOST,0,0,0,0,swpStyle)
* set this window
swpStyle = SWP_NOSIZE + SWP_NOMOVE + SWP_NOSENDCHANGING
bb = SetWindowPos(This.hWnd,HWND_TOPMOST,0,0,0,0,swpStyle)
As you can see most of this code is generic and can easily be placed in the respective forms and the forms saved as classes. Once that is done it is a simple matter of subclassing the forms as needed and using them. We are almost done here. Once all the code is in place just run mainform. It will open childform and position it exactly below it giving the form a semi transparent Aero like look.
Here are some screenshots of what the final form looks like on my desktop. Notice that the borders and a part of the top are transparent while the rest is opaque.
We have Glass!!!

Here are some more screenshots:

Only one icon in the taskbar

A different skin here

and a different background for the mainform.

There are some caveats.
- The form cannot be resized (as yet) but in my experience this is not something that is often done for such forms.
- I have been unable to force the windows to the background when focus goes to another app. Thus the form stays “Always Topmost”. If anyone can fix this please send me the solution.
- Since there are 2 forms involved, the Activate / Deactivate of the forms are fired when switching between them. Not a big thing unless you depend on it.
You only have your imagination as to the colours that can be used for the glass part as well as the form skinning. You can vary the skin and as above by using a bitmap as the form background to achieve different effects. Using this technique, some very cool effects can be achieved and once the forms are converted into classes, they are very easy to implement and incorporate into your framework.
I hope this has been a pleasant experience for you as it has been for me in developing this technique. You can download the complete source as well as the article from the attachment link below.
Now go out there and show your Glass .![Big Smile [:D]](/emoticons/emotion-2.gif)