Utilizzare il designer DataReport di Visual Basic 6
a cura di Amedeo Fantini (requisiti: conoscenza generica di VB)All'interno di Visual Basic 6 è presente uno strumento che permette la creazione di report (denominato per l'appunto Data Report Designer) il quale, allo stesso modo dei più blasonati Crystal Report o ActiveReport, consente di produrre tabulati richiedendo, in molti casi, pochissimo sforzo da parte del programmatore.
Purtroppo la presenza di alcune limitazioni non gli permettono di soppiantare i già citati tool di stampa, ma, acquisendo un po' di dimestichezza nel suo utilizzo, si può benissimo arrivare al punto di utilizzarlo come soluzione esclusiva.
Ovviamente complessità e limitazione nel suo impiego sono in funzione del tipo di output che si desidera ottenere e soprattutto da quello che richiede l'utente finale il quale, nella stragrande maggioranza dei casi, è totalmente a digiuno d'informatica e pretende di ottenere stampato su carta ciò che lui stesso, pervaso da una irrefrenabile vena di surrealismo, è in grado di produrre con una semplice matita.
Spesso sta quindi al programmatore cercare di limitarne la fantasia (e i danni) facendo sì che si possa, ove possibile, trovare una soluzione in grado di soddisfare la richiesta e nel contempo evitare di spendere troppo tempo nel tentativo di ottenere dal tool di stampa ciò che purtroppo non è in grado di offrirvi.Da parte mia posso affermare che da quando utilizzo VB6 sono riuscito ad utilizzare esclusivamente il Data Report Designer per generare i report delle mie applicazioni. Con questo non voglio dire che sia possibile fare tutto, ma a mio parere una buona parte delle problematiche di stampa si può risolvere con l'utilizzo del tool in questione.
Come richiamare il Data Report Designer
Innanzi tutto occorre renderlo disponibile selezionandolo nella finestra di dialogo "Componenti" richiamata dal menu "Progetto" (oppure CTRL+T dall'IDE di VB6) scheda "Finestre di progettazione". Dopodiché possiamo semplicemente aggiungerlo ad un progetto come se si trattasse di una nuova form, premendo il pulsante destro del mouse nella finestra "Gestione progetti / Inserisci / Data Report".
Vi troverete quindi nel nodo "Finestre di progettazione" il vostro DataReport1 pronto all'uso.La struttura
L'oggetto è simile ad un form di Visual Basic e quindi include una finestra di progettazione ed un modulo di codice. La differenza con quest'ultimo sta nel fatto che non è possibile utilizzare i controlli intrinseci nativi o altri controlli ActiveX ma soltanto quelli inclusi nella scheda DataReport.
Le sezioni principali dell'oggetto sono le seguenti:
- Intestazione Report (Report Header):
include il testo che sarà stampato una sola volta all'inizio del report.- Intestazione pagina (Page Header):
include le informazioni che saranno stampate all'inizio di ciascuna pagina.- Dettaglio (Detail):
include la parte del report più interna corrispondente nella maggior parte dei casi ai record. Questa sezione è legata all'oggetto Command di livello inferiore nella struttura gerarchica del Recordset associato.- Piè di pagina (Page Footer):
include le informazioni visualizzate in fondo a ciascuna pagina del report, come ad esempio il numero di pagina. Questa sezione è sempre posizionata a partire dal fondo del modulo e non dal punto nel quale potrebbe essere, ad esempio, arrivato il dettaglio.
In pratica se l'output ottenuto termina a metà del modulo (formato A4) questa sezione si posizionerà con la parte inferiore in corrispondenza della fine del foglio espandendosi verso il centro del modulo, per un'altezza pari al valore impostato nella relativa proprietà Height.- Piè di pagina report (Report Footer):
include il testo stampato alla fine di tutto il report. Questa sezione si posiziona immediatamente sotto la fine dei dati stampati nel dettaglio (o un piè di gruppo) e quindi in molti casi si posiziona davanti alla sezione Page Footer.A queste sezioni si possono aggiungere successivamente altre sezioni, le quali si posizionano in ogni caso sempre al di sopra della sezione Dettaglio (composte sempre da un'intestazione e da un piede).
Naturalmente all'interno d'ogni sezione potete aggiungere i controlli presenti nella scheda DataReport considerando però quanto segue:
- nelle sezioni Report Header, Page Header, Page Footer e Report Footer non è possibile inserire oggetti RptTextBox e quindi non è possibile attingere informazioni direttamente dalla fonte dati associata (si vedrà più avanti come fare);
- nelle sezioni Report Header, Page Header, Page Footer e nelle Intestazioni di gruppo non è possibile inserire oggetti RptFunction;
- l'oggetto RptImage non è associabile ad una fonte dati, quindi non potete utilizzarlo per prelevare direttamente immagini memorizzate sul database.
I componenti
All'interno dell'oggetto DataReport è possibile muoversi utilizzando i seguenti componenti:
- l'oggetto Section:
ciascuna sezione della finestra di progettazione Data Report è rappresentata dall'oggetto Section dell'insieme Sections. In fase d'esecuzione è possibile intervenire sulle caratteristiche d'ogni sezione e sui controlli in essa inseriti.- I controlli Data Report:
Come già specificato in precedenza, si tratta di controlli speciali che funzionano soltanto nella finestra di progettazione Data Report, e sono inclusi nella casella degli strumenti di Visual Basic nella scheda DataReport. Anch'essi si raggruppano nell'insieme Controls presente all'interno della sezione nella quale sono inseriti.All'interno dell'oggetto Data Report ci si riferisce all'oggetto Section utilizzando il suo indice (n) di appartenenza alla collection Sections, oppure tramite il nome univoco specificato nella relativa proprietà .name:
DataReport1.Sections(n).<proprietà> Oppure DataReport1.Sections(nome).<proprietà>Allo stesso modo ci si può riferire ad un controllo in esso contenuto:
DataReport1.Sections(n/name).Controls(n/name).<proprietà>Occorre però tener conto di un limite importante: non è possibile utilizzare questa sintassi per modificare una proprietà dell'oggetto section, se questo è già stato utilizzato almeno una volta nella stampa (tranne in alcuni casi).
Provo a fare un esempio per chiarire il concetto: supponiamo che l'intestazione della pagina (un controllo rptLabel inserito nella sezione IntestazionePagina) sia variabile in funzione delle pagine pari e dispari e che all'inizio del report (evento initialize), sia stata inizializzata con la stringa "Pagina dispari".
Una volta stampata la sezione IntestazionePagina, non è più possibile modificare il contenuto della rptLabel con la stringa "Pagina pari", perché la modifica non si riflette correttamente sul controllo, neanche utilizzando il metodo Refresh del Data Report (il quale ritorna l'errore 8502).Proprietà, metodi ed eventi
Come ogni oggetto che si rispetti, anche il Data Report espone le sue proprietà, i suoi metodi ed i suoi eventi. Ovviamente non sto ad elencarli tutti, essendo ampiamente spiegati nell'Help di VB6, ma mi limiterò ad evidenziare quelli a mio avviso più significativi cercando, ove possibile, di sottolineare alcuni aspetti derivanti soprattutto dall'esperienza diretta e non rintracciabili sulla documentazione in linea.Proprietà
- ReportWidth:
imposta la dimensione del report in Twips, ossia l'effettiva larghezza che dovrà occupare l'output prodotto. Relativamente a questa proprietà vorrei far notare che non è impostando un valore molto alto che si determina l'orientamento della pagina (verticale / orizzontale), perché il Data Report assume automaticamente le caratteristiche della stampante di default installata sul computer, e quindi la dimensione del report risulta ininfluente sulla effettiva dimensione del foglio presente sulla stampante.
Quindi nel caso che l'ampiezza del report superi quella del foglio, tutto il contenuto delle parte destra in eccedenza verrà tagliato invece che riportato sulla riga successiva.
Attenzione a non confondere questa proprietà la proprietà Width, che si riferisce invece alla finestra dentro alla quale sarà posizionato il report, se utilizzato il metodo Show (anteprima di stampa) invece di PrintReport. Ricordo che ci sono 1440 twip per pollice e 567 twip per centimetro.
- LeftMargin, TopMargin, RightMargin, BottomMargin:
si riferiscono allo spazio che deve essere lasciato ai lati del report. Attenzione al loro utilizzo perché spesso si verifica l'errore "La larghezza del report è maggiore della larghezza del foglio".
Da notare che se s'imposta il valore delle proprietà LeftMargin, TopMargin e RightMargin a zero, lo spazio risultante è determinato dal driver della stampante, nella maggioranza dei casi esso corrisponde al valore ottimale (circa 1,5 cm).Metodi
- PrintReport / Show:
permettono la stampa e la visualizzazione dell'anteprima. Passando il valore booleano True si ottiene col primo metodo la visualizzazione della finestra di dialogo relativa alle stampanti, mentre col secondo si rende modale la finestra dentro la quale è visualizzato il report.
- ExportReport:
permette di esportare in un file il report ottenuto; i formati disponibili sono Html e Testo (sia normale che Unicode). Inoltre, partendo da uno dei formati predefiniti, è possibile creare modelli personalizzati per formattare a piacere l'output finale. Ad esempio si potrebbe generare automaticamente un file Html al quale si aggiungono automaticamente anche i dati di formattazione di una particolare pagina Web. Per creare un formato personalizzato occorre creare una stringa contenente, oltre alle necessarie parole chiave Html, anche le costanti rptTagTitle e rptTagBody che verranno sostituite da VB con il contenuto effettivo della stampa. Una volta creata la stringa modello, occorre poi aggiungerla all'insieme ExportFormats in modo che, nella finestra di dialogo relativa all'esportazione, sia presente anche il vostro formato. Per maggiori dettagli vi consiglio di cercare sull'Help di VB6, dove potete trovare tutte le informazioni che vi servono, compreso un bell'esempio di esportazione in formato Html (cercate la parola chiave ExportFormat).Eventi
Purtroppo il Data Report durante la sua esecuzione non genera eventi tali per cui si possa intervenire sulle singole sezioni che lo compongono. Quindi tutto il lavoro di decodifica dei dati deve essere fatto direttamente sul recordset associato (non è il solo sistema per fortuna).
Di seguito riporto quelli che, a mio parere, possono in qualche modo rivelarsi più utili.
- Initialize:
corrisponde all'evento Load di un form. In questa fase è possibile aprire, creare ed associare il command necessario al recupero dei dati, oppure inizializzare i controlli presenti nella varie sezioni. Risulta molto utile quando il command da utilizzare richiede dei parametri in input; in questo punto è quindi possibile crearlo, ed associare il recordset ottenuto all'oggetto Data Report. Nel caso utilizziate questa tecnica, ricordate di chiudere manualmente il recordset nell'evento Terminate.- AsyncProgress:
viene generato ogni volta che una pagina viene inviata alla stampante. E' utile per visualizzare un indicatore d'avanzamento del processo di stampa, oppure per determinare se l'utente ha premuto effettivamente il pulsante di stampa presente nella toolbar dell'anteprima.- ProcessingTimeOut:
è generato ciclicamente durante la stampa del report. Può essere utile per terminare un processo di stampa che si protrae da troppo tempo (ad esempio quando la stampante non risponde):Private Sub DataReport1_ProcessingTimeout( _ ByVal Seconds As Long, _ Cancel As Boolean, _ ByVal JobType As MSDataReportLib.AsyncTypeConstants, _ ByVal Cookie As Long) If Seconds < constTIMEOUT Then If MsgBox("La stampa ha superato il tempo massimo. Si desidera terminare ?", vbYesNo + vbExclamation) = vbYes Then Cancel = True End If End If End SubL'associazione ai dati
Uno dei punti deboli (comunque non limitativo) del Designer Data Report consiste nel fatto che funziona soltanto se associato ad una fonte dati (tipicamente un recordset).Il metodo più semplice è sicuramente quello di utilizzarlo in combinazione con il Designer DataEnvironment attraverso il quale è possibile creare visivamente la struttura che si desidera stampare.
Esiste una limitazione nell'utilizzo dei Recordset gerarchici e cioè, mentre il Data Environment consente di creare gerarchie di oggetti Command (si può fare anche a mano con l'istruzione SHAPE) in cui un oggetto Command contiene più oggetti secondari paralleli tra loro, con la finestra di progettazione Data Report non è possibile raggiungere questo grado di flessibilità in quanto non viene visualizzato più di un oggetto secondario alla volta.
In ogni caso occorre legare ogni sezione che compone il report ad una fonte dati, altrimenti non si riesce proprio a stampare. Questo avviene automaticamente quando si associa il report ad un Command presente nel DataEnvironment utilizzando l'opzione Recupera struttura, mentre è necessario eseguirlo manualmente in tutti gli altri casi.
Nell'esempio che segue si utilizza un recordset di tipo gerarchico per meglio evidenziare la struttura del DataReport, altrimenti risulterebbe più complesso analizzarne le caratteristiche; naturalmente utilizzando un command ad "un solo livello" l'esempio resta valido e si semplifica un po'.
Provate quindi a creare un DataEnvironment connesso al Database Biblio.mdb avente la seguente struttura:
La stringa seguente serve per controllare la struttura del command finale. Il tutto è stato creato semplicemente con il Designer DataEnvironment nel seguente modo: ho creato il command "cmAllAuthors" al quale ho aggiunto il command secondario "cmTitleAuthor" al quale, a sua volta, ho aggiunto un altro command secondario "cmTitles": SHAPE {SELECT * FROM AUTHORS} AS cmAllAuthors APPEND (( SHAPE {SELECT * FROM [Title Author]} AS cmTitleAuthor APPEND ({SELECT * FROM TITLES} AS cmTitles RELATE 'ISBN' TO 'ISBN') AS cmTitles) AS cmTitleAuthor RELATE 'Au_ID' TO 'Au_ID') AS cmTitleAuthorInserite nelle proprietà DataSource e DataMember del DataReport1 che avrete aggiunto al progetto, rispettivamente il nome del DataEnvironment e del Command principale che in tal caso è "cmAllAuthors", e scegliete l'opzione "Recupera struttura" dal menù contestuale che appare premendo il pulsante destro del mouse sull'area del DataReport1.
Come potrete notare, quanto appare successivamente corrisponde alla struttura gerarchica del Command creato, partendo dall'oggetto più esterno per terminare con quello più interno, relativo al dettaglio.
Ogni sezione, composta da una intestazione e da un piede, è stampata per ogni riga contenuta nel rispettivo recordset, seguendo la logica della stampa per "rottura di livello", ossia prima l'intestazione di ogni sezione fino a raggiungere la sezione più interna relativa al dettaglio, e poi il Piè di pagina.A questo punto al report mancano i dati.
Selezionate quindi dalla scheda DataReport un controllo rptTextBox e inseritelo nella sezione corrispondente all'intestazione del primo gruppo (cmAllAuthors_Intestazione) e poi, nella finestra "proprietà" relativa al controllo appena inserito, cliccate sul pulsante che appare alla destra della proprietà DataMember.
Vi verrà proposto il nome del primo oggetto command della gerarchia, ma se ripetete l'operazione per le altre sezioni successive, noterete che vi verranno proposti anche gli altri command che compongono la struttura.
Scegliete quello proposto ed inserite successivamente nella proprietà DataField, il nome del campo del Recordset al quale desiderate riferirvi.
Ripetete l'operazione fino a quando non avrete raggiunto la disposizione di stampa che desiderate.In ogni caso ricordate che la visibilità dei dati contenuti nei Recordset risultanti dipende dal rispettivo livello di creazione. In breve, un riferimento ad un campo può avvenire nella sezione in cui è stato creato e in tutte le sezioni di livello inferiore, ossia i campi del recordset cmAllAuthors sono disponibili anche nelle sezioni legate a TitleAuthors e Dettaglio, mentre i campi del recordset cmTitles sono disponibili solo nella sezione Dettaglio.
Naturalmente questo non è l'unico modo per associare i dati al Data Report, ma come si può ben immaginare è possibile eseguire il tutto anche manualmente, ottenendo così una maggiore scalabilità ed un maggior controllo delle fasi di stampa.
(segue)