Questo articolo è la conclusione del precedente, di cui al link seguente:
http://blog.shareoffice.it/giannigiaccaglini/articles/10068.aspx
Tabella Word 2007 in Excel. Soluzione conclusiva con LINQ + SDK 1.0
Con questa soluzione si adotta la libreria OpenXML SDK ver. 1.0, che consente di aprire una qualsiasi parte di un file Open Office senza scompattare tale file. Ci rivolgiamo a esperti, pertanto non forniremo commenti particolari, ma solo il codice.
Importante. Tenendo anche conto di precedenti osservazioni (v. anche quanto ripreso dal sito degli openXMLdeveloper, in fondo a questo intervento), occorre inserire nelle Dichiarazioni in testa al modulo Foglio1 anche le direttive Imports relative alla corretta libreria SDK 1.0 (solo DocumentFormat.OpenXml.Packaging e non come in precedenza Microsoft.Office.DocumentFormat.OpenXml), nonché alla System.IO [anche qui: NON la vetusta System.IO.Packaging.ZipPackage – rigettata nella parte Packaging.ZipPackage - ma solo System.IO).
In definitiva otteniamo:
Imports System.IO
Imports Packaging = DocumentFormat.OpenXml.Packaging
Imports System.Xml.Linq
Public Class Foglio1
'. . . omissis . . .
Private Function FormatoInglese(ByVal strDatoIngl As String) As String
If IsNumeric(strDatoIngl) Then
strDatoIngl = Replace(strDatoIngl, ".", "*")
strDatoIngl = Replace(strDatoIngl, ",", ".")
strDatoIngl = Replace(strDatoIngl, "*", ",")
End If
Return strDatoIngl
End Function
Private Sub Button4_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button4.Click
Dim MiaDir As String = _
"C:\LINQ\Tabella\"
Dim Ns_w As XNamespace = _
"http://schemas.openxmlformats.org/wordprocessingml/2006/main"
Dim CellaAttiva As Excel.Range = Application.ActiveCell
CellaAttiva.CurrentRegion.ClearContents()
Using PackageWord As Packaging.WordprocessingDocument = _
Packaging.WordprocessingDocument.Open(MiaDir & "Tabella.docx", True)
Dim DocMainPart = PackageWord.MainDocumentPart
Dim StrRead As StreamReader = New StreamReader(DocMainPart.GetStream())
Dim MainDoc As XDocument = XDocument.Load(StrRead)
Dim Tabella As XElement = _
MainDoc.Descendants(Ns_w + "tbl").First() 'NON Da'ERRORE a run-time!
Dim Righe = Tabella.Elements(Ns_w + "tr")
Dim Riga As XElement
Dim i As Integer = 1, j = 1
For Each Riga In Righe 'Tabella.Elements(Ns_w + "tr")
For Each CellaTab In Riga.Elements(Ns_w + "tc")
CellaAttiva.Cells(i, j) = FormatoInglese(CellaTab.Value)
j += 1
Next CellaTab
i += 1
j = 1
Next Riga
End Using
End Sub
End Class
Note riprese dal sito degli sviluppatori Open Office
The Open XML Format SDK 1.0 contains the changes described below:
1. Renames the Microsoft.Office.DocumentFormat.OpenXml dll to DocumentFormat.OpenXml
2. Renames the Microsoft.Office.DocumentFormat.OpenXml.Packaging namespace to DocumentFormat.OpenXml.Packaging
3. Renames the Microsoft.Office.DocumentFormat.OpenXml namespace to DocumentFormat.OpenXml
4. Adds support for validating by using the XmlSchemaSet object
If you have written code using previous CTP releases, keep in mind that the first three are breaking changes. You need to delete the current reference to the Microsoft.Office.DocumentFormat.OpenXml.dll and replace it with DocumentFormat.OpenXml.dll. Also, you need to change the namespace references to DocumentFormat.OpenXml.Packaging and DocumentFormat.OpenXml.
The last change has to do with a common ask from developers. Previous releases of the CTP allowed you to validate the contents of a part in an Open XML Package against a single schema file. However, it was not possible to validate against a collection of schemas. The Open XML Format SDK 1.0 provides an overloaded method of the ValidateXml method that allows you to validate a document part against a specific XmlSchemaSet object. The following sample code shows you how to validate the XML content of the MainDocumentPart part of a WordprocessingDocument package by calling the ValidateXml method of MainDocumentPart part. You pass a list of schemas to the ValidateXml method as an input parameter.
' How to validate the contents of a document part against
' a collection of schemas.
Public Shared Sub ValidDocumentContent(document As String,
schemaList As List (Of String))
Dim schemas As New XmlSchemaSet()
For Each schemaUri As String In schemaList
schemas.Add(Nothing, schemaUri)
Next schemaUri
Using wordDoc As WordprocessingDocument =
WordprocessingDocument.Open(document, False)
wordDoc.MainDocumentPart.ValidateXml(schemas, Nothing)
End Using
End Sub
Come usare i tag "angolari" (segreto svelato da P. Pialorsi)
Confesso che a lungo ho nutrito un dubbio atroce, ossia il timore che il dannato prefisso w non fosse lecito con i tag posti tra parentesi angolari tipici di VB 9. Infatti l'istruzione seguente, valida con Descendants, Elements ecc.
Dim Ns_w As XNamespace = _
http://schemas.openxmlformats.org/wordprocessingml/2006/main
NON dava esiti positivi con la seconda sintassi, ovvero né con cose tipo né tampoc0o con la fantasiosa .
Per fortuna la SCIENZA di Paolo Pialorsi mi è infine venuta incontro. Ecco il segreto da lui svelato, che riporto pari pari:
Ecco come fare:
Imports <xmlns:x="http://schemas.demo.it/Customers">
Module Module1
Sub Main()
Dim customers = _
xml version="1.0"?>
<x:customers>
<x:customer id="1" name="Paolo" country="IT"/>
<x:customer id="2" name="Marco" country="IT"/
<x:customer id="3" name="Frank" country="EN"/>
<x:customer id="4" name="James" country="EN"/>
< SPAN>x:customers>
Dim italianCustomers = _
From c In customers.<x:customers>.<x:customer> _
Where c.@country = "IT" _
Select c.@name
For Each c In italianCustomers
Console.WriteLine(c)
Next
Trasmetto questa preziosa, quanto poco nota info all'inclita e al volgo, unitamente una buona notizia a caldo:
la clausola in questione applicata nelle Dichiarazioni della precedente Sub per VSTO, ossia:
Imports <xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
fa sì che ENTRAMBE le istruzioni seguenti vengono ora accettate dal compilatore
Dim Tabella As XElement = _
Doc.Descendants(Ns_w + "tbl").First()
Dim TabSintetica As XElement = Doc...<w:tbl>.First ()
All'inclita, ossia al più esperto affido infine un non arduo esercizio: MODIFICARE IN SENSO, diciamo così, ANGOLARE LA ROUTINE.
Questo prima di esaminare la mia variante finale. Finale per davvero.
Variante “due piccioni”: assi angolari e indici
Il seguente listato è offerto in pasto agli abilissimi, che pertanto sono invitati al confronto fai-da-te con la routine precedente. Sappiano solo che oltre a sfruttare la nozione di Paolo Pialorsi testé discussa quest’ultima variante spazzola altresì gli XElement descritti con la sintassi esclusiva di VB 9 tramite indici i e j. La qual cosa è, per un verso, un poco laboriosa per quel che riguarda gli XElement, ma per contro appare più “naturale” per l’intervallo Excel.
Nota – Un problema resta forse aperto. Percorrere un insieme di elementi IEnumerable tramite indici (cosa poco o per niente trattata nei testi e nei blog specifici) non sarà mica più lenta rispetto all’uso di cicli For Each... Next? Chi ha tempo e voglia faccia esperimenti, ovviamente mediante tabelle molto più grandi della nostra...
Ma ecco il sudato listato:
Private Sub Button5_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button5.Click
Dim MiaDir As String = _
"C:\LINQ\Tabella\"
'L’istruzione seguente qui NON è sfruttata ma è lasciata per prudenza: potrebbe
'servire altrove, ad es. per la proprietà Element (elem. Singolo) NON gestito con <>
Dim Ns_w As XNamespace = _
"http://schemas.openxmlformats.org/wordprocessingml/2006/main"
Dim CellaAttiva As Excel.Range = Application.ActiveCell
CellaAttiva.CurrentRegion.ClearContents()
Using PackageWord As Packaging.WordprocessingDocument = _
Packaging.WordprocessingDocument.Open(MiaDir & "Tabella.docx", True)
Dim DocMainPart = PackageWord.MainDocumentPart
Dim StrRead As StreamReader = New StreamReader(DocMainPart.GetStream())
Dim MainDoc As XDocument = XDocument.Load(StrRead)
Dim Tabella As XElement = MainDoc...<w:tbl>.First()
Dim Righe = Tabella.<w:tr>
MsgBox("Righe(0) =" & Righe(0).ToString) 'Dà l'intero sottoalbero
MsgBox("Elemento Righe(0) =" & Righe(0).Value) 'Dà "abba Rabà ciccì"
Dim i As Integer = 0, j = 0
For i = 0 To Righe.Count - 1
For j = 0 To Righe(i).<w:tc>.Count - 1
CellaAttiva.Cells(i - 1, j - 1) = _
FormatoInglese(Righe(i).<w:tc>(j).Value)
Next j
Next i
End Using
End Sub
Un’ultima annotazione, relative alle due istruzioni Msgbox, servite per debug e da eliminare. Hanno valenza didattica. La prima mostra un sotto-guazzabuglio relativo all’intero subire del nodo Righe(0). La seconda segnala “abbaRabàciccì”, il concatenamento di tutti i Value della prima riga.
?>
?>