DOCX-Vorlagen mit dynamischen Daten bestücken
avatar

In einer Applikation, die ich derzeit konzipiere, ist eine Kernanforderung Daten aus einer Datenbank-Tabelle in einem Word 2007- Dokument zu präsentieren. Diese Anforderung ist mir auch schon des Öfteren in anderen Projekten begegnet. Bei meinem früheren Lösungsansatz habe ich immer Platzhalter (bspw. $PLZ$) im DOCX platziert und per String.Replace(…) (über die OpenXML-API) zur Laufzeit ersetzt. Dies funktioniert auch super, ist aber leider etwas fehleranfällig, da bei Anpassungen der Vorlage man sich nie sicher sein kann, dass der Platzhalter intern immer noch als ein Wort vorliegt. Daher hier noch mal ein neuer Ansatz, der etwas fehlerresistenter funktioniert. Der neue Ansatz funktioniert über Inhaltsteuerelemente, die an eine im DOCX integrierte XML-Struktur gebunden werden. Zur Laufzeit wird nun das XML-Gerüst mit neuen Daten bestückt. Fertig!!! Für das Binden der Inhaltssteuerelemente kann man wunderbar das Tool “Word Content Control Toolkit” benutzen, das unter http://dbe.codeplex.com/ frei heruntergeladen werden kann.

Die Vorteile von MS Word gegenüber einem reinen Bericht (bspw. in SSRS) sind offensichtlich. Sowohl die Vorlage als auch die generierten Dokumente können von Fachanwendern nachträglich angepasst und erweitert werden.Die Anforderung, aus einer .Net-Anwendung heraus ein angereichertes DOCX zu öffnen, wird von MS Word nicht standardmäßig unterstützt.

Oft werden die Word Vorlagen in einem speziellen Verzeichnis zentral auf dem Server abgelegt, wo sie angepasst werden können. Generell und vor allem in einer Web-Anwendung ist dieser Ansatz allerdings nicht sehr charmant. Daher bestand ein Ziel meines POC‘s darin, die Vorlagen ebenfalls in der Datenbank zu halten.

Nachfolgend habe ich die einzelnen Schritte für meinen Proof Of Concept mal konsolidiert beschrieben. Viel Spaß beim Lesen.

 

Erstellen der DOCX-Vorlage durch einen Fachanwender

Um das Dokument, welches als Vorlage dienen soll, initial zu erstellen, öffnet der Fach-Anwender MS Word ab der Version 2007 und erstellt das Dokument ohne besondere Rücksichtnahme auf den späteren Mechanismus.

Die Felder, die später durch Werte aus der Datenbank ersetzt werden sollen, werden lediglich in spitze Klammern gesetzt, um sie kenntlich zu machen.

image

 

Aufbereiten der Vorlage durch einen Administrator

Der Administrator öffnet die Vorlage in MS Word und kapselt die gekennzeichneten Felder in einem „Nur-Text-Inhaltssteuerelement“.

clip_image002

Danach sieht das Feld wie folgt aus.

clip_image003

Anschließend speichert und schließt der Administrator das Dokument und öffnet es mit der Anwendung „Word 2007 Content Control Toolkit“ (http://dbe.codeplex.com ), die auf seinem Rechner installiert werden muss.

clip_image005

Wenn das Dokument in dem Tool geöffnet wird, erkannt die Anwendung die Inhaltssteuerelemente im Inhalt. Zudem kann der Admin eine XML Struktur definieren und die XML-Felder mit den Inhaltssteuerelementen verbinden.

clip_image007

Ist noch kein XML-Gerüst vorhanden, kann der Anwender eins erstellen. Hierbei müssen die XML-Knoten wie die Felder in der Datenbank heißen, damit das Bestücken via Programmlogik erleichtert wird.

clip_image009

Per Drag&Drop können nun die XML-Felder mit den Inhaltssteuerelementen verknüpft werden.

Der Admin kann nun das Dokument als Vorlage in der Datenbank ablegen. Solange die Verknüpfung nicht geändert werden muss, können nun auch die Fachanwender den Text in die Vorlage anpassen.

Das Bereitstellen/Hochladen der Vorlage geschieht innerhalb der zu realisierende Verfahrens-Anwendung im Power User-Bereich.

clip_image011

(Natürlich ist es in der PoC-Anwendung auch möglich, die bereits hochgeladenen Vorlagen wieder herunterzuladen, anzupassen und erneut zu veröffentlichen. )

 

Die Datenbasis

Für diesen kleinen PoC sind eigentlich nur zwei Tabellen in einer Datenbank erforderlich. Eine für die Vorlagen und die andere für die Datensätze, die in einer der Vorlagen präsentiert werden sollen.

Tabelle für die Vorlagen:

In der Tabelle werden neben der ID nur der Name der Vorlage und ein BLOB-Feld für das DOCX benötigt.

clip_image013

Tabelle für die zu präsentierenden Daten:

In der anderen Datenbank-Tabelle, in der die Daten für die Anzeige im DOCX stehen, habe ich die ID der Word-Vorlage hinterlegt. Diese Verknüpfung kann die Programmlogik natürlich später auch über andere Wege realisieren.  Ansonsten sind hier nur die Felder, die mit den Inhaltssteuerelementen bzw. mit der gebundenen XML-Struktur übereinstimmen, erforderlich.

clip_image015

Der Code, der Daten und Vorlage zusammenbringt

Es gibt natürlich noch diversen Code drum rum, aber nachfolgend ist das Herzstück mal herauskristallisiert.  

In der PoC – MVC Webanwendung habe ich in einem Controller die Aktion „Open“ implementiert, die die ID einer Person entgegennimmt. Die Daten der Person werden aus der Datenbank geladen. Anschließend wird die zugewiesen Vorlage über die ID (in den Person-Daten) als Stream bereitgestellt. Aus dem Stream wiederum wird via OpenXML-API das Worddokument geladen und das XML-Gerüst darin identifiziert. Das XML wird dann in ein bearbeitungsfähiges XmlDocument geladen und dort entsprechend angepasst.

Abschließend wird das modifizierte Dokument dem Anwender im Browser zugänglich gemacht.

Function Open(id As Integer) As ActionResult

        ' get person from database
        Dim person As T_Person
        Using context As New WinbusyPoCEntities()
            person = context.T_Person.Where(Function(r) r.ID = id).Single()
        End Using

        ' get docx template as stream
        Dim vorlage As T_Vorlage = DocumentHelper.GetFromEntity(person.Vorlage_ID)
        Dim stream As MemoryStream = New MemoryStream()
        stream.Write(vorlage.Inhalt, 0, vorlage.Inhalt.Length)

        'process the docx  by changing the xml-data source
        Using docx As DocumentFormat.OpenXml.Packaging.WordprocessingDocument = _
            DocumentFormat.OpenXml.Packaging.WordprocessingDocument.Open(stream, True)
            Dim xmlPart As CustomXmlPart = docx.MainDocumentPart.GetPartsOfType(Of CustomXmlPart)()(0)

            Dim contentDoc As XmlDocument = New XmlDocument()
            contentDoc.Load(xmlPart.GetStream())
            contentDoc.DocumentElement.SelectSingleNode(".//" + "Vorname").InnerXml = person.Vorname
            contentDoc.DocumentElement.SelectSingleNode(".//" + "Nachname").InnerXml = person.Nachname
            contentDoc.DocumentElement.SelectSingleNode(".//" + "Alter").InnerXml = person.Alter

            xmlPart.FeedData(ToMemoryStream(contentDoc.DocumentElement.OuterXml))
        End Using

        ' send to user
        Response.Clear()
        Response.ContentType = "application/msword"
        Response.AppendHeader("Content-Disposition", "attachment;filename=" + vorlage.Name)
        stream.Seek(0, SeekOrigin.Begin)
        Response.BinaryWrite(ReadFully(stream))
        Response.End()
        Response.Flush()
        ' don't really required
        Return RedirectToAction("Index")
End Function

 

Fazit

Platzhalter im Word Dokument zur Laufzeit ersetzten war gestern! Heute wird die XML-Datenquelle zur Laufzeit ausgetauscht, was ein Stück fehlerresistenter ist, wenn ein reiner Fachanwender die Vorlage bearbeiten soll.

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *


× 2 = sechs

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>