Visual dBASE is a wonderful programming language. You can do so much with it. Because you can manipulate data, present data, and retrieve information in so many different and powerful ways, I sometimes forget the actual graphical presentation of my apps and, many times, they are very simple. My view has been that its not what's on top but what's underneath that counts. I mean, I don't really mind if my forms are plain gray with gray buttons, but underneath the forms, the application finds its real power. Unfortunately not everybody sees it this way. Often some users will judge your application by its looks rather than by it's performance. So to this end I began to put some more into the presentation of my apps. But although everything looks great, I still have the gray background of the VdB desktop, or _app.framewin. Does this concern me? No. Does it concern my users? Well it shouldn't but it does. “Why is it so plain, why can't it be pretty like other applications....?” etc. So I decided to do some investigation into creating a desktop for my apps.
To do this I'd have to have a form with a different background. And also that form must be able to contain other forms within itself, (just like the VdB shell, or framewin does). The only problem is that all forms are children of the framewin object, making it impossible for one form to fall inside another. Somehow we have to change who is the parent of our main forms. Our Desktop form has to become the parent. The only way to do this is through the Windows API (Application Program Interface).
There is a function under Windows that all windows-type applications call to determine their parent windows. This function is the SetParent() function.
From the Win32 Programmers Reference:
The SetParent function changes the parent window of the specified child window.
An application can use the SetParent function to set the parent window of a pop-up, overlapped, or child window. The new parent window and the child window must belong to the same application.
If the window identified by the hWndChild parameter is visible, Windows performs the appropriate redrawing and repainting.
The function requires two arguments, one for the child window's handle, and the other for the parent window's handle.
The SetParent function changes the parent window of the specified child window.
HWND hWndChild, // handle of window whose parent is changing
HWND hWndNewParent // handle of new parent window
Read the help file to gain more information on this and other function.
The Desktop Form
This is a normal form with the maximize property set to true. I set this to true just as a startup setting, in fact it does not matter what it is: all the other forms will be contained within it. Create the form. Place a few buttons on it ( you don't have to, you can use a menu, or / and a tool bar or a combination of the three). Create a background, or use one that comes with VdB. I used the “shore.jpg” and changed it slightly just for presentation purpose. Use this file as the background of your Desktop, and make sure that the form's MDI property is set to false.
We also need to create our child applications or child forms. Theses can be any form that performs any function. I just created one child form and changed its properties as it is opened, just for the purpose of this article. Note that these forms' MDI property must be set to false. Also note that these forms must not have any menu items or toolbars attached to them. Some might think that I'm mixing MDI and SDI. Well I'm not because all the forms are SDI. What I am doing is creating an app that looks like an MDI app. Please note that the features of the MDI form will not apply to your forms, but the features of an SDI form will apply. I'm not trying to confuse the two or combine the two. What you will need to do to correctly understand this, is to read the section on forms in the VdB help file.
Once your forms are created we then need to make some changes. When the form is opened or executed or run, the form's bootstrap is executed; this defines the form and all its properties and then opens and displays the form.
f = new backgroundForm()
f.mdi = false // ensure not MDI
What we have to do is prevent this bootstrap from executing and, in addition, create our own code in its place. To do this, and to insure that it is not overwritten when the form is edited in the Form Designer, (this bootstrap and anything contained within the class...endclass of the form is streamed out by the designer), we insert the code before the line that reads: “** END HEADER -- do not remove this line ”.
The first line is a normal parameter line so that we can determine if the form needs to be opened as a modal or modeless form. (Read the help file for more information.) Next we declare or extern the SetParent function from the Windows API.
EXTERN CHANDLE SetParent(CHANDLE,CHANDLE) User32
We then declare a variable _app.FoParent that can reference the parent form from anywhere. We then declare and open the form as normal, using our variable declared to reference the form.
_app.FoParent= new backgroundForm()
_app.FoParent.mdi = false // ensure not MDI
IMPORTANT NOTE: Don't forget the return at the end.
So far so good, we will discuss more later but for now let's see our child form.
The Child Form
The child form, for now, is created
as normal. The only difference is in the onOpen event. When the form opens
we want to change its parent. More precisely, we want our Desktop
to be the parent. Because we have declared, or externed the SetParent function
in the Desktop form, we can now use this function to change the child's
First we must check if the form is indeed being called by a form that wants to be the parent, or whether the form is being called by another form or whether it is run by itself. The determining factor (not the only determining factor, but the one that I am using) is the _app.FoParent variable. Naturally if this variable is present in memory we can use it to reference the parent form's window handle. So we use the type() function to check this.
if type("_app.FoParent") # "U"
From The VdB Help file:
TYPE() returns a character string containing a one- or two- letter code indicating the data type. The following table lists the values TYPE() returns.
Use TYPE() to detect whether a function, class, or method is loaded into memory. If so, TYPE() will return "FP" (for function pointer), as shown in the following IF statements, which detect if the named function is not loaded (this is done to determine if the specified function needs to be loaded)
Expression type TYPE() Array object A DBF or Paradox binary field (BLOB) B Bookmark BM Character field or string value, Paradox alphanumeric field C Codeblock CB Date field or value, Paradox date field D Float field, Paradox numeric or currency field F Function pointer FP OLE (general) field G Logical field or value L DBF or Paradox memo field M DBF numeric field or value N Object reference (other than Array) O Undefined variable, field, invalid expression, or null U
Please read further to understand more on the type() function
In other words, if our variable is not undefined, that is it is loaded into memory, we can then proceed to change this form's parent. We can do this because we now have a reference to the parent form. We do this by issuing the setparent function with two variables, one for the child form's window handle, and the other for the prospective parent's window handle.
Now this now would change our child's parent to the Desktop form previously defined.
Executing the Desktop form, you will notice that it looks like it is an application by itself: If this app is minimized you will see that it is part of the Task Bar, signifying that it is a stand alone app, or so it appears.
If we now change the onClick event of one of the pushbuttons on our Desktop to open the child form, we can expect that child form to open within the Desktop and also to be moved only within the limits of the the Desktop.
The main form, which is the Desktop form, is now the main application. The other child forms are part of that main app, and are contained within that app. If I had to minimize that child form I would expect it to minimize to the bottom right hand corner of the Desktop, just like what would happen if you had to minimize the Navigator and the Command window of VdB.
If we now minimize the entire app, we only have one reference on the Taskbar, that of the main Desktop application: the child forms would not minimize to the Taskbar.
Note: if the child form's MDI property was set to true it would disappear when minimized. Why is this? Because it is an MDI app and therefore it has another parent. (Strictly speaking, this parent is the VdB framewin object. Please read the section in the help file about this.) Also make sure that the topmost property is set to true so that when the form is minimized it is visible on top of the Desktop.
And similarly if the form was to be maximized it would fall underneath its parent form:
Notice also that you can open multiple child windows, maximize them all, minimize them all. And, if you feel up to it, you can insert code to manage the child windows, that is code to, say, cascade or tile the child forms' windows. There are code samples and utilities in the dUFLP (dUFLP = dBASE Users' Function Library Project) found at Ken Mayer's web site and others around the Globe.
In order for the forms to work correctly they have to be able to talk to each other. In other words, you would want to set some properties of the child form from within the parent and you would want to set properties of the parent from within the child. For example you might want to disable the pushbutton in the parent that called the child form, and when that child form is closed you might want to re-enable that pushbutton. There might be a thousand other things that you might want to achieve that you can only do through forms having the ability to talk to each other. This article is not intended to be an in-depth tutorial on this subject. You would need to read the “HOW.TO”file made available at various Web sites. (One place to start is the Visual dBase Web Ring). Specifically the “HOW.TO” written for VdB5.6, called “FormVars.how”.
The first thing we need to do is create an object reference within the parent form to the child form. Any object contained in any form has a reference to it (e.g., a pushbutton has the reference “form.pushbutton”), and by using this you can reference any property of the pushbutton from within its container, namely the form. So the enabled property would be “form. pushbutton1.enabled = true”. Likewise a reference to the child (e.g., “ochild”), would then be created as “form.ochild”. If you are using the “this” reference, then obviously it would reference that immediate object. In this case it is the pushbutton.
this.ochild = new childForm()
Now we have to create a reference to the parent property of the child to reference something in the parent form. Seeing that “this” references the pushbutton we will keep with that. So the “child.parent” would be equal to something.
What we are saying here is that when we reference the “child.parent” property, because we have assigned it a reference to the pushbutton, we can use it to reference any other property of the parent pushbutton. E.g., in the child, “form.parent.pushbutton1.enabled” would reference the “pushbutton1.enabled” of the parent form.
Now we can open the form and display it. We can call the child form's onOpen event from within the parent form because we have a reference to it.
Once the form is open we can use it as normal. We can even minimize it, and open another form, if the app allows that. In other words we can now have multiple forms opened at once (If the app was designed to allow that to be done).
When we close the form, the form's onClose event fires. In the onClose code we can now disable the parent's pushbutton with the reference to the parent property that we created earlier. First though, we need to check to see if that reference really does exist. We do this by checking if the variable “_app.FoParent”, which we used earlier, is defined in memory. By having this reference back to the parent then we can reference any object contained, or attached to the parent form or parent object. E.g., if we reference the form, we can then reference any assigned menu object, or toolbar object, or activeX, etc.
if type("_app.FoParent") # "U"
this.parent.enabled = true
This may not be the only way to accomplish this, and it may not be the best way, but the point is that I use this in my apps and it works for me. That's the beauty of Visual dBASE: many people can achieve the same thing in many different ways. This is certainly not an exhaustive presentation of the topic, but you may take this idea and expand on it or totally change it. It does help to play and experiment, to see how things work; that's how we learn. Practice makes perfect, or at least improved, because a program is never perfect. It's all up to you. In the end, you have to do what works for you and what keeps the users happy.