Visual Basic 6, come è noto, consente di creare anche applicazioni di tipo gestionale. Questo tipo di software deve possedere in molti casi, la possibilità di stampare documenti, resoconti, prospetti, lettere commerciali e quant'altro.
Nonostante VB6 disponga comunque di un buon seppur leggermente criptico sistema di reporting, a volte questo può non bastare per le proprie necessità, soprattutto se si desidera uno stile fortemente personalizzato sulle stampe da produrre.
La metodologia che vorrei esporre in questo articolo è quella di creare i propri modelli in Word, ottenendo così la possibilità di personalizzazioni piuttosto variegate, che contemplano ad esempio formattazioni particolari sia dei caratteri che dei paragrafi in genere, l'inserimento di tabelle, immagini, forme e così via.
L'unica accortezza che è necessario utilizzare è l'utilizzo dei Campi Modulo, nei punti dove sarà necessario inserire i dati provenienti dalla nostra applicazione, così che possano essere identificati e valorizzati.
Apriamo quindi il nostro Microsoft Word (va sicuramente bene qualsiasi versione dalla 2000 in poi) e iniziamo a scrivere, ad esempio, una lettera commerciale per ricordare ad un nostro cliente che la sua polizza assicurativa è scaduta:
Come dicevo prima, avremo l'accortezza di inserire, nei punti in cui sarà necessario inserire i dati relativi al cliente o alla polizza, dei Campi Modulo reperibili visualizzando la barra degli strumenti Moduli. Qualora non fosse già presente, per visualizzarla andiamo sul menu Visualizza -> Barre degli Strumenti -> Moduli.
Diventerà visibile la seguente barra e l'icona adeguata per l'inserimento di un Campo Modulo è la prima a sinistra (nel cerchio rosso)
Ogni campo inserito andrà adeguatamente rinominato, al fine di identificarlo in maniera esatta da codice, una volta terminato il modello. Per questo scopo possiamo fare doppio click sul campo da rinominare. Si aprirà la finestra delle impostazioni per il campo di testo, quindi nella sezione Impostazioni Campo variamo il contenuto del campo Segnalibro attribuendo il nome che riteniamo più opportuno.
Per esercizio, anzichè creare un modello da zero, si può prendere a base il documento Word fornito a corredo dell'articolo che contiene, opportunamente predisposti e nominati, dei campi di esempio.
A questo punto, per evitare modifiche indesiderate alla lettera-modello, si può "bloccare" il testo, lasciando comunque invariata la possibilità di editare i campi di testo. Per bloccare il modello basta cliccare l'icona del lucchetto sulla barra degli strumenti Moduli
Saviamo pure il documento, nominandolo opportunamente, per il futuro uso da Visual Basic.
Per rendere ancora più semplice l'attuazione di questo metodo, ho incluso nel progetto di esempio relativo all'articolo anche un Database Access (.mdb) contenente 2 clienti e 2 polizze. Ovviamente il DB è assolutamente scarno e adatto all'esclusiva funzionalità esplicativa che l'articolo intende avere.
Ci interfacceremo a questo DB da Visual Basic utilizzando il modello ad oggetti ADO, per cui sarà bene ricordarsi inserirlo tra i riferimenti in Visual Basic, prima di iniziare a scrivere il codice. Non sarà necessario farlo, in quanto già inserito, nel caso in cui si usi il progetto Visual Basic fornito in allegato.
Da VB6 è veramente semplice accedere ai campi modulo precedentemente inseriti, poichè si utilizzerà l'automazione OLE per definire da codice un'istanza di Word, sfruttando così il modello ad oggetti fornito dal pacchetto Office. Attraverso l'automazione possiamo accedere ad ogni singolo oggetto, proprietà e metodo dell'applicazione e, di conseguenza, anche ai campi modulo, visto che fanno parte di una Collection denominata FormFields che è possibile indirizzare sia numericamente che - cosa più importante - attraverso il segnalibro impostato in fase di definizione dei campi.
Un esempio per tutti:
wDoc.FormFields("tLuogo").Result = "Sede della Ditta"
wDoc, come si vedrà nel codice di esempio in allegato, definisce l'istanza di Word aperta da codice.
L'applicazione VB6 è anch'essa ridotta all'essenziale e consta di un solo Form che ospita una ListBox e 2 pulsanti.
La ListBox viene popolata con i dati presenti sul DB utilizzando un Recordset ADO e un ciclo While, mentre la compilazione vera e propria del modello Word, è affidata al codice presente nell'evento Click di uno dei pulsanti:
Private Sub cmdCompila_Click() Dim wApp As Object Dim wDoc As Object Dim SQL As String Dim rsPolizza As ADODB.Recordset Dim rsCliente As ADODB.Recordset Dim sNomeLettera 'Apro il recordset della polizza selezionata Set rsPolizza = New ADODB.Recordset SQL = "SELECT * FROM tPolizze WHERE IDPolizza = " & lstPolizze.ItemData(lstPolizze.ListIndex) rsPolizza.Open SQL, cnDB, adOpenForwardOnly, adLockReadOnly, adCmdText 'Apro il recordset del cliente relativo alla polizza selezionata Set rsCliente = New ADODB.Recordset SQL = "SELECT * FROM tAnagrafica WHERE IDCliente = " & rsPolizza("rifIDCliente").Value rsCliente.Open SQL, cnDB, adOpenForwardOnly, adLockReadOnly, adCmdText 'Apro il file Word del Modello della Lettera Set wApp = CreateObject("Word.Application") Set wDoc = wApp.Documents.Open(App.Path & "\LetteraEsempio.doc") 'Compilo i campi non corrispondenti al recordset wDoc.FormFields("tNumPolizzaOgg").Result = rsPolizza("NumPolizza").Value wDoc.FormFields("tIntestazioneLett").Result = rsCliente("Cognome").Value & " " & rsCliente("Nome").Value wDoc.FormFields("tLuogo").Result = "Sede della Ditta" wDoc.FormFields("tData").Result = Date 'Adesso wDoc.FormFields("tTermineMax").Result = DateAdd("y", 20, Date) ' Tra 20 giorni da adesso On Error Resume Next 'Salta l'errore se un campo non esiste 'Con un ciclo riempio i campi corrispondenti sul file di Word For i = 0 To rsCliente.Fields.Count - 1 wDoc.FormFields("t" & rsCliente.Fields(i).Name).Result = rsCliente.Fields(i).Value Next i For i = 0 To rsPolizza.Fields.Count - 1 wDoc.FormFields("t" & rsPolizza.Fields(i).Name).Result = rsPolizza.Fields(i).Value Next i On Error GoTo 0 'Ripristina il gestore degli errori 'Salvo la lettera compilata con il nome scelto dall'utente sNomeLettera = InputBox("Specificare il nome della lettera") If Not sNomeLettera = "" Then wDoc.SaveAs App.Path & "\" & sNomeLettera End If 'Chiudo il documento Word e annullo le variabili wDoc.Close (False) wApp.Quit Set wDoc = Nothing Set wApp = Nothing 'Chiudo i Recordset e annullo le variabili rsCliente.Close rsPolizza.Close Set rsCliente = Nothing Set rsPolizza = Nothing End Sub
Come si può notare, innanzitutto vengono definiti gli oggetti indispensabili all'operazione di compilazione, ovvero l'applicazione Word, il Documento e i Recordset relativi al Cliente e alla Polizza, quindi vengono aperti i Recordset con opportune frasi SQL, sulla base della selezione effettuata sulla ListBox.
Il documento Word viene aperto con 2 semplici righe:
Set wApp = CreateObject("Word.Application") Set wDoc = wApp.Documents.Open(App.Path & "\LetteraEsempio.doc")
Considerato che per definire i Campi Modulo di Word ho utilizzato gli stessi nomi dei campi presenti nelle tabelle del DB, aggiungendo una "t" come prefisso, riesce particolarmente semplice popolare il documento con un ciclo For...Next.
Per evitare errori sui campi che sul documento Word non verrebbero trovati (IDCliente, rifIDCliente, IDPolizza) ho attivato un gestore di errori (On Error Resume Next) che permette di ignorare l'errore, qualora si presenti. In questo caso l'uso l'uso di On Error Resume Next è sicuro e controllato, perchè l'unico errore che può presentarsi nella circostanza in cui viene usato è la mancanza di un nome di campo, ed è proprio quello che vogliamo ignorare.
Alla fine dei cicli, infatti, l'esposizione degli errori viene ripristinata con On Error Goto 0
Terminata la valorizzazione dei campi del documento, viene richiesto un nome di file, che servirà per salvare il modello con un altro nome, conservando così la copia originale.
Ovviamente, in una situazione reale si potrà prevedere una gestione più articolata del documento risultante, implementando, ad esempio, la stampa del modello ottenuto, piuttosto che il salvataggio in un'altra cartella attraverso una CommonDialog o l'invio tramite posta elettronica.
Non esitate nel fornire i vostri commenti, in caso vogliate farmi presente delle inesattezze o per richiedere ulteriori spiegazioni, in merito all'argomento trattato.
HEADER
Ciao Gianni,
La cosa è fattibile senz'altro anche da VBA.
Nell'editor di VBA dell'applicazione che intendi usare per accedere al DB, ti basta spuntare tra i riferimenti (Strumenti -> Riferimenti...) la libreria Microsoft ActiveX Data Object 2.8 o la più recente che hai.
L'accesso a Word viene realizzato attraverso l'uso del Late Binding, quindi non hai bisogno di referenziare nulla.
Tutto il codice dovrebbe rimanere pressochè identico ed è quello presente nell'articolo (Sub cmdCompila_Clic k())
Ciao Luigi, in teoria sarebbe possibile, ma dovresti prevedere, a priori, il numero di nominativi da inserire, predisponendo di conseguenza il numero sufficiente di campi Modulo.
Ritengo sia meglio lasciar perdere i campi modulo in favore di una tabella, che puoi tranquillamente creare e compilare da codice.
Per un aiuto sul codice da utilizzare ti conviene registrare delle macro in Word, studiandone la struttura e cercando di adattarla al tuo contesto.
Grazie comunque e ciao
Ciao Giovanni,
Il comportamento che riscontri è normale, visto che la routine di compilazione del documento prevede il salvataggio del documento.
Se a te non interessa il salvataggio ma solo la stampa di ogni record, allora devi modificare la routine in modo che, al posto del salvataggio, venga eseguita la stampa.
In particolare, dovresti eliminare queste righe:
'Salvo la lettera compilata con il nome scelto dall'utente
sNomeLettera = InputBox("Specificare il nome della lettera")
If Not sNomeLettera = "" Then
wDoc.SaveAs App.Path & "\" & sNomeLettera
End If
sostituendole con queste:
wDoc.PrintOut Background:=False
DoEvents
Spero ti sia utile.
Potete aiutarmi?
Ciao Fra,
Fermo restando che per la stampa di una fattura non ritengo che il metodo dell'articolo sia molto pratico, quello che posso consigliarti è di creare un ciclo sulle righe da stampare.
Quando arrivi a 20 stampi la fattura e cancelli le righe, quindi scrivi le restanti 10.
Altrimenti puoi creare una seconda pagina con le rimanenti 10 righe.
Mi spiego meglio: dalla mia applicazione apro un modello word che ha la struttura di una fattura su una sola pagina, riempio i campi utilizzando i segnalibri (quindi associo ad esempio al segnalibro ART1 il codice articolo della prima riga, ART2 il codice articolo della seconda, ecc.), arrivo fino alla fine della tabella prevista dal mio modulo, ma mi serve di stampare ancora 10 articoli (righe). A questo punto vorrei mantenere il documento Fattura che ho creato utilizzando il modello Fattura e aggiungere un'altra pagina (nello stesso documento) che sia sempre una copia del modello. Io sono riuscita a fare tutto, ma quando creo un'altra pagina mi si crea una pagina bianca nello stesso documento e non con la struttura del modello. Come posso fare?
Considera che io sono autodidatta, ho scopiazzato qua e la cercando di capire e riutilizzare quello che ho trovato. Mi sembra di aver capito che devo creare delle Section del documento.....ma non sono riuscita ugualmente a risolvere il problema. In realta' io cerco di utilizzare WORD perche' ho trovato piu' informazioni, ma sarebbe uguale utilizzare un pdf.
Sai aiutarmi?
Quando dicevo di creare una seconda pagina intendevo di realizzare un secondo modello, sulla seconda pagina, con i campi adeguatamente contrassegnati con i segnalibri, ovviamente susseguenti a quelli già utilizzati per la prima.
Dal codice basterà, come avviene già per la prima pagina, indirizzare i valori da stampare sui corrispondenti segnalibri, senza far caso a quale pagina essi si riferiscono.
In pratica, se dovrai stampare sul segnalibro "ART30" e questo si trova sulla seconda pagina, non farà alcuna differenza e il campo verrà riempito ugualmente.
Cosa dovrei fare: creare un modello con 10 pagine (che comunque sono tante), assegnando segnalibri diversi. Poi creare un documento utilizzando il modello e procedere al riempimento, quando ho finito cancello le pagine rimanenti. Non saprei.....pensavo di trovare una soluzione diversa....a te sembra valida?
Sinceramente te lo avevo già detto qualche risposta fa...
Nel tuo caso il metodo dell'articolo non mi sembra confacente all'esigenza.
Personalmente cambierei totalmente approccio. Non so consigliarti quale perchè non conosco il contesto della tua applicazione, ma in linea generale, se usi Visual Basic 6 potresti orientarti sul DataReport, mentre se usi un'altra applicazione del pacchetto Office potresti pensare all'uso di Excel...
Comunque non vorrei andare troppo fuori tema, rispetto all'argomento dell'articolo
ragione sociale
via.....
cap citta provincia
Mi serve mantenere il layout inserito e quindi passare sia i testo che i cr + lf per far si che il testo si presenti nello stesso modo come inserito nella app. Invece nel modulo mi finisce tutto in fila una parola dietrol'altra. Ho controllato cosa gli arriva e pare che al modulo arrivi testo + cr, CHR(13), ma non il Line Feed chr(10) malgrado lo passi insieme al testo. Hai un idea di come possa risolvere il problema?
Grazie
Prova ad effettuare un replace dei vbCrLf con Chr(13)+Chr(10)
CIAO GRAZIE
Hai qualche suggerimento utile? non ci sto capendo il verso!!!
Grazie.
Complimenti, e grazie per il contributo che dai allo sviluppo con VB6
Ciao Pietro, il codice é relativo al VBA di Excel e così com'è non si può usare in Writer/Base di Open Office.
Grazie per i complimenti
come posso applicarlo ad un report di access, ovvero avrei
bisogno che mi riportasse più record contemporaneame nte nel foglio word es. lo utilizzo per ordinare degli articoli quindi
più righe. grazie ancora attendo gradito aiuto
Ciao Gianni, il codice e la metodologia esposti nell'articolo sono applicabili solo a word. Il report di Access necessita di altri metodi per realizzare quello che ti serve.
RSS