Welcome to Foxite.COM Community Weblog Sign in | Join | Help

Bernard Bout

May the Fox be with you...

<October 2006>
SuMoTuWeThFrSa
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

Post Categories

Navigation

Some VFP links

Others...

Archives

Who links to this site?

Hey There!!!

Syndication




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.

  1. The form cannot be resized (as yet) but in my experience this is not something that is often done for such forms.
  2. 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.
  3. 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]

 

Published Saturday, October 28, 2006 2:37 PM by bbout
Attachment(s): VFPAero.zip

Comments

# Nice tricks... @ Sunday, October 29, 2006 2:22 AM

Hi Bernard,

after reading your article multiple times, I'm wondering a bit about your effort to hide the second icon in the taskbar. Wouldn't this work as well as your code?

ChildForm.ShowInTaskBar = .F.

Just set the property and it should work smoothly as excepted.


Sincerely, JoKi

Hey JoKi

You are absolutely correct. I never saw that property and just setting that property works fine without the API's. Thanks for that. Now if you can also solve the "AlwaysOnTop"  problem...

Thanks again

Bernard

jochenk

# re: Visual Foxpro shows its Glass! @ Thursday, November 16, 2006 1:46 PM

i cant try this bcos i was too bussy .

now when i want 2 run this an error message displays [ ochild : Property value is out of bound]

i m using vfp6

You can try 2 things.

1. Upgrade to VFP9

2. Open the form as a table and delete the _MemberData information.

Salwan

# re: Visual Foxpro shows its Glass! @ Tuesday, November 21, 2006 12:11 AM

Hello Bernard.
Your articles are truely interesting and very good. I wish to use the effect glass in XP with VFP.
I wish that you can understand what I write, because I write bad enough in English. I don’t achieve to obtain file TXT for the ChildForm. I apply the same procedure to mainform, but an error appears “ the text is too long for TEXT ENDTEXT”. Would you help me to obtain the TXT for CHILDFORM?. I will thank for you very much.

Please leave your email and I will send it to you. Otherwise visit the SECOND link in this post -  the blog at http://www.sweetpotatosoftware.com/SPSBlog/PermaLink,guid,9a91dea3-6413-42e9-aeff-f0097937474d.aspx where you will find the code for generating the Skin Def.

Horacio Giménez

# re: Visual Foxpro shows its Glass! @ Thursday, November 23, 2006 11:34 AM

Thanks Bernard, I adapted the code of Craig, and in the readskindefinition method I make directly read of the  txt file, instead of the SkinDef method. Fantastic!

Horacio Giménez

# re: Visual Foxpro shows its Glass! @ Wednesday, January 17, 2007 11:50 PM

Thanks for burning the candle on this. It will indeed help with the enhancements my projects need.

You're welcome.

Terry Thurber

# re: Visual Foxpro shows its Glass! @ Wednesday, February 28, 2007 1:49 PM

Very nice site! Good work.

Big Smile [:D]

...

# re: Visual Foxpro shows its Glass! @ Friday, March 09, 2007 3:21 PM

Nice design, good graphical content. I think I'll come back later again;)

...

# re: Visual Foxpro shows its Glass! @ Wednesday, March 28, 2007 4:29 PM

Visual FoxPro indeed ROCKS ...
The best Language i have ever worked with...

Thanks to you Guys.
in fact, i believe, the futur of this great tool and many programmers like me depend on you ...

Thanks
(Feldstein, MacNeill, Kramek, Bout, Hsia, Chalom, Nicholls, Bailey, Hennig, ...)

Daniel

New Comments to this post are disabled
Powered by Community Server, by Telligent Systems