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:

  1. Intestazione Report (Report Header):
    include il testo che sarà stampato una sola volta all'inizio del report.
  2. Intestazione pagina (Page Header):
    include le informazioni che saranno stampate all'inizio di ciascuna pagina.
  3. 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.
  4. 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.
  5. 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:

I componenti
All'interno dell'oggetto DataReport è possibile muoversi utilizzando i seguenti componenti:

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à

Metodi

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.

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 Sub

L'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 cmTitleAuthor

Inserite 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)