Sottotitolo: Tutto quello che Vi serve sapere per creare un bel documento Microsoft Word da codice C#.
L'esigenza è semplice: ottenere dai dati gestiti dal nostro fantastico software un documento Microsoft Word che riporti un'analisi degli stessi.
Manco a dirlo il documento deve avere delle tabelle, con i dati presentati in modo sensato e graficamente convincente.
L'attività di per se è abbastanza semplice, ma ritengo non sia altrettanto semplice trovare tutte le informazioni necessarie.
Per esempio come mettere una tabella nell'header del documento in modo tale che lo stesso sia riportato in ogni pagina ?? Come annegare dentro questa un'immagine ?? Come porre dei testi in verticale ? Semplici domande e le cui risposte non sono facilmente rintracciabili.
Ci ho messo un pò a reperire tutte le dritte necessarie, per cui ho pensato di raggrupparle tutte insieme per aiutare altri che possano avere un'esigenza simile.
Per iniziare occorre dire che tutte le funzionalità qui presentate usano esclusivamente le API messe a disposizione da Microsof Office: è banale sottolinearlo, ma occorre che sia sulla macchina di sviluppo su cui andremo a implementare il codice, che sulla macchina destinataria dell'utilizzatore, il pacchetto office deve essere correttamente installato e funzionante.
Con il rischio di offenderVi ulteriormene devo anche dire che occorre anche aggiungere al progetto il riferimento a Microsft Office Object Library. Fatto questo avremo la possibilità di accedere a tutte le funzioni viste nel seguito.
Altrimenti..... No office... No party.....
Come sapere se Word è installato ??
Ricordo che per ora non è obbligatorio avere installato il pacchetto Office su qualsiasi macchina del mondo: nel futuro forse le cose varieranno, ma posso giurare che esistono pc sulle quali si usa in modo efficace solo LibreOffice o similari.
Per tale motivo occorre verificare come step iniziale che Office Word sia installato, e solo allora è possibile proseguire con le altre funzionaltià qui presentate altrimenti possono occorrere strani errori sulla macchina dell'utilizzatore finale.
public static bool WordInstallato() { Type officeType = Type.GetTypeFromProgID("Word.Application"); if (officeType == null) { return false; // NO Party !! Word non c'è } else { return true; // Word è installato: possiamo proseguire ! } }
Aprire un documento word esistente o crearne uno nuovo
Per creare un nuovo documento word.
var applicationWord = new Microsoft.Office.Interop.Word.Application();
applicationWord.Visible = false;
Document Worddocument2 = applicationWord.Documents.Add();
Worddocument2.Activate();
Per aprirne uno esistente.
var applicationWord = new Microsoft.Office.Interop.Word.Application();
applicationWord.Visible = false;
Document Worddocument = applicationWord.Documents.Open(m_pathexe + @"\tpl.doc");
Worddocument.Activate();
Qui abbiamo messo applicationWord.Visible = false il che significa che il documento word creato/aperto non è visualizzato: optando per true allora sarà immediatamento visualizzato.
Nel mio caso ho preferito optare per creare il documento in modo "nascosto" eppoi al termine delle elaborazioni l'ho visualizzato ponendo direttamente successivamente al termine paramentro al valore true.
E' anche possibile eseguire il tutto in modo nascosto, e poi salvare il documento usando il metodo Worddocument.Save.
Creare un heading con una tabella e un'immagine.
private void AggiungiHeader(string m_pathimg,Document _Worddocument) { object oMissing = Type.Missing; Range oRange = _Worddocument.Sections[1].Headers[WdHeaderFooterIndex.wdHeaderFooterPrimary].Range; var tblHeader = _Worddocument.Tables.Add(oRange, 6, 4, ref oMissing, ref oMissing); tblHeader.Rows[1].Cells[1].Range.InlineShapes.AddPicture(m_pathimg + @"\logo.jpg"); tblHeader.Rows[1].Cells[2].Range.Text = "Valutazione"; tblHeader.Rows[1].Cells[2].Range.Bold = 10; tblHeader.Rows[1].Cells[2].Range.Font.Size = 18; tblHeader.Rows[1].Cells[4].Range.Text = "MOD0XX"; tblHeader.Rows[1].Cells[4].Range.Bold = 10; tblHeader.Rows[1].Cells[4].Range.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphCenter; tblHeader.Rows[2].Cells[4].Range.Text = "Rev. 0"; tblHeader.Rows[2].Cells[4].Range.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphCenter; tblHeader.Cell(1, 1).Merge(tblHeader.Cell(2, 1)); tblHeader.Cell(1, 2).Merge(tblHeader.Cell(2, 3)); tblHeader.Cell(3, 1).Merge(tblHeader.Cell(3, 4)); tblHeader.Cell(1, 2).Range.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphCenter; tblHeader.Cell(4, 1).Range.Text = "Cognome: "; tblHeader.Cell(4, 1).Merge(tblHeader.Cell(4, 2)); tblHeader.Cell(5, 1).Range.Text = "Nato a: "; tblHeader.Cell(5, 1).Merge(tblHeader.Cell(5, 2)); tblHeader.Cell(6, 1).Range.Text = "Ruolo: "; tblHeader.Cell(6, 1).Merge(tblHeader.Cell(6, 2)); tblHeader.Cell(4, 2).Range.Text = "Nome: "; tblHeader.Cell(4, 2).Merge(tblHeader.Cell(4, 3)); tblHeader.Cell(5, 2).Range.Text = "Data di nascita: "; tblHeader.Cell(5, 2).Merge(tblHeader.Cell(5, 3)); tblHeader.Cell(6, 2).Range.Text = "Data: "; tblHeader.Cell(6, 2).Merge(tblHeader.Cell(6, 3)); tblHeader.Cell(1, 1).Borders.OutsideLineStyle = Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleSingle; tblHeader.Cell(1, 2).Borders.OutsideLineStyle = Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleSingle; tblHeader.Cell(1, 3).Borders.OutsideLineStyle = Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleSingle; tblHeader.Cell(2, 3).Borders.OutsideLineStyle = Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleSingle; tblHeader.Cell(4, 1).Borders.OutsideLineStyle = Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleSingle; tblHeader.Cell(5, 1).Borders.OutsideLineStyle = Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleSingle; tblHeader.Cell(6, 1).Borders.OutsideLineStyle = Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleSingle; tblHeader.Cell(4, 2).Borders.OutsideLineStyle = Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleSingle; tblHeader.Cell(5, 2).Borders.OutsideLineStyle = Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleSingle; tblHeader.Cell(6, 2).Borders.OutsideLineStyle = Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleSingle; }
Qui il risultato è nell'immagine, e sarà riportato su tutte le eventuali pagine facenti parte del documento word che stiamo costruendo.
Nella pratica si è creata una tabella di 6 righe e 4 colonne.
Nella riga 1 colonna 1 ho messo un'immagine usando tblHeader.Rows[1].Cells[1].Range.InlineShapes.AddPicture(m_pathimg + @"\logo.jpg").
La riga 1 colonna 2 e 3 sono state usate per mettere il titolo, il cui relativo testo è in bold e con dimensione 18.
La riga 3 serve come separatore, e per non evidenziarla ho posto i bordi dell'immagine ovunque meno che in questa cella.
La cosa interessante è il metodo da utilizzare per eseguire il merge delle celle.
tblHeader.Cell(<num riga inizio>, <Num Colonna Inizio).Merge(tblHeader.Cell(<num riga fine>, <num colonna fine>)).
Sono certo che per ottenere il risultato in figura esistono anche altri metodi, certamente più eleganti ed efficaci, ma come ho già avuto modo di dire che quanto esposto ha il difetto di essere brutto ma funzionante.....
Tabella
Qui il risultato ricercato è quello nella figura.
Nulla di eccezionale, ma la cosa un pò rognosa è mettere i campi in verticale.
Per il resto tutto è simile a quanto visto per la tabella che abbiamo messo nell'header del documento.
Per porre una tabella nel mio caso ho identificato la posizione della fine del documento (Microsoft.Office.Interop.Word.Range table1Location = Worddocument.Bookmarks.get_Item(ref oEndOfDoc).Range) ove posizionare la tabella.
Così trovata la posizione basta usare Worddocument.Tables.Add(table1Location, totrighetabella, totcolonnetabella), che quindi accetta come argomento la posizione trovata prima e, come intuibile, il numero di righe e quindi delle colonne.
object oEndOfDoc = "\\endofdoc"; Microsoft.Office.Interop.Word.Range table1Location = Worddocument.Bookmarks.get_Item(ref oEndOfDoc).Range; var tabella1 = Worddocument.Tables.Add(table1Location, totrighetabella, 8); tabella1.Cell(1, 1).Merge(tabella1.Cell(1, 2)); tabella1.Cell(1, 2).Range.Text = "Produzione"; tabella1.Cell(1, 2).Range.Bold = 10; tabella1.Cell(1, 2).Range.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphCenter; tabella1.Cell(1, 3).Range.Text = "Risultato"; tabella1.Cell(1, 3).Merge(tabella1.Cell(1, 7)); tabella1.Cell(1, 3).Range.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphCenter; tabella1.Cell(1, 3).Range.Bold = 10; tabella1.Cell(2, 1).Range.Text = "Fase"; tabella1.Cell(2, 1).Merge(tabella1.Cell(2, 2)); tabella1.Cell(2, 1).Range.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphCenter; tabella1.Cell(2, 1).Range.Bold = 10; tabella1.Cell(2, 2).Range.Text = "Step"; tabella1.Cell(2, 2).Range.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphCenter; tabella1.Cell(2, 2).Range.Font.Bold = 10; tabella1.Cell(2, 3).Range.Text = "Nulle"; tabella1.Cell(2, 3).Range.Orientation = WdTextOrientation.wdTextOrientationVertical; tabella1.Cell(2, 3).Range.Bold = 10;
tabella1.Cell(2, 4).Range.Text = "Scarse"; tabella1.Cell(2, 4).Range.Orientation = WdTextOrientation.wdTextOrientationVertical; tabella1.Cell(2, 4).Range.Bold = 10;
tabella1.Cell(2,5).Range.Text = "Sufficienti"; tabella1.Cell(2, 5).Range.Orientation = WdTextOrientation.wdTextOrientationVertical; tabella1.Cell(2, 5).Range.Bold = 10;
tabella1.Cell(2, 6).Range.Text = "Discrete"; tabella1.Cell(2, 6).Range.Orientation = WdTextOrientation.wdTextOrientationVertical; tabella1.Cell(2, 6).Range.Bold = 10; tabella1.Cell(2, 7).Range.Text = "Buone"; tabella1.Cell(2, 7).Range.Orientation = WdTextOrientation.wdTextOrientationVertical; tabella1.Cell(2, 7).Range.Bold = 10;
L'estratto del codice sopra evidenzia che per per mettere un campo in veritcale occorre agire su Range.Orientation, assegnado l'enumerativo WdTextOrientation.wdTextOrientationVertical. Più facile a farsi che a spiegarsi.
Una volta compilata la tabella nel modo voluto per mettere i bordi, e adattare in modo automatico la tabella alle dimensioni dei testi contenuti è possibile agire con lo snippet di seguito.
tabella1.Borders.InsideLineStyle = Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleSingle; tabella1.Borders.OutsideLineStyle = Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleSingle; tabella1.AutoFitBehavior(WdAutoFitBehavior.wdAutoFitContent);
Per aggiungere una nuova tabella di seguito ??
Se si volesse porre una tabella a seguire di quella precedente usare il seguente.
Worddocument.Content.InsertAfter(Environment.NewLine); Microsoft.Office.Interop.Word.Range table2Location = Worddocument.Bookmarks.get_Item(ref oEndOfDoc).Range; var tabella2 = Worddocument.Tables.Add(table2Location, totrighetabella, totcolonnetabella);
Ho aggiunto un riga vuota (NewLine) per avere uno spazio, e quindi ho identificato la fine documento e lì posta la nuova tabella, usando la stessa tecnica vista in precedenza.
Sostituire dei valori in un testo word
Si immagini che il documento word che si sta trattando contiene dei campi che si vogliono sostuire con un testo calcolato da codice.
So perfettamente che in questo caso è possibile agire con stampa-unione, ma nel mio casospecifico ciò non era possibile.
Per ottenere questo effetto basta utilizzare il codice nel seguito.
Worddocument.Content.Find.Execute("TESTODASOSTITUIRE", false, true, false, false, false, true, 1, false, "NUOVOTESTO", 2, false, false, false, false);
Usando quanto sopra si ricercherà nel documento ogni occorrenza di TESTODASOSTITUIRE, e lo stesso sarà sostituito da NUOVOTESTO mantendo le eventuali formattazioni applicate al testo originale.
Perfomance
E' meglio dirlo chiamente: è lento. Se dovete costruire una tabella complessa su più pagine potete aspettare anche minuti !
Per incrementare le perfomance è possibile agire su Visibile: ponendo all'inizio False e solo alla fine dell'elaborazione riporre il valore in True, visualizzando così il documento generato, oppure salvare quanto generato direttamente in una cartella, si ottengono modesti incrementi di velocità.
Per spremere al massimo è anche possibile anche disabilitare il controllo sintattico in fasi di creazione del documento nonchè le animazioni.
applicationWord.ShowAnimation = false; applicationWord.CheckLanguage = false; applicationWord.Options.CheckSpellingAsYouType = false; applicationWord.AutoCorrect.CorrectCapsLock = false; applicationWord.AutoCorrect.CorrectDays = false; applicationWord.AutoCorrect.CorrectHangulAndAlphabet = false; applicationWord.AutoCorrect.CorrectInitialCaps = false; applicationWord.AutoCorrect.CorrectKeyboardSetting = false; applicationWord.AutoCorrect.CorrectSentenceCaps = false; Worddocument.SpellingChecked = false;
Spero che quanto sopra Vi possa essere utile !