Come creare una applicazione in Visual FoxPro

" La rubrica telefonica "

Articolo di Franco Caloni© - Settembre 2001

Questo articolo si prefigge come scopo quello di mostrare ai neofiti di Visual FoxPro (rel. 6.0, di seguito VFP) un'esempio pratico su come si possa realizzare un'applicazione con questo fantastico ambiente di sviluppo.


L'applicazione consiste nella creazione della classica "rubrica telefonica" con annessa agenda di appuntamenti.

 

1 Non usiamo i controlli di VFP!

Partiamo subito con un'argomento che può sembrare difficile, ma non è così. Questa operazione, tra l'altro va fatta subito, o mai più, quindi….non abbiamo scampo.


Per controlli non si intendendono quelli che vengono effettuati durante l'inserimento dei dati come per Es.: "non e' possibile inserire un numero superiore a 10", bensì gli oggetti che vediamo (e usiamo con windows) sulle form: Textbox (le caselle in cui inseriamo dati), Checkbox (le caselle che spuntiamo e che attivano/disattivano opzioni), ecc.


Supponiamo abbiate finito la vostra applicazione che consiste di centinaia di form, andate dal cliente per mostrargli orgogliosi il risultato di tanto lavoro e questo vi chiede di modificare il colore del testo visualizzato nei controlli textbox, questa semplice richiesta vi obbliga a modificare nelle centianaia di form, tutti i controlli textbox che avete: un lavoro pazzesco!

Per questo motivo (naturalmente ce ne sono di più validi, ma ve li lascio scoprire col tempo) si SUBCLASSANO i controlli standard forniti da VFP.

Le parole controllo e classe in VFP sono praticamente la stessa cosa: un controllo textbox è un controllo di classe textbox, una form è un controllo di classe form (ma guarda un po').


Dato però che esistono alcuni controlli (come le form o i container) che possono contenere altri controlli (una form contiene i textbox, ma i textbox non possono contenere nessun altro controllo) quando si parla di "controllo" si intende il singolo controllo, quando si parla di "classe" si parla del singolo elemento ma, nel caso si tratti di un contenitore con all'interno altri controlli, di tutto l'insieme. Inoltre, le classi sono memorizzate in file con estensione VCX (e VCT) e ogni file può contenere più classi contemporaneamente (un po' come le tabelle contengono tanti record).


VFP dà la possibilità di creare nuove classi (quindi controlli) partendo da un'altra classe (o controllo). La nuova classe eredita automaticamente tutte le proprietà, metodi ed eventi della classe da cui deriva (o parentclass), ed e' possibile, naturalmente, modificarne i valori. Se devo modificare il colore del testo (per esempio), basta farlo nella parentclass, e tutte le classi che derivano da quella, recepiscono automaticamente le modifiche.


Le classi base di VFP non sono modificabili, quelle create da voi, si.


Risulta chiaro che se nell'applicazione con migliaia di form, aveste usato come controllo textbox, non quello base di VFP, ma il vostro, per modificare il colore bastava modificare UNA volta il colore nella parentclass, e il cliente era accontentato in 1 minuto.

1.1 Subclassare le classi base di VFP

Prima di procedere con l'analisi dell'applicazione, quindi, subclassiamo tutte le classi base di VFP, un'operazione non divertente, ma che va fatta una volta sola, perche' poi utilizzaremo queste classi in tutte le nostre applicazioni.
Dal menu di VFP: File->New->scegliete "class":

In "class name" dovete inserire il nome che avrà la classe in fase di creazione, consiglio di inserire un prefisso seguito dal nome della classe VFP che stiamo subclassando. Così per la classe "activedoc" creeremo "bactivedoc", per "checkbox", "bcheckbox", via, via, fino all'ultima classe che è "toolbar".
Fondamentale e' che salviate tutte le classi in un unico file (casella "Store in") che chiamerete per esempio "cbase.vcx".
A questo punto bisogna dire a VFP di usare (nei suoi automatismi) le nostre classi base e non le sue, lo si fa andando in: Tools->Options->Scheda Field Mapping, qui vengono "associati" i tipi di dati con la relativa classe. Modificate Class Library e Class Name con le classi appena create:



Questa e' la dialog del mio ambiente di sviluppo.

In questo modo, quando, per esempio, su una form viene creato un campo di tipo character, VFP crea un controllo ti classe "btextbox" (o come l'avete chiamato voi) e non "textbox".

Un'ultima cosa da fare e' quella di "registrare" la classe tra quelle che vengono visualizzate nella toolbar "Form controls", in modo che possano essere utilizzate. Sempre da Tools->Options-> Scheda Controls, selezionate "Visual Class Libraries" e cliccate su "Add", selezionate poi la classe creata in precedenza.




Questa e' la dialog del mio ambiente di sviluppo.

A questo punto siamo pronti!

2 Creare il progetto

La prima cosa da fare è creare una directory sul nostro hard-disk che conterrà i sorgenti dell'applicazione.
Create (sul desktop di windows) una cartella di nome "progetto".
La seconda è creare un progetto: da menu File->New->scegliete Project, salvatelo naturalmente nella cartella progetto (lasciate pure il nome "proj1").
Posizionate il mouse sul progetto, cliccate il tasto destro del mouse e scegliete "Project info", selezionate la pagina "Project" e impostate la voce "Home" alla cartella che avete appena creato ("progetto"), e attivate la casella "Debug info".


 

3 La struttura dei dati


VFP permette di implementare la stessa funzionalità in diversi modi, sta al programmatore decidere quale usare.
Per quanto riguarda la base dati, la mia scelta personale è stata quella di utilizzare il "Database", un tipo di file di VFP che permette di estendere le proprietà delle tabelle, e di automatizzare alcuni compiti, nel mio esempio, quindi, faremo uso del Database, e tutte le tabelle, se non diversamente specificato, saranno incluse nel database.
La base dati conterrà la tabella con i dati dei nominativi inseriti in rubrica e la tabella degli appuntamenti.
Ogni tabella, indipendentemente dai dati contenuti o dalla funzione che deve svolgere, dovrà contenere un campo che sarà il campo chiave della tabella stessa. In questo modo possono essere relazionate tra di loro le tabelle. Il campo verrà sempre chiamato "rid" (Row Identifier, identificativo del record) e' sarà un numero progressivo.


La struttura della tabella nominativi sarà quindi:

Nome campo Tipo di dati
Rid Integer
Cognome Char (30)
Nome Char (30)
Indirizzo Char (30)
Citta Char (30)
Cap Char (6)
Telefono Char (15)
Cellulare Char (15)

La struttura della tabella degli appuntamenti sarà:

Nome campo Tipo di dati
Rid Integer
Codnom Integer
Dataora Datetime
Luogo Char (30)
Note memo

Notate che la tabella appuntamenti contiene il campo "Codnom", questo conterrà il valore del campo "rid" della tabella nominativi, in questo modo è possibile avere una lista degli appuntamenti per nominativo.

3.1 Autonumerazione dei campi chiave

I campi "rid" devono essere autonumerati, nel senso che ad ogni inserimento di un nuovo nominativo/appuntamento, il codice deve essere fornito dal sistema in automatico. VFP non fornisce questo tipo di funzionalità, bisogna quindi scrivere una funzione che svolga questo compito e che utilizzeremo in tutte le future applicazioni.
L'idea e' quella che i progressivi per tabella vengano memorizzati in una tabella dedicata al questo scopo.

La tabella in questione si chiamerà IDS e avrà la seguente struttura:

 

Nome campo Tipo di dati
Table Char (50)
Nextid Integer

Occorre inoltre creare un indice sul campo "table".
A questo punto ecco la funzione che genera l'autonumerazione, funzione che andrete a salvare nel database come stored procedure (poi vedremo come).


FUNCTION NextID(tcAlias)
  LOCAL lcAlias, ;
  lnID, ;
  lcOldReprocess, ;
  lnOldArea, lcupd

 lnOldArea = SELECT()

 IF empty(tcAlias)
  lcAlias = UPPER(ALIAS())
 ELSE
  lcAlias = UPPER(tcAlias)
 ENDIF

 lcOldReprocess = SET('REPROCESS')

 *-- Lock until user presses Esc
 SET REPROCESS TO AUTOMATIC

 IF !USED("IDS")
  USE ids IN 0
 ENDIF
 

 SELECT ids

 IF SEEK(lcAlias, "Ids", "table")
 IF RLOCK()
   lnID = ids.nextid+1
   REPLACE ids.nextid WITH m.lnID in "ids"
   UNLOCK 
 ENDIF
 ELSE
   lnID=1
   insert into ids values(lcAlias, 1)
 ENDIF

 SELECT (lnOldArea)
 SET REPROCESS TO lcOldReprocess
 RETURN lnID
ENDFUNC

Come si usa questa funzione? Calma, calma…

3.2 Creazione delle tabelle

Il prossimo passo e' quello di creare il database e le tabelle "nominativi", "appuntamenti" e "ids".
Dò per scontato che sapete farlo, altrimenti leggete la miniguida (link) e poi tornate qui.

Come avrete capito, alla funzione bisogna passare il nome della tabella per la quale si desidera sapere il prossimo ID. La tabella appuntamenti, inoltre ha il campo che riporta il codice del nominativo, per questo campo il default value sarà: nominativi.rid. Per il campo "dataora" può essere impostata la funzione datetime().

Abbiamo creato la prima stored procedure.

4 Cominciamo a divertirci!

A questo punto create il programma principale che chiamerete main.prg selezionando la pagina "Code" del project manager e cliccando su "New". Inserite questo codice:

set deleted on
set date italian
do menuprinc.mpr
read events
clear all
close data all
close procedure
release all
set sysmenu to defa
cancel

Chiudete e salvate. Verificate che main.prg sia settato come "main" (ovvero il programma da cui parte tutto) selezionandolo, premendo il tasto destro e attivando l'opzione "set main" (main deve apparire in bold).


Create il menu dell'applicazione: attivate la pagina "Other", e cliccate su new, selezionate "menu", viene aperto il menu designer.


La prima voce da inserire nel menu e' l'opzione per uscire dall'applicazione: nella casella "prompt" inserite "File" e in "Result": "Submenu", cliccate sul bottone create, nella casella "prompt" inserite "Esci" e in "Result": "Command", nella casella di testo di fianco a "Result" inserire: "clear events", chiudete e salvate il menu con il nome "menuprinc".


A questo punto creiamo l'applicazione: dal project manager cliccate "build", selezionate l'opzione Build Action: "Applicazion (app)" e attivate le opzioni: "Recompile All File", "Display Errors", "Run After Build", cliccate su ok, e confermate la creazione del file "proj1.app". Il menu di VFP dovrebbe essere scomparso e al suo posto dovrebbe esserci il vostro, l'applicazione e' in esecuzione, per uscire e tornare a VFP, selezionate File->Esci.

Abbiamo provato il corretto funzionamento dell'applicazione, procediamo con la creazione delle form di gestione dei dati.

Tornate in modifica del menu "manuprinc", aggiungete un'altra voce del menu sotto la casella "File": "Gestione archivi", lasciate "result" a "submenu", cliccate su "create" e inserite una voce con: "prompt" -> "Gestione nominativi", "result" -> "Command", il comando-> "do form gnominativi". Notate che potete provare il menu cliccando sul bottone "Preview". Dovreste avere la voce "File" e la voce "Gestione archivi". Chiudete e salvate.

4.1 Form di gestione dei nominativi

Create la form "gnominativi": attivate la pagina "Docs", e cliccate su new, viene aperto il form designer.

Attivate la scheda delle proprietà (destramouse sulla form->properties) e impostate la proprietà "Caption" a "Gestione nominativi", impostate anche "autocenter" a .true..

Dimensionate la form in maniera appropriata, questa form deve visualizzare i dati dei nominativi.

Attivate il Dataenvironment con: destramouse sulla form->Dataenvironment-> aggiungete le tabelle "nominativi" e "ids", a questo punto selezionate uno alla volta i campi della tabella "nominativi" e trascinateli sulla form (dove meglio credete), verranno creati dei controlli textbox di classe "btextbox" (la nostra) e non "textbox" (quella standard di VFP). Per verificarlo attivate la scheda delle proprietà e selezionate uno qualsiasi dei textbox, la proprietà "classlibrary" dovrà essere il file cbase.vcx creato in precedenza.

Mancano i pulsanti per: creare e cancellare nominativi, un eventuale meccanismo per scorrere l'elenco e il pulsante per uscire dalla form.
Partiamo da quest'ultimo: attiviamo (nel caso non lo fosse già), la "Form controls toolbar": dal Menu->View->Form controls toolbar:


cliccate il pulsante e attivate la librearia "Cbase".
Vengono attivati i controlli che abbiamo creato in precedenza:cliccate su "bcommandbutton", posizionatevi sulla form e cliccate nel punto in cui volete posizionare il pulsante di uscita dalla form, viene creato un controllo "bcommandbutton".
Selezionatelo e attivate la scheda "Properties", impostate "Caption"="Esci", fate un doppio click su "click event" (scusate il gioco di parole!) e nella finestra che appare ("bcommandbutton1.click") scrivete: "Thisform.release", chiudete questa finestra.
Provate il funzionamento della form eseguendola premendo "CTRL/E", la form dovrebbe andare in esecuzione, cliccando sul bottone "esci" dovreste tornare in design.
Aggiungete un pulsante per inserire un nuovo record nella stessa maniera settando "Caption"="Nuovo", nell'evento "click" inserite:

append blank in nominativi
thisform.refresh

Aggiungiamo dei pulsanti per lo scorrimento dei record presenti in tabella: create sulla form 4 commanbutton, settate le proprietà caption, rispettivamente a "primo", "precedente", "successivo" e "ultimo". Nell'evento "click" di questi commandbutton scrivete:

* commandbutton "primo":
Go top in nominativi
Thisform.refresh

* commandbutton "precedente":
SKIP -1 in nominativi
IF BOF("nominativi")
GO TOP in nominativi
ENDIF

* commandbutton "successivo":
SKIP 1 in nominativi
IF EOF("nominativi")
GO BOTTOM in nominativi
ENDIF
thisform.refresh

* commandbutton "ultimo":
GO BOTTOM in nominativi
thisform.refresh

Aggiungete un ultimo commandbutton che avrà caption "elimina" il codice da inserire nell'evento click sarà:

msgerr="Eliminare il nominativo "+alltrim(ThisForm.txtCognome.value)
if messagebox(msgerr,33,"Eliminazione dati")=1
delete in nominativi
skip in nominativi
if eof("nominativi")
skip -1 in nominativi
endif
thisform.refresh
endif


Lanciate in esecuzione la form (o generate l'applicazione da project manager), dovreste essere in grado di aggiungere, modificare, eliminare record.
Ecco la form:

4.2 Form di gestione degli appuntamenti

Questa form implementerà funzionalità più sofisticate che la precedente.
In questa form l'utente avrà la possibilità di inserire/cancellare appuntamenti, ma anche avere l'elenco per nominativo.
L'idea quindi e' quella di avere sulla stessa form i classici controlli textbox per l'inserimento dei dati, e un controllo grid per visualizzare l'elenco degli appuntamenti. Per selezionare il nominativo per il quale vogliamo elencare gli appuntamenti useremo un controllo combobox.
Create la form "gappuntamenti" (oramai lo sapete fare!), nel dataenvironment aggiungete le tabelle "appuntamenti", "nominativi" e "ids".
Sempre nel dataenvironment create la relazione tra le tabelle nominativi e appuntamenti, selezionando il campo rid delle tabella nominativi e trascinandolo sul campo codnom della tabella appuntamenti. Questa relazione fa si' che ad ogni spostamento del record pointer nella tabella padre (la tabella da cui si parte con l'operazione di drag&drop, in questo caso nominativi) VFP automaticamente (senza che il programmatore scriva ulteriore codice) renda visibili solo i record della tabella figlia (appuntamenti) che hanno nel campo "codnom" il relativo codice nominativo (piu facile a farsi che a dirsi!). Settate la proprietà "initialselectedalias" del DE a "nominativi".
Ora chiudete il DE e create (esattamente come per gnominativi) un controllo bcommandbutton per uscire dalla form.
Create il controllo bcombobox che verrà usato dall'utente per selezionare il nominativo sul quale lavorare, settate le proprietà:

boundcolumn=3
boundto=.t.
columncount=3
columnwidths=200,200,40
rowsource=nominativi.cognome,nome,rid
rowsourcetype=6
style=2

Spostate il controllo combobox e i commandbutton in alto nella form.
Eseguite la form, il combo dovrebbe contenere l'elenco dei nominativi presenti in archivio (se non ne avete inseriti, fatelo!).
Create un controllo bpageframe e dimensionatelo a sufficienza. Destramouse sul pageframe->edit, il contorno del pageframe viene evidenziato, state editando il contenuto del controllo pageframe.
Spendiamo due parole sui controlli container (quelli, per intenderci che possono contenere altri controlli: container, grid, pageframe).
Quando si seleziona (cliccandoci sopra) una classe che contiene altri controlli, tutto quello che fate, lo fate sul contenitore, NON sugli oggetti contenuti. Per modificare gli oggetti contenuti dovete EDITARE il contenitore cliccando il tasto destro del mouse e selezionando appunto Edit, in questo modo potete selezionare i singoli oggetti contenuti nel contenitore.
Cliccate su "Page 1", verrà selezionata la pagina 1 del pageframe (le pagine sono oggetti contenuti nel pageframe, come tutti gli altri), cliccate su "Page 2", viene attivata la pagina 2.
Cambiate la caption della pagina 1: selezionatela, attivate (se non attiva) la finestra delle proprietà, selezionate la proprietà "caption", impostatela a "Elenco". Settate la caption della pagina 2 a "Dettaglio".
Selezionate di nuovo la pagina 1, dal menu di VFP->view->dataenvironment, selezionate tutti i campi della tabella "appuntamenti" (tenendo premuto il tasto CTRL) e trascinateli sulla pagina 1, verrà creato un controllo di classe bgrid, dimensionatelo come volete.
Create un bcommandbutton per creare un appuntamento, settate caption="nuovo" inserite nell'evento "click":

append blank in appuntamenti
thisform.refresh
ThisForm.Bpageframe1.Page1.grdAppuntamenti.setfocus

Create un bcommandbutton per eliminare appuntamenti e inserite nel solito evento click:

msgerr="Eliminare l'appuntamento del "+dtoc(appuntamenti.dataora)
if messagebox(msgerr,33,"Eliminazione dati")=1
  delete in appuntamenti
  skip in appuntamenti
  if eof("appuntamenti")
     skip -1 in appuntamenti
  endif
  thisform.refresh
endif

Lanciate la form e verificate che creando un nuovo appuntamento vengano riportati i i valori giusti nelle colonne "rid", "codnom" (in questa colonna deve essere riportato il rid del nominativo visualizzato nel combo, attenzione!) e "dataora".

Notate che gli appuntamenti vengono automaticamente filtrati per nominativo (grazie alla relazione nel dataenvironment).

Eliminate le colonne della bgrid che non servono: "rid" e "codnom" selezionando pageframe, destramouse->edit, selezionate la grid, destramouse->edit->cliccate sulla colonna "rid" e premete il tasto "canc" (viene chiesta conferma dell'operazione di cancellazione), ripetete per la colonna "codnom".

Dimensionate le restanti colonne secondo i vostri gusti.

Avrete notato che la colonna "note" visualizza la scritta "memo", questo perche' i campi di tipo "memo" devono essere visualizzati utilizzando un controllo "editbox"e non "textbox" come invece crea di default VFP.
Vediamo come fare per sostituire il controllo "textbox"(una procedura un po' macchinosa): selezionate la colonna, nella scheda delle proprietà, selezionate (nel combo in alto) il controllo "text1" della colonna interessata, successivamente ri-attivate il form designer (cliccandoci sopra) e premete il tasto "canc", il controllo dovrebbe essere stato rimosso.
A questo punto inserite il controllo "beditbox" semplicemnte selezionandolo dalla "form controls" e cliccando col mouse sulla colonna in cui deve essere creato.

Settate la proprietà della grid: rowheight=36.
Settate la proprietà della colonna con l'editbox: sparse=.f.
Settate le proprietà dell'editbox:

borderstyle=0
margin=0

Salvate ed eseguite la form, come vi sembra?


Tornate in design, attivate la pagina 2 del pageframe e droppateci (trascinandoli dal dataenvironment) i campi cognome, nome, indirizzo, città, cap, telefono, cellulare (della tabella nominativi) e i campi: dataora, luogo, note della tabella appuntamenti.

Inserite nel metodo "activate" della pagina 2 il codice:

this.refresh

e nel metodo "valid" del combo:

ThisForm.Bpageframe1.Page2.refresh

Dovremmo esserci, salvate, tornate nel project manager e inserite una voce nel menu per l'esecuzione della form gappuntamenti, generate l'applicazione, funziona ?

Se non funziona, qui trovate i sorgenti dell'applicazione!!

© Articolo: FRANCO CALONI- Settembre 2001 - Riproduzione vietata

© FoxPro e Visual FoxPro sono un marchi registrati da Microsoft Corporation

 



Data: 04/09/2001
webmaster@foxitaly.com

 

dal 22 Giugno 1999