Il blog di Gianni Giaccaglini

Blog su VBA e VSTO
Gianni Giaccaglini

My Links

News

NB - V. anche gli ARTICOLI (in fondo a questa barra)
Solo quesiti validi a: giannigiac@tin.it
Il mio Best seller su VBA
(v. www.hoepli.it)


Il mio ultimo libro su Open XML
(v. www.FAG.it):



La mia nipotina ELISA

Foto con dedica a ME di
Bill Gates giovanissimo
nei mitici anni 80!

Categorie Post

Categorie Articoli

Archivio

Immagini

Blog Stats

Gestire documenti Word mediante Automation

Gestione documentaria con Automation (esempio didattico)

Il trattamento di documenti, in particolare di quelli creati con Microsoft Word sta assumendo un’importanza crescente nelle aziende. Senza ricorrere a particolari (e costosi!) software, due sono i mezzi possibili:

·         Utilizzo del formato Open XML;

·         Utilizzo della tecnologia (OLE) Automation

Il primo ha il vantaggio di funzionare anche su macchine ove Word non è installato, tuttavia NON è in grado di gestire il formato .doc delle versioni di Word precedenti la 2007 o la prossima 2010. In attesa che Open XML si affermi totalmente consigliamo il secondo che da tale limitazione non è affetto.

Purtroppo neanche con Automation sono tutte rose e fiorellini, in quanto non solo può presentare rallentamenti con archivi corposi, ma perché la libreria specifica è offerta in più versioni, cosa che ne complica la diffusione (deployment) su PC dotati di edizioni eterogenee (v. più avanti). Per questo motivo, ossia per evitare di essere disturbato con chiarimenti & lagnanze da parte dei meno esperti, non fornisco un modellino bell’e pronto bensì le (semplici) indicazioni per farselo da soli, unitamente - ci mancherebbe - ai necessari listati VBA.

L’idea base: un minimo di strutturazione

I file Office differiscono nettamente dalle tabelle dei database per la loro natura libera, fin troppo. In tali condizioni pretendere di gestire documenti “qualsiasi” è utopistico, eccezion fatta per le ricerche di stringhe e simili, per le quali esiste già una varietà di strumenti più o meno sofisticati. Per dare un senso più “aziendale” alla faccenda mi pare indispensabile prevedere un minimo di struttura nei documenti di un certo tipo, auspicabilmente creati a partire da tipici template (file di estensione .dot).

A tale scopo ho, anche per una semplificazione didattica ho previsto la presenza di due caratteristiche:

a)      un Oggetto, precisamente a seguito di una stringa “OGGETTO: “ e fino al primo carattere CR (Carriage Return) di codice ascii 13;

b)      una TABELLA contenente i dati rilevanti di quel certo documento (fattura, bolla o qualunque altra cosa).

Inoltre, sempre per semplicità, ho ipotizzato che tutti i .doc (o docx) di quel certo tipo risiedano nella stessa directory che ospita anche il modello Excel che li elabora e siano del tipo seguente:

Primo Documento

OGGETTO: Questo e quello per me pari sono.

CODICE

Descrizione

Quantità

Prezzo

AA

Mele

120

2

BB

Pere

150

3

 

Resto del documento...

Secondo Documento

OGGETTO: Quanto è bella giovinezza, che si fugge tuttavia.

CODICE

Descrizione

Quantità

Prezzo

AA

Mele

100

2

BB

Pere

90

3

AA

Mele

120

2

 

Resto del documento...

Eccetera.

 

 

A sua volta il modellino Excel proposto ha grossomodo l’aspetto seguente (sulla destra viene emulato un pulsante ActiveX etichettato, in modo eloquente, “Inserisci l’Oggetto”):

 

A

B

C

D

E

F

1

Documento Word

Oggetto

 

 

 

 

2

Primo documento.doc

 

 

 

 

 

3

Secondo documento.doc

 

 

Inserisci  l'Oggetto

 

4

Terzo documento.doc

 

 

 

 

 

5

Quarto documento.doc

 

 

 

 

 

6

Quinto documento.doc

 

 

 

 

 

7

 

 

 

 

 

 

8

 

 

 

 

 

 

9

 

 

 

 

 

 

10

 

 

 

 

 

 

 

Nota Sempre per semplicità e... pigrizia i nomi dai vari documenti sono già scritti in colonna A, da A2 in giù. Il codice VBA per caricarli – ad es. con un controllo Common Dialog – è lasciato per esercizio.

 

Per fruire delle macro che descriveremo è necessario che, nell’Editor VBA, vengano fissati, previa scelta dal menu Strumenti del comando Riferimenti... i riferimenti seguenti:

ü  Visual Basic For Application (ovviamente!)

ü  OLE Automation (indispensabile)

ü  Microsoft Excel ** Object Library

ü  Microsoft Word ** Object Library

I due asterischi (**) stanno per il numero di versione, che è 11 con MS Office 2003 e 12 con MS Office 2007. Di qui la preannunciata necessità di inserire la libreria corretta a seconda della versione installata su questo o quel PC.

Nota  Un certo MioModello.xls nato, putacaso, sotto Excel 2007 se caricato in Excel 2003 esibirebbe un’indicazione di non disponibilità alla sinistra del riferimento Microsoft Word 12 Object Library. Il rimedio al malfunzionamento delle macro è ovvio (sostituire 12 con 11) ma, ahimè, manuale.

Le macro per inserire i vari Oggetto

Premesso che tutte le routine si possono (ma non obbligatoriamente) scrivere nel modulo Foglio1. Ecco la prima tranche di codice finalizzata all’inserimento dell’Oggetto in colonna B, ovvero accanto a ciascun nome di documento.

La prima coppia di routine ha avuto scopo sperimentale:

Sub ApriDoc(Docum As String)

  Dim QuestaDir As String

  QuestaDir = ThisWorkbook.Path & "\"

  Dim MioDoc As Document

  Set MioDoc = GetObject(QuestaDir & Docum)

  Dim ParagrOggetto As String

  With MioDoc

    ParagrOggetto = .Paragraphs(2).Range.Text

  End With

  ParagrOggetto = Replace(ParagrOggetto, "OGGETTO: ", "")

  MsgBox ParagrOggetto

  MioDoc.Parent.Application.Quit

  Set MioDoc = Nothing

End Sub

 

Sub ProvaApriDoc()

  ApriDoc "Secondo documento.doc"

End Sub

 

Commenti essenziali. Diciamo solo che QuestaDir = ThisWorkbook.Path & "\" determina la directory che per ipotesi accomuna il modello Excel e i vari archivi .doc. Subito dopo interviene Automation con il codice GetObject applicato al file QuestaDir & Docum che palesemente fornisce il pathname completo del file Docum passato come argomento. Quanto a ParagrOggetto in questa prima battuta lo si prende pari al secondo paragrafo, Paragraphs(2), del Docum, una regola un po' troppo restrittiva che comunque “Secondo documento.doc” dovrà rispettare.

La routine termina eliminando con Replace della stringa inziale “OGGETTO: “ da tale ParagrafOggetto in modo che il solo contenuto dell’Oggetto venga enunciato dalla MsgBox. Si notino infine due importanti istruzioni. La prima, con Miodoc.Parent punta al babbo del documento, che poi è l’applicativo Word che agisce nell’ombra. Dal quale si deve, tassativamente!, uscire con .Application.Quit.

Nota In assenza di tale istruzione si incappa in inattese quanto fastidiose comparse di Word, che reclama la propria chiusura. I più esperti se la cavano obbedendo all’imperativo tornando regolarmente a Excel, ma la situazione non è delle più felici...

 

La seconda istruzione, Set MioDoc = Nothing libera l’oggetto incorporato in Miodoc.

Dopo le precedenti lungaggini lascio interamente all’esegesi autonoma dei più o meno esperti le routine affidate alla fin della fiera al pulsante CommandButton1, il cui effetto è quello già detto di affiancare tutte le celle (contigue) a partire da A2 con gli Oggetto di ciascun file .doc elencato. Anche il caso di Oggetto mancante è previsto, come si può constatare.

Function OggettoDocum(ByRef Doc As Document) As String

  Dim Ogg As String, PosOggetto As Integer

  On Error GoTo Fine

  Ogg = "OGGETTO:"

  With Doc

    For Each Paragr In .Paragraphs

      PosOggetto = InStr(1, UCase(Paragr.Range), "OGGETTO:")

      If PosOggetto > 0 Then Exit For

    Next

  End With

  If PosOggetto = 0 Then

    OggettoDocum = "OGGETTO ASSENTE!"

  Else

    OggettoDocum = Replace(Paragr.Range, "OGGETTO: ", "")

  End If

  Doc.Close

Fine:

End Function

 

Sub InserisciOggetto()

  Dim ZonaDocum As Range, CellaDoc As Range

  Dim QuestaDir As String

  On Error GoTo Fine

  QuestaDir = ThisWorkbook.Path & "\"

  With Range("A2")

    Set ZonaDocum = Range(.Cells(1), .End(xlDown))

  End With

  Dim MioDoc As Document

  Dim NumDoc As Integer, i As Integer

  NumDoc = ZonaDocum.Count

  For i = 1 To NumDoc 'Each CellaDoc In ZonaDocum

    Set MioDoc = GetObject(QuestaDir & ZonaDocum(i)) 'CellaDoc.Value)

    ZonaDocum(i, 2) = OggettoDocum(Doc:=MioDoc)

    If i = NumDoc Then

      MioDoc.Parent.Application.Quit ' Chiudi Word!

    End If

  Next

Fine:

End Sub

 

Private Sub CommandButton1_Click()

  InserisciOggetto

End Sub

Le macro per inserire la tabella

Si affidano all’evento BeforeDoubleClick del modulo Foglio1. Prima del codice poco più avanti riportato vanno fatte alcune premesse:

  • la cella a partire dalla quale si vuol inserire la tabella prelevata da ciascun .doc dell’elenco in colonna A va denominata “IniTab”; potrebbe essere la cella D7 ma l’importante è che sia circondata da tutte celle vuote almeno all’apertura del modello Excel;
  • La tabella è la prima dell’insieme Tables del documento, ovvero MioDoc.Tables(1);
  • È prevista una forma (Shape) di tipo rettangolo, di nome, diciamo, “RettangoloTab” incollata suppergiù sopra la cella “IniTab”, nella quale si possano inserire testi indicativi del tipo “Tabella di Terzo documento.doc” mediante la proprietà
    Me.Shapes
    (“RettangoloTab”).TextFrame.Characters.Text

Ma ecco la routine base di inserimento tabella dotata di argomenti Docum (il documento) e Cart (la cartella ove giace: una prece +), con la sua brava Sub di prova:

Sub InserisciTabella(Docum As String, Cart As String)

  Dim MioDoc As Document, Tabella As Table

  Dim Nrig As Integer, Ncol As Integer

  Dim i As Integer, j As Integer

  On Error GoTo Fine

  Set MioDoc = GetObject(Cart & Docum)

  If MioDoc.Tables.Count = 0 Then

    MsgBox "TABELLA ASSENTE!"

    Me.Shapes("RettangoloTab").TextFrame.Characters.Text = ""

  Else

    Set Tabella = MioDoc.Tables(1)

    With Tabella.Range

      Nrig = .Rows.Count

      Ncol = .Columns.Count

      .Copy

    End With

    Me.Shapes("RettangoloTab").TextFrame.Characters.Text = _

    "Tabella di " & Docum

    Dim ValCella As String

    With Range("IniTab")

      .CurrentRegion.ClearContents

      '.PasteSpecial ' Creerebbe una tabella EMBEDDED del documento

      For i = 1 To Ncol

        For j = 1 To Nrig

          ValCella = Tabella.Columns(i).Cells(j).Range

          ValCella = Left(ValCella, Len(ValCella) - 1)

          .Cells(j, i) = ValCella

        Next

      Next

    End With

  End If

Fine:

  MioDoc.Parent.Application.Quit 'MioDoc.Close dava ERRORI!

  Set MioDoc = Nothing

End Sub

 

Sub ProvaInserisciTabella()

  Dim QuestaDir As String

  QuestaDir = ThisWorkbook.Path & "\"

  InserisciTabella "Primo documento.doc", QuestaDir

End Sub

 

I commenti? Lasciati quasi interamente alla pazienza di chi legge, al quale ricordo solo che Range(“IniTab”).CurrentRegion individua tutte le celle “attorno” alla “IniTab” – corrisponde alla manovra manuale Ctrl+* (asterisco), di qui l’importanza dell’ultima osservazione al riguardo – permettendo di cancellare l’eventuale tabella precedente con ClearContents. Viene lasciato al comprendonio di Chicche and Sia pure il doppio ciclo iterativo di indici i e j che spazzola le colonne della tabella e le celle di ciascuna riversandone via via i contenuti nelle celle del foglio di lavoro che “partono” da Range(“IniTab”) con Range(“IniTab”).Cells(i, j).

Nota La Left(ValCella, Len(ValCell) – 1 si rende necessaria per eliminare il carattere speciale all’estrema destra del contenuto effettivo di una cella di tabella Word.

 

Concludo riportando senza particolari osservazioni la routine dell’evento che si scatena quando si dà un doppio clic su una cella Target, salvo ricordare che Cancel = True evita che il cursore resti in tale cella:

Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)

  Dim QuestaDir As String

  QuestaDir = ThisWorkbook.Path & "\"

  Cancel = True

  If Target = "" Or Target.Address = "$A$1" Then Exit Sub

  If Target.Column <> 1 Then Exit Sub

  InserisciTabella Target.Text, QuestaDir

End Sub

 

?>

posted on lunedì 14 dicembre 2009 18.24