Alessandro Del Sole's Blog

{ A programming space about Microsoft® .NET® }
posts - 1908, comments - 2047, trackbacks - 352

My Links

News

Your host

This is me! Questo spazio è dedicato a Microsoft® .NET®, di cui sono molto appassionato :-)

Cookie e Privacy

Disabilita cookie ShinyStat

Microsoft MVP

My MVP Profile

Microsoft Certified Professional

Microsoft Specialist

Xamarin Certified Mobile Developer

Il mio libro su VB 2015!

Pre-ordina il mio libro su VB 2015 Pre-ordina il mio libro "Visual Basic 2015 Unleashed". Clicca sulla copertina per informazioni!

Il mio libro su WPF 4.5.1!

Clicca sulla copertina per informazioni! E' uscito il mio libro "Programmare con WPF 4.5.1". Clicca sulla copertina per informazioni!

These postings are provided 'AS IS' for entertainment purposes only with absolutely no warranty expressed or implied and confer no rights.
If you're not an Italian user, please visit my English blog

Le vostre visite

I'm a VB!

Guarda la mia intervista a Seattle

Follow me on Twitter!

Altri spazi

GitHub
I miei progetti open-source su GitHub

Article Categories

Archives

Post Categories

Image Galleries

Privacy Policy

VSTO: Interrogare le celle di una cartella di Microsoft Excel con LINQ

Come ormai sapete, LINQ è una tecnologia che permette di interrogare innumerevoli tipologie di dati ed è utilizzabile in tantissimi scenari della programmazione .NET, che vanno dalle applicazioni Windows a quelle Web, passando per i Visual Studio Tools for Office.

 

Di quest’ultimo settore abbiamo parlato in diverse occasioni, vedendo come utilizzare LINQ nei confronti di documenti di Microsoft Word 2007 in Visual Basic 2008, sfruttando LINQ to Objects.

Si pone, però, un notevole problema nel momento in cui decidiamo di utilizzare LINQ to Objects nelle soluzioni basate su cartelle di lavoro di Microsoft Excel 2007, in particolare se volessimo eseguire delle query su insiemi di celle, rappresentati da oggetti Cells.

 

Solitamente, nella programmazione .NET rivolta ai documenti di Excel, si ricorre ad oggetti Range che rappresentano insiemi di celle o gruppi di insiemi di celle. Teoricamente, quindi, sarebbe sufficiente formulare query LINQ nei confronti di insiemi Range. Questo, purtroppo, non è possibile perchè gli oggetti Range non implementano l’interfaccia IEnumerable, pertanto non basta neanche ricorrere a un cast del tipo tramite CType.

 

Come si può fare? Il procedimento è un tantino più lungo, non difficile ma un po’ più elaborato e in questo post vedremo come fare. Primo step, creare una cartella di lavoro di Microsoft Excel 2007 che contenga un elenco di ipotetici clienti. La figura seguente rappresenta un elenco minimale sul quale potremo lavorare successivamente ed è importante sottolineare come sia necessario selezionare le sole celle costituenti il “range” senza riga di instestazione:

 

 

Una volta selezionato il range, nell’apposita casella (cerchiata in rosso) digitate un identificatore, Customers in questo caso, che rappresenti il gruppo di celle, rappresentative, a loro volta, dell’elenco dei clienti. In questo modo stiamo definendo quello che in .NET chiameremmo un NamedRange. Salvato il documento, apriamo Visual Studio 2008. Va creato un nuovo progetto per Office 2007 in Visual Basic, in particolare una soluzione a livello di documento come mostrato in figura:

 

 

 

Quando la creazione del progetto è terminata, il designer incapsulerà la finestra principale di Excel 2007, la cui figura si omette per praticità. Ora, passiamo al file di code-behind chiamato Sheet1.vb (o Foglio1.vb).

In primo luogo bisogna definire una classe che rappresenti un singolo cliente e una collezione che conterrà l’elenco dei clienti stessi:

 

    Private Clienti As List(Of Customer)

 

    Class Customer

 

        Private _customerID As Integer

        Private _contactName As String

        Private _customerCity As String

 

        Property ContactName() As String

            Get

                Return _contactName

            End Get

            Set(ByVal value As String)

                _contactName = value

            End Set

        End Property

 

        Property CustomerCity() As String

            Get

                Return _customerCity

            End Get

            Set(ByVal value As String)

                _customerCity = value

            End Set

        End Property

 

        Property CustomerID() As Integer

            Get

                Return _customerID

            End Get

            Set(ByVal value As Integer)

                _customerID = value

            End Set

        End Property

    End Class

 

L’operazione successiva è la previsione di un metodo che recuperi dal range di celle, che abbiamo evidenziato in Excel, l’elenco dei clienti e che aggiunga ciascuno di essi alla collezione List(Of Customer). Il codice che segue realizza l’obiettivo (ci sono dei commenti nel codice per facilitare la lettura):

 

    Private Function GetCustomers() As List(Of Customer)

 

        'Istanzia una collezione di oggetti Customer

        Dim result As New List(Of Customer)

 

        'Dichiara un oggetto di tipo Range

        Dim cellRange As Excel.Range

 

        'Dichiara un singolo cliente

        Dim cliente As Customer

 

        'Ottiene il contenuto del range di Celle

        'dimensionando la matrice

        cellRange = Me.Range("Customers").Resize(1, 1)

 

        'Finchè non incontra un valore nullo..

        Do Until IsNothing(cellRange.Value)

            '..istanzia un nuovo oggetto Customer..

            cliente = New Customer With {.CustomerID = CInt(cellRange.Value), _

                                   .ContactName = cellRange.Offset(0, 1).Value.ToString, _

                                   .CustomerCity = cellRange.Offset(0, 2).Value.ToString}

            '..e lo aggiunge alla collezione..

            result.Add(cliente)

            '..spostandosi alla riga successiva

            cellRange = cellRange.Offset(1, 0)

        Loop

        Return result

    End Function

 

Fatto questo, abbiamo a disposizione un oggetto sul quale possiamo eseguire le nostre query LINQ, ossia la List(Of Customer). Il seguente esempio è una banale query LINQ che ottiene l’elenco dei clienti che risiedono in una città il cui nome inizi con la lettera R; il risultato viene poi visualizzato tramite una MessageBox:

 

    Private Sub Sheet1_Startup(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Startup

 

        'Ottiene l'elenco dei clienti memorizzandoli nella collection

        Me.Clienti = GetCustomers()

 

        Dim query = From cliente In Me.Clienti _

                   Where cliente.CustomerCity.StartsWith("R") _

                   Select cliente.ContactName

 

        Dim elenco As String = String.Empty

 

        For Each cliente In query

            elenco = String.Concat(elenco, " ", cliente)

        Next

 

        MessageBox.Show("Solo i clienti " + elenco + " vivono in una città che inizia per R")

    End Sub

 

Il risultato prodotto è il seguente:

 

 

In effetti, se si lavora con i VSTO, tutto questo lavoro è un grosso limite. Non sarebbe male se il product team potesse riprogettare gli oggetti Range e Cells in modo che implementino IEnumerable rendendoli interrogabili con LINQ, come sarebbe davvero naturale.

 

Al momento, però, dobbiamo accontentarci di questo tipo di soluzione, che spero possa essere utile.

 

Alessandro

Print | posted on giovedì 4 dicembre 2008 01:27 | Filed Under [ Visual Basic Visual Studio 2008 Visual Studio Tools for Office LINQ ]

Powered by:
Powered By Subtext Powered By ASP.NET