Az előző részben láttuk, hogyan kapcsolódik a DOCX állományban lévő customXml part kétirányú adatkötéssel a content control vezérlőkhöz. Ha ezzel a módszerrel akarunk programozottan dokumentumot előállítani, akkor már csak egyetlen lépés van hátra: olyan kódot kell írnunk, amely belenyúl az Open XML állományba és felülírja a korábban már odatett XML partot, aminek tartalma azután az adatkötéseken keresztül automatikusan megjelenik a felhasználó számára.
A feladat megoldásában a System.IO.Packaging névtér nagy segítségünkre lesz. A Package osztály segítségével meg tudjuk nyitni a DOCX fájlt ZIP tudomány nélkül, csak az elérési útját kell ismernünk:
// A sablon dokumentum megnyitása.
Package package = Package.Open( path, FileMode.Open, FileAccess.ReadWrite );
A következő lépés az XML part megkeresése. Az egyes part-okra a csomagon belüli URI-jük segítségével hivatkozhatunk. A Word 2007 Content Control Toolkit által generált Item1.xml itt található:
// Az XML part útvonala a sablon dokumentumban. Uri partUri = new Uri( "/customXml/Item1.xml", UriKind.Relative );
Lekérdezhetjük, hogy egyáltalán létezik-e ilyen part a dokumentumban:
// Ellenőrizzük, hogy a sablonban van-e XML part. if( package.PartExists( partUri ) ) { // itt folytatjuk...
Ha létezik, akkor el is kérhetjük, mégpedig egy PackagePart formájában:
// Az XML part elkérése. PackagePart xmlPart = package.GetPart( partUri );
Az XML part tartalmát streamként tudjuk elérni. Ha például egy content nevű változóban megtalálható a beírandó XML UTF-16 formátumban, akkor annak beírására használhatjuk például az alábbi kódot:
// Az XML part tartalmát kezelő stream elkérése. using( Stream xmlStream = xmlPart.GetStream() ) { // A stream hosszának beállítása, hogy rövidebb szöveg esetén a régi tartalom levágódjon. xmlStream.SetLength( content.Length ); // Az új tartalom beírása a streambe. Unicode kell, mert az XML stringben utf-16 szerepel. using( StreamWriter writer = new StreamWriter( xmlStream, Encoding.Unicode ) ) { writer.Write( content ); writer.Flush(); // writer.Close(); automatikus a Dispose miatt. } // Az XML part mentése. // xmlStream.Close(); automatikus a Dispose miatt. }
Nincs is más hátra, mint a dokumentum módosításait elmenteni a diszkre:
// A dokumentum változásainak mentése.
package.Flush();
package.Close();
Miért vacakoltam az UTF-16 kódolással? Azért, mert a beírandó XML előállítására szerintem az a legegyszerűbb megoldás, ha készítünk például egy Contact osztályt, annak beállítjuk úgy a tulajdonságait, ahogy azokat a Word dokumentumban látni szeretnénk, majd egyszerűen XmlSerializer segítségével kisorosítjuk:
XmlSerializer serializer = new XmlSerializer( typeof( Contact ) ); using( StringWriter writer = new StringWriter( CultureInfo.InvariantCulture ) ) { serializer.Serialize( writer, this ); result = writer.ToString(); // writer.Close(); automatikus a Dispose miatt. }
Mivel itt StringWritert használok, az eredmény a .NET Framework sztringjeinek alapértelmezett kódolása, azaz "utf-16" lesz, amivel tapasztalataim szerint a Word nem birkózik meg. Persze biztosan lehet egyszerűbben is…
Egyetlen dologra hívnám fel a figyelmet – azon kívül természetesen, hogy a fenti kód a probléma megoldásának igen egyszerű módja: kódból módosítunk egy Word dokumentumot, de mindezt úgy tesszük, hogy nem használjuk hozzá a Word objektum modelljét. Sőt, nem használjuk a Word egyik komponensét sem, azaz a fenti kód működik akkor is, ha nincs Word a gépen! Ez igen fontos fegyvertény az Open XML mellett, hiszen aki próbált már szerver oldalon Office dokumentumot előállítani az biztosan belefutott abba a problémába, hogy az Office-t nem kiszolgáló oldali automatizálásra tervezték. Ez még a 2007-es verzióra is igaz, de nem az Open XML-re!
Ezt a tudásunkat felhasználva akár a SharePointot is kiegészíthetjük, készíthetünk például egy olyan új funkciót a Névjegyalbum listákba, amely lehetővé teszi a névjegy lista elemek alapján megcímzett, fejléces levelek egy kattintással történő előállítását. Ehhez nem kell mást tennünk, mint…
(folytatjuk)