HOME | SIGN UP | ACCOUNT | SUBSCRIBE | TROUBLESHOOTING | LOGIN
News · Meetings · Jobs · Downloads · Universal Thread articles · Web articles · Conference coverages · Consultants · Blogs · FAQ · User groups · Photos · Videos

VFP for Beginners: Part V

Claudio Lassala, Dio Clouds Software
Claudio Lassala is an independent Software Developer who currently works mostly building Ruby on Rails applications. Previously, he has worked for several years developing .NET applications, presented several lectures at Microsoft events such as PDC Brazil, TechEd Europe, and various other Microsoft seminars, as well as several conferences and user groups across North America, Europe and Brazil. He is a multiple winner of the Microsoft MVP Award since 2001 (for Visual FoxPro in 2001-2002, and for C# ever since). He has articles published on several magazines, such as MSDN Brazil Magazine and CoDe Magazine. He started the Virtual Brown Bag meetings (www.virtualbrownbag.com) in 2009 and have been hosting it weekly since then. When not writing code, Claudio is probably rocking out with his band, Descent Into Madness (http://www.descentintomadness.com). In a previous life, Claudio authored and presented several training videos that can be found on the Universal Thread.

Following our course (which began on the 8th Issue of RapoZine), we will learn to create Forms. Visual FoxPro give us tools that make Form creation very simple. Let's start.

Creating an "About..." Form.

A very common Form, and also one of the simplest to create, is an "About..." Form, where we place authorship information about our applications. So we will start such a Form.

To start building a Form we could select File->New from the VFP Main menu; then, on the dialog box that comes up we select the "form" File Type, and click over New File (if we click over New Wizard, a wizard would open to help us in basic tasks, but as all good developers does, we would create our Form manually).

Alternatively, we could simply type right into the Command Window the command: Create Form

Figure 1: Creating a new Form.

Once our new Form got opened in editing mode, we could insert controls (many times referred as "objects"), as Command Buttons, Edit Boxes, etc. To do that, we use the "Form controls" toolbar. We also have the "Form Designer" toolbar that bring some other options to help us create Forms.

Figure 2: The "Form Controls" and "Form Designer" toolbars.

If you're not seeing your toolbars, you can make them visible trought the menu View-> Toolbars... selecting them and clicking on the Ok button. Our goal is to create a Form simmilar to the one show in Figure 3:

Figure 3: Our first goal - An "About..." Form.

Alright, let´s start placing an Image control, that can contain pictures of the most common formats as JPEG, GIF, Bitmap, etc. Essentially, it's enough to click the Image button on the "Form Controls" toolbar, and the click over our Form. The control would be inserted and then we have to go to the Properties Window to tell which picture have to be used with this control. If you don't see the Properties Window, just right-click the control and select the "Properties" option. For your understandig, Properties are object characteristics. Properties allow us to define the appeareance (color, size, width, etc) and state (visibility, enabling, etc) of objects. For the Image control, we have a Picture property where we must type the path and filename of the picture that we wish to show. Or we can click on the "..." button to navigate our directories and choose the desired picture.

Figure 4: Inserting an Image Control.

Now we'll go to insert some text Labels on our Form. Let's click over the Label button on the tollbar, and then click our Form. Over the Properties Window, we'll search for the Caption property, and enter the text "SIFIN - Financial Controlling System". The Caption property of the Label control allows us to indicate the text that should appear on the Form. You'll notice that the text appears incomplete on the Form. It's just a matter of increasing its size manually, or set its AutoSize property to .T. This property determines that the Label size would be dinamically increased or decreased to fit the size of its content (defined by the Caption property).

Figure 5: Inserting a Label Control.

To give your Label with the appeareance of the Form in Figure 5, set the following properties with the values shown bellow:

Property Value
AutoSize.T.
Caption SIFIN - Financial Controlling System
FontBold.T.
FontNameTahoma
FontSize 11

You should have noticed that the Label is now too big, and it doesn't fit anymore inside the Form. We'll then set some of the Form properties to give it a better appeal.

Property Value Goal
AutoCenter.T. Automatically centers the Form on the screen when executed.
BorderStyle2 - Fixed Dialog Changes Form's border so the user can't resize it.
Caption About SIFIN Defines Form's title that appear on the tilte bar.
Height257 Sets Form's height to 257 pixels.
Iconc:\desenvolvimento\projetos\financeiro
\libs\graph\icon\rapozine.ico
Point to Form's icon.
MaxButton.F. - False Disables the Form's Maximize button.
MinButton.F. - False Disables the Form's Minimize button.
NamefrmSobre Defines the Form's name, by which you can refer programmatically.
Width414 Sets Form's width to 414 pixels.
WindowType1 - Modal Defines the Form's as Modal, so when it becomes active, no other application's Form or Menu can be accesed until this Form is closed.

The remaining Lables of our Form are quite simmilar, changing essentially their placement, Caption and color (the ForeColor property). Maybe the most importante trick here: after writing a long text in a Label's Caption, for it to break in multiple lines according to its width, just set its WordWrap property to .T. I'm sure that you'll get the remaining Labels without problems. Anyway, the full source code for this month's project can be downloaded from the foot of this article.

Now, let's beautify a little our Form's appeareance adding some Shape controls. Just click the Shape button on the toolbar and then on the Form. Create two Shapes and place them around our logo and also around the area with the application's name. To get the 3D effect on the Shape, just set the SpecialEffect property to: 0 - 3D.

Figure 6: Inserting a Shape Control.

You should notice that the Shapes are drawn over our text. That happens because of the order in which the controls were inserted on the Form. However, we certainly want that the Shapes get behind so we can see the labels. We need to use the Layout Toolbar, which can be invoked from the Layout button on the Form Designer toolbar, or from the View ->Layout Toolbar menu.

With the Layout toolbar we can work on the alignment and positioning of the controls on our Forms. For example, let's align the Shapes horizontally (Align Horizintal Centers), and send them behind the Labels (Send to Back). First select the two controls (select one control, keep the Shift key pressed, and select the other one). The Layout toolbar buttons would be enabled. Just press the referred buttons.

Figure 7: Adjusting controls with the Layout Toolbar.

To finish adding controls on our Form, let's place a Command Button. Just click the Command Button on the toolbar, and then over the form. After resizing and placing the button over the Form, enter in its Caption's text: "Ok".

Figure 8: Inserting a Command Button Control.

Until now, all we made was visually created. No single line of code was typed. However, all that we visually did can be done with programming, but we will see this later in our course.

Well, our OK Command Button is useless by now, as if we execute the Form and click on the button, nothing happens. We will associate one line of code to it, so when we click on it, the Form gets closed.

Double-click the button, and a window for code editing will open. In this window you'll see that the selected Object is Command1 (later we'll see how to name more properly our controls), and the Method must be the Click method. Methods are actions an object can execute. The Click method is automatically associated with the Click Event. That means that every time the user clicks on the button, the Click Event is triggered. This event would search for the Method of the same name associated to it; in this case, the Click Method. Finally, the Click Method will be executed and consequently, the code associated to it. Don't be scared, because we will talk a lot about methods and events during the course.

The line of code needed to close the Form is simple: Thisform.Release. This line executes the Release Method of the Form in which we are.

Figure 9: Code to be executed when the button is clicked.

Perfect! We can execute our Form now and see the results. To execute it, just click the Exclamation (!) sign on Visual FoxPro Standard Toolbar (if you don't see it, just call it from the View->Toolbars... menu, selecting Standard).

Figure 10: Executing our Form.

The first time the Form is executed, VFP will ask you to save it, selecting a directory and giving it a file name (the SCX extension would be automatically assumed). Next time VFP wouldn't ask to save it, because it would be automatically saved and executed. Then look at our Form in execution. You can try to select any other VFP option, and you'll see that's not possible because we defined this as a Modal Form. Finally, click on the OK button, and you'll see the Form gets closed.

Figure 11: Our "About..." Form executing.

A Data-Entry Form

If you're coming from Clipper, you must be waiting to create a Data-Entry Form in VFP (to insert, edit or delete records, etc). And without any doubt our applications will ever have tons of Forms of this type. So let's create a Form like this.

Begin creating a new Form as we did in the previous example. With the new Form opne for editing, we will work within its Data Environment. In the Form's Data Environment, we determine the tables with which the Form will work, which indexes will be active, which will be the relationship, etc. All you used to do in Clipper with lots of Use, Set Order and Set Relation, we will do in the Data Environment visually, without even a single line of code (obviously, if you want to do it programatically, there's no problem).

To open the Form's Data Environment, click the Data Environment button on the Form Designer toolbar, or right-click the Form and select Data Environment from the opening menu.

As our Data Environment is empty at this time, it will automatically open a window for us to start selecting the ones that would be attached to our Form. Even if at least a table exist, to add more tables, just right-click on the Data Environment and select Add... on the opening menu. You would notice that if you have a Database open, you don't need to navigate through your directories to search for the tables, because all the tables in your Database would be automatically listed.

For this example, select the Customers table.

Figure 12: Inserting a table in the Data Environment.

There are several properties for a table added to the Data Environment that we can set. For example, the table Alias, a Filter, an active Index, etc. Once again, we could do all this trough code, but it's far easier to set some properties visually, isn't it? Then let's define as our tables's active index "Name".

Figure 13: Setting Table properties in the Data Environment.

Every time we execute a Form, its Data Environment is automatically opened, and all its tables are opened as well, toghether with its indexes, relations, and everything that has been defined. It is possible to "detach" this behavior and leave to our criteria the moment in which tables have to be opened.

Now it's the time to create the Labels and Text boxes corresponding to the fields of the table we will edit. In the Clipper era, how many time we spent with @SAY and @GET to build a screen able to capture values and store them in fields? It used to take some time, remember? In VFP, all is far easier. To insert all the table's fields in the Form, just click in Fields over the table inside the Data Environment, and drag it inside the Form. If you want only some of the fields, just select them individually, keeping pressed the Ctrl key, and then drag them with the mouse's right button, selecting in the menu the "Create Multiple Controls Here" option.

Figure 14: Inserting controls for editing table's fields.

Very easy, isn't it? All those lines of code replaced by a simple drag-and-drop operation.

As you can see, several controls were created automatically for us (obviously, all this could be done manually). Remember that we set Caption properties when we created our tables? Now the Labels created got this Captions. And for showing and editing the values of the table's fields, controls like TextBox or ChekBox were created. You can see that for text or date fields, Textbox controls were created, while for a logic field a Checkbox were created. Beside all that, the controls were created in an intelligent way, using the "txt" prefix for textboxes and "chk" for the checkbox, followed by the field name. No words about that VFP makes an auto-alignment for us.

What else? Do you remember that in Clipper we needed to create variables to store the values from the table, work with that variables, and at the end of the process, unload this variables contents again to the table? In VFP you don't have to worry for this anymore. Look that the ControlSource property of every control in the Form has been automatically completed with the table and field name.

Figure 15: Controls automatically linked to table's fields.

The ControlSource property keeps a link between the Control and its data source, in this case, some field in a table. This way, when we alter the value of controls, the table's field value is automatically altered without the need of executing any replace command.

You can alse see that the comments we entered when we created the table in the Database were also transferred to the controls. This way, is easy for the developer having a quick documentation on every field without the need of going to see the table structure.

This VFP feature, dragging fields from a table onto a Form, is called IntelliDrop, and can be customized by the developer, as we'll see later in our course.

Now is the time to insert some command buttons. Let's create, for example, those traditional buttons of a data-entry screen, such as buttons to go to the first record, the previous record, the next record, and the last record, plus a button to close the Form.

Add the five buttons to the Form as you already learned. No, instead of use text for the buttons, leave the Caption property empty and let's use pictures. You need to set the Picture property for each button, pointing to the proper image, following the table bellow (take the chance to give more intuitive names to the buttons):

Control Picture filename
cmdPrimeiro..\..\libs\graph\bmp\primeiro.bmp
cmdAnterior..\..\libs\graph\bmp\anterior.bmp
cmdProximo..\..\libs\graph\bmp\proximo.bmp
cmdUltimo..\..\libs\graph\bmp\ultimo.bmp
cmdFechar ..\..\libs\graph\bmp\fechar.bmp

Watch that using a relative path to the files (..\..\libs\...) gives you more flexibility in the sense of no getting nailed to "C:\" or"D:\", for example.

If you're using VFP 7, set the buttons' SpecialEffect property to 2 - Hot Tracking (that would give a flat/hot effect to them).

Very well, now is the time to program a little. We need to implement code in the buttons to give them functionality. So let's double-click the cmdPrimeiro button, and select the Click Method. Now we just enter there the following lines of code:

Go TOP

Thisform.cmdPrimeiro.Enabled = .F.
Thisform.cmdAnterior.Enabled = .F.
Thisform.cmdProximo.Enabled = not Eof()
Thisform.cmdUltimo.Enabled = not Eof()

Thisform.Refresh

The code is fairly simple. With Go TOP, we move the pointer to the top of the table. With Thisform.cmdPrimeiro.Enabled = .F., we tell VFP to disable the cmdPrimeiro control of the Form in which we are. The remaining lines are simmilar, just pointing to other controls. Finally, we order the Form to update itself with the Thisform.Refresh instruction. Remember: the table pointer was moved to other record, so we want the shown values in the controls reflecting the currently selected value.

Now, fill the Click method of the cmdAnterior with the following code:

Skip -2

Thisform.cmdPrimeiro.Enabled = not Bof()
Thisform.cmdAnterior.Enabled = not Bof()
Thisform.cmdProximo.Enabled = not Eof()
Thisform.cmdUltimo.Enabled = not Eof()

If Not Bof()
   Skip
EndIf

Thisform.Refresh

If you come from Clipper, you certainly already recognized the Skip command and the Bof() and Eof() functions. the Skip command allow us to walk between records (wiathout any argument, it advances 1 record, with a positive argument, it advance that number of records, and with a negative one -as in the code above-, it moves back the number of records indicated). The Bof() and Eof() functions test if we are at the beggining or the end of the table, respectively.

Here is a trick: I'm initially walking back two records because I want that, if we reached the first record, the "First" and "Previous" buttons gets disabled, as it doesn't make sense leaving them enabled when we are at the first record (if we are already in the first record, why should we wish to go there again? And it is also impossible to go to the "previous" record).

Let's go to the Click of cmdProximo button:

Skip +2

Thisform.cmdPrimeiro.Enabled = not Bof()
Thisform.cmdAnterior.Enabled = not Bof()
Thisform.cmdProximo.Enabled = not Eof()
Thisform.cmdUltimo.Enabled = not Eof()

If not Eof()
   Skip -1
Else 	
   Go BOTTOM 
EndIf

No big difference... Just the fact that we're going forward. And at last, the cmdUltimo button.

Go BOTTOM 

Thisform.cmdPrimeiro.Enabled = not Bof()
Thisform.cmdAnterior.Enabled = not Bof()
Thisform.cmdProximo.Enabled = .F.
Thisform.cmdUltimo.Enabled = .F.

thisform.Refresh

Once again, very much like the rest, with the exception that we use now Go Bottom to go to the end of the file.

The button to close the Form have to be named cmdFechar, and I think that you already learned the code to close the Form, didn't you?

Execute the Form and experiment clicking the buttons. You'll see that you can navigate between records. How many lines of code do you need to do the same in Clipper?

Very good. I don't particularly like the way in which records are exposed, because the user can freely alter them. I prefer to disable all the fields, so the user can watch at their content, but not alter it. That leave us margin to create a security system.

Let's disable all the controls at the Form's initialization. For that purpose, double-click the Form and select the Init method. This method is associated to the Form's Init event, and is triggered when the Form initializes. Later, in the appropiate moment, we'll see the event execution sequence.

Complete the Form's Init method with the following code:

This.nStatus = 0

Thisform.cmdPrimeiro.Enabled = .F.
Thisform.cmdAnterior.Enabled = .F.
Thisform.cmdProximo.Enabled = not Eof()
Thisform.cmdUltimo.Enabled = not Eof()

WITH Thisform
    .txtI_id_clientes.Enabled = .F.
    .txtC_razao_social.Enabled = .F.
    .txtC_nome_fantasia.Enabled = .F.
    .txtC_fone_1.Enabled = .F.
    .txtC_fone_2.Enabled = .F.
    .txtC_endereco.Enabled = .F.
    .txtC_bairro.Enabled = .F.
    .txtC_cidade.Enabled = .F.
    .txtC_uf.Enabled = .F.
    .txtD_cadastro.Enabled = .F.
    .chkL_ativo.Enabled = .F.
ENDWITH	

In this code, you see different things. At the first line we have a reference to a "this" object. When we use the reference "This" we indicate that we are accessing something on the object in question. So, in this example, we are accessing the nStatus property of the Form. The nStatus property is also a novelty, because it is not part of the native properties of the Form base-class (we'll talk more about classes as the course follows). This is a custom property that I created to keep a flag that will indicate the current status of the Form. Look at the possible values for the nStatus property on Table 1.

Table 1: Possible values to nStatus property.

Value Status
0 Display
1 Append
2 Edit

In the following four lines of code we are just adjusting the Enabled property of the navigation buttons. And next, another novelty: the With...EndWith structure. See, we will need to make reference to Thisform for every control, as this is were the controls are contained, right? Using the With Thisform command we can access any of its controls, properties or methods just beginning with a dot (.), because VFP knows that it have to complete the command line with Thisform.

What do we gain with that? Well, besides a cleaner code to read, we have a smaller file, because we decrease considerably the typed text (when we have bigger hierarchical structures, you'll realize this even more accurately), and finally, a performance gain, because instead of searching all the hierarchy until reach an object that we want, VFP already knows where the object is, and then it starts its search from this point.

Don't get scared, as you'll get used to all this. You can execute the code again now, and you'll see that all controls are protected, as they are disabled.

Now let's create the nStatus property. In the Form menu, select New Property... Type the nStatus name, and in the Description field, assign it a description like "Keeps the form's state". This description is shown in the Properties Window.

Figure 16: Creating a New Property.

Now's the time to insert another three essential buttons to allow us Add, Edit, Save and Delete records, and cancel an operation as adding or editing. As the buttons Save and Cancel only make sense once the Add or Edit buttons have been pressed, we'll use each button to do two tasks.

So add another three buttons and name them cmdNovoSalvar, cmdEditarCancelar and cmdExcluir. For the Picture property of each one select the images "Novo.bmp", "Editar.bmp" and"Excluir.bmp" respectivelly.

In the Click method of the cmdNovoSalvar button, enter the following code:

If Thisform.nStatus = 0  && Display
	Thisform.nStatus = 1 && Add
	This.Picture='..\..\LIBS\GRAPH\BMP\SALVAR.BMP'
	Thisform.cmdEditarCancelar.Picture='..\..\LIBS\GRAPH\BMP\DESFAZER.BMP'

	Thisform.nReg = Recno()
	Append Blank
	
	Thisform.Refresh

	With Thisform
	    .txtI_id_clientes.Enabled = .T.
	    .txtC_razao_social.Enabled = .T.
	    .txtC_nome_fantasia.Enabled = .T.
	    .txtC_fone_1.Enabled = .T.
	    .txtC_fone_2.Enabled = .T.
	    .txtC_endereco.Enabled = .T.
	    .txtC_bairro.Enabled = .T.
	    .txtC_cidade.Enabled = .T.
	    .txtC_uf.Enabled = .T.
	    .txtD_cadastro.Enabled = .T.
	    .chkL_ativo.Enabled = .T.
		
	    .cmdFechar.Enabled = .F.
	    .cmdAnterior.Enabled = .F.
	    .cmdProximo.Enabled = .F.
	    .cmdPrimeiro.Enabled = .F.
	    .cmdUltimo.Enabled = .F.
	    .cmdExcluir.Enabled = .F.
	    
	    .Closable = .F.
	Endwith

Else
	Thisform.nStatus = 0  && Display
	This.Picture='..\..\LIBS\GRAPH\BMP\NOVO.BMP'
	Thisform.cmdEditarCancelar.Picture='..\..\LIBS\GRAPH\BMP\EDITAR.BMP'
	
	With Thisform
	
	    .txtI_id_clientes.Enabled = .F.
	    .txtC_razao_social.Enabled = .F.
	    .txtC_nome_fantasia.Enabled = .F.
	    .txtC_fone_1.Enabled = .F.
	    .txtC_fone_2.Enabled = .F.
	    .txtC_endereco.Enabled = .F.
	    .txtC_bairro.Enabled = .F.
	    .txtC_cidade.Enabled = .F.
	    .txtC_uf.Enabled = .F.
	    .txtD_cadastro.Enabled = .F.
	    .chkL_ativo.Enabled = .F.

	    .cmdFechar.Enabled = .T.
	    .cmdAnterior.Enabled = .T.
	    .cmdProximo.Enabled = .T.
	    .cmdPrimeiro.Enabled = .T.
	    .cmdUltimo.Enabled = .T.
	    .cmdExcluir.Enabled = .T.
	    
	    .Closable = .T.
	    
	    =TableUpdate()
	    
	    Thisform.Refresh
	    MessageBox("Record Saved!")
		
	Endwith	
Endif

This piece of code is the longer one by now, but anyway, it is still quite easy- The idea is simple: Once the cmdNovoSalvar is pressed, we check for the nStatus property to know the state the Form is in. If it is in Display state, then it means that we are trying to add a record. The first thing that we do in this case is to update the nStatus property, setting its value to 1 (Add). Immediately, we change the button's image to show a "diskette" representing the button is now for Save, and we also change the image of the cmdEditarCancelar button to represent a "Cancel" type button.

After that, you can see we are accessing a Form property called nReg that doesn't exist, and we have to create. This property should store de current table position (the Recno() function returns the physical number of the record on the table). Now that you already know how to do it, create this Form variable.

The reason for the existence of the nReg property is that, if a user gives up about adding a record, we'll need to know which record she was looking at before pressing the "Add" button, and then go back to that record.

Then comes the Append Blank command (also known for the Clipper people), that adds a blank record to the table.

And then we simply disable the remaining buttons, because doesn't make sense, for example, to click the "Next" button when we are in the process of editing a record.

Further on, if nStatus is not 0, either being 1 or 2, we are in the middle od and add or an edit. In such case this button have the responsability of saving a record (being an add or edit). Beside the change of images of the buttons, and the enabling of the disabled buttons, we use the function TableUpdate(). Let's talk a bit about that.

In the Clipper ages, a good deal of the table corruption problems came from the fact that we were all the time reading and wroting right to the file. If the system was abruptly shut down, because of a bug or a simple power outgage, while writing a record, it was very likely that our file got corrupted.

In VFP we have the concept of Buffering. With buffering, we no more read and write all the time from the table, and instead, we start working with records right into the memory of our computer. That way, the reading happens just once for bringing the record to memory and the subsequent readings are made right over the computer memory. And the writing happens when the TableUpdate() function is executed.

The TableUpdate() function's purpose is to tell VFP: "Paste the records that are in memory (buffered) and wrote them to the source table".

For buffering to be working, we need to activate it. There are several ways to do this, but we would use the simplest one now. Go to the Form's BufferMode property and set it to 2 - optimistic. That will instruct VFP to just block a record when the TableUpdate() command is executing. The buffering concept is wider and more complex than that, but to avoid scaring you, we'll stop here, and we'll go deeper later in our course.

Now let's go to the Click method of the cmdEditarCancelar button:

If Thisform.nStatus = 0  && Display
	Thisform.nStatus = 2 && Edit
	This.Picture='..\..\LIBS\GRAPH\BMP\DESFAZER.BMP'
	Thisform.cmdNovoSalvar.Picture='..\..\LIBS\GRAPH\BMP\SALVAR.BMP'

	Thisform.nReg = Recno()

	With Thisform
	    .txtI_id_clientes.Enabled = .T.
	    .txtC_razao_social.Enabled = .T.
	    .txtC_nome_fantasia.Enabled = .T.
	    .txtC_fone_1.Enabled = .T.
	    .txtC_fone_2.Enabled = .T.
	    .txtC_endereco.Enabled = .T.
	    .txtC_bairro.Enabled = .T.
	    .txtC_cidade.Enabled = .T.
	    .txtC_uf.Enabled = .T.
	    .txtD_cadastro.Enabled = .T.
	    .chkL_ativo.Enabled = .T.
		
	    .cmdFechar.Enabled = .F.
	    .cmdAnterior.Enabled = .F.
	    .cmdProximo.Enabled = .F.
	    .cmdPrimeiro.Enabled = .F.
	    .cmdUltimo.Enabled = .F.
	    .cmdExcluir.Enabled = .F.
	    
	    .Closable = .F.
	Endwith

Else
	Thisform.nStatus = 0  && Display
	This.Picture='..\..\LIBS\GRAPH\BMP\EDITAR.BMP'
	Thisform.cmdNovoSalvar.Picture='..\..\LIBS\GRAPH\BMP\NOVO.BMP'
	
	With Thisform
	
	    .txtI_id_clientes.Enabled = .F.
	    .txtC_razao_social.Enabled = .F.

            .txtC_nome_fantasia.Enabled = .F.
            .txtC_fone_1.Enabled = .F.
            .txtC_fone_2.Enabled = .F.
            .txtC_endereco.Enabled = .F.
            .txtC_bairro.Enabled = .F.
            .txtC_cidade.Enabled = .F.
            .txtC_uf.Enabled = .F.
            .txtD_cadastro.Enabled = .F.
            .chkL_ativo.Enabled = .F.

            .cmdFechar.Enabled = .T.
            .cmdAnterior.Enabled = .T.
            .cmdProximo.Enabled = .T.
            .cmdPrimeiro.Enabled = .T.
            .cmdUltimo.Enabled = .T.
            .cmdExcluir.Enabled = .T.
	    
            .Closable = .T.
	    
            =TableRevert()
	    
            Go Thisform.nReg
	    
            Thisform.Refresh
            MessageBox("Editing cancelled!")
		
      Endwith	
Endif

Very simmilar to the code of the Save button, isn't it? The only big news are two things. First, the TableRevert() function. It does the opposite that TableUpdate(): simply discards the changes made over the record (or records, depending on how the buffering is set - we'll talk more abpout that in the appropiate moment). That way, if the user cancels and addition, the record added is cancelled, or if she cancels an edition, the changes made to the record are discarded.

The Go Thisform.nReg comand just instructs VFP to move the record pointer to where it was before the Add or Edit operation started.

And finally, go to the Click method of the Delete button:

If MessageBox("Do you want to delete this record?",4+32+256,"Atention") = 6  && Yes

    Delete 
	
    TableUpdate()

    MessageBox("Record deleted!")
	
    Skip
    If Eof()
	Go bott
    EndIf
	
    Thisform.Refresh
	
EndIf 

Let's understand the code: first we use the MessageBox() function to ask the user if she really want to delete the record (see more details about this function in the VFP Help). If the user clicks the Yes button, this function returns the value 6 (if the No button is clicked, we simply ignore the rest).

If the action is confirmed, we perform the Delete command to mark the current record for deletion, and then the TableUpdate() function to send the update to the table. After telling the user the record has been deleted, we just advance to the next record in the table (if there is not a "next" record, we just go to the last record).

At this moment you can execute the Form, and test all the buttons. Add records, edit and delete some, make all the common operations in this type of screens.

Figure 17: Our Data-EntryForm.

Public Session and Private Session

Try to execute two instances of the Form: just go to the Command Window and execute the command Do Form Clientes. twice.

Now, in one of the instances of the Form, advance to any record (10 records forward, for example). Then go to the other form being executed, pay atention to the Customer code in which you are, click the Edit button, and finally, click on the Customer code. You'll see that the control is updated and it shows the customer code that's in the other Form.

But why this happens?!?

That happens because we are using a public (default) data session for the Form. That way, both are using the same "copy" of the table in which it is based. Then, when we move around the records in one form, the pointer is changed also in the other one. In some circumstances this is exactly what we want, but not in this case for our Form. What we need here is that each one of the Forms have a Private Data Session. This means that one instance of the Form has nothing to do with the other, and they are totally independent.

To configure the Form to use a Private Data Session, close the two Forms, and set the DataSession ro the value 2 - Private Data Session. Create two instances of the same Form again (with the Do Form command), and make the test. you'll see that each Form is really independent from the other.

Why create Properties instead of variables?

Maybe this question is in your head. The reason is: Imagine that instead of a property nStatus for the Form we had created a public variable called gnStatus. Then, as in the last example, we execute two instances of the Form. If in one of the Forms we make click in the Edit button, and in the other one we click Add, how can we set the value of the gnStatus variable???

Each Form can have its own state, and for that reason, a variable would just cripple it all. That's why we create Form properties, allowing each Form to keep its own state, absolutely independent from the other Form (or from all we would have in execution).

Another reason: the life scope of properties is exactly the life scope of the object. Thus, once an object is destroyed, its properties are automatically destroyed too. With that, we don't need to remember to release memory variables all the time.

During all the course we will be creating Properties and Methods, and the concept will get stronger on you.

Conclusion

In this chapter, you learned the basic principles for Form creation, indeed creating the most common Form in our applicactions, a Data-Entry Form. If you came from Clipper, you can be a little lost, but without any doubt, you already saw that with many fewer lines of code we get the results that in Clipper took us hundreds of lines. In the next chapters of our course, you'll learn to decrease even more the lines of code needed to perform some tasks, and fundamentally, you'll learn to reuse code. The world of application development will never be the same for you!

Source Code

More articles from this author

NoTitleDate
1.An interview with Claudio LassalaOctober 2004
2.An Interview with Rick Strahl from WWC ConferenceDecember 2002
3.Coverage of PDC 2002 in BrasilJune 2002
4.Developing XML Solutions with Visual FoxProOctober 2002
5.Expanding the magazine to the Spanish communityApril 2002
6.Get ready for the UT VFP Worldwide Tournament!July 2002
7.Instant training provides some great advantages!August 2002
8.Light up the candles!June 2002
9.More on Certification with Evan DelayJanuary 2002
10.One Community - Many eventsSeptember 2002
11.The 10-Year Anniversary Event - A Great InitiativeAugust 2002
12.The Community SpeaksMay 2002
13.The Web technologies with Rick StrahlJanuary 2002
14.Third RapoZine's VFP Conference in BrazilOctober 2002
15.UTMag/RapoZine for the communityFebruary 2002
16.VFP 8: The non-visual aspects that we always dreamed ofOctober 2002
17.VFP for Beginners - Part VIIJuly 2002
18.VFP for Beginners: Part VIMay 2002
19.Vieira and Koziol: A partnership that gave an UT Award to FRX2WordFebruary 2002
20.Working with communitiesMarch 2002

Copyright © 1993 - 2014 Level Extreme Inc., All Rights Reserved · Telephone: 506-783-9007 Email: info@universalthread.com · Privacy & Security · Copyright · Terms & Conditions