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

Tabella Word 2007 in Excel. Soluzione conclusiva con LINQ + SDK 1.0

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"/>

     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.

?>

?>

posted on lunedì 16 marzo 2009 11.20