OpenXml è una figata pazzesca :-) potete finalmente produrre/manipolare/leggere programmaticamente documenti di Office, e non solo, senza aver bisogno di Office… il che è decisamente positivo soprattutto se pensiamo al delirio che era far funzionare Office ad esempio da un’applicazione web…
Per cominciare è necessario procurarsi l’SDK (al momento della scrittura in versione 2.0 December 2009 CTP) direttamente dal sito Microsoft, una volta installato se non avete mai visto in vita vostra OpenXml e come me siete allergici al manuale delle istruzioni (le specifiche di OpenXml sono qualche migliaio di pagine… e le ho anche lette in parte…) niente paura! L’SDK contiene il tool piu fantasticissimo del mondo, nella directory “tool” dove avete installato l'SDK: OpenXmlSdkTool.exe.
Avviate il robo e provate a caricare un documento, nello screenshot un file di Excel di esempio, il signorino vi esplode sulla sinistra la struttura del file OpenXml e selezionando un nodo è possibile chiedergli (Reflect Code) di produrci il codice C# necessario per produrre quel nodo!
image
Spettacolo!!! :-)
Con questo tool diventa veramente banale produrre dei documenti che aderiscono alle specfiche OpenXml. Non è altrettanto facile il contrario, cioè leggere un documento perchè è necessario conoscere molto bene il formato e le sue specifiche. Due esempi, giusto per capirci.
Scrittura/Lettura di valori in una cella di Excel
Quando scrivete un valore potete limitarvi a fare:
var cell = new Cell( new CellValue( "sample text" ) ) 
{ 
    DataType = CellValues.String 
};
Questo crea una cella con del testo ed Excel vive felice, se però cercate di leggere una cosa prodotta da Excel, o anche il vostro file dopo averlo modificato con Excel, scoprite che il ragionamento non vale più. Excel, come da specifiche, usa una cosa che si chiama SharedStringTable e linka i valori, che piazza nella tabella condivisa, alle celle. Ergo per leggere un contenuto dovete fare una cosa del tipo:
String GetCellText( Cell cell )
{
    var value = String.Empty;
    if( cell != null && cell.CellValue != null )
    {
        value = cell.CellValue.Text;
        if( cell.DataType == CellValues.SharedString )
        {
            value = this.stringTable.Descendants<SharedStringItem>().ElementAt( Int32.Parse( cell.CellValue.Text ) ).Text.Text;
        }

        value = value.Trim();
    }

    return value;
}
Perchè tutti quei controlli sui null?
La battaglia navale…
Noi siamo abituati a pensare al foglio di calcolo come ad una griglia ma se proviamo a scorrere le celle come se scorressimo gli elementi di una matrice bidimensionale ci troveremmo di fronte a delle interessanti sorprese…
Se:
  • provate ad aprire un nuovo foglio di Excel;
  • vi posizionate alla cella D12;
  • scrivete qualcosa;
quando leggerete da codice quel foglio scoprirete che esiste solo la cella D12 e basta tutto il resto non c’è proprio. Il che in ottica “serializzazione” è decisamente sensato ma in ottica lettura ne dovete tenere conto, ad esempio facendo una cosa del tipo:
const string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
String ParseCellReference( Int32 hCord, Int32 vCord )
{
    var letter = letters[ hCord ].ToString();
    var number = vCord.ToString();

    return String.Concat( letter, number );
}
Che potete usare così:
var reference = this.ParseCellReference( 3, 12 ); //D12
var cell = row.Elements<Cell>()
    .Where( c => c.CellReference == reference )
    .SingleOrDefault();
E’ ovvio che ParseCellReference è un po’ semplicistico perchè non tiene conto di colonne oltre la 25esima cosa fattibilissima in Excel ma è giusto un hint :-)
.m