Jens HäupelSr. Application Development ManagerMicrosoft Deutschland GmbH These postings are provided "AS IS" with no warranties, and confer no rights. Use of included code samples are subject to the terms specified at Microsoft - Information on Terms of Use
Die VSTO Powertools bringen ein Tool Window zum auswählen der ImageMso strings mit. Nur leider gab es seit Visual Studio 2008 kein Update des Tools und nach einer Installation steht es auch nicht zur Verfügung. Der Trick für Visual Studio 2010 funktioniert allerdings auch in Version 2012. Einfach für die Version 11.0 eintragen und fertig.
Ich bin jetzt schon zum zweiten Mal darauf hereingefallen: Office 2010 und Visual Studio 2012 installiert und dann schnell ein Add-In für Word 2010 gebaut. Aber wo ist das Template dafür? Ich habe nur noch Office 2013 Templates:
Das Problem verbirgt sich ganz oben, bei der ausgewählten .NET Framework Version. Standardmäßig ist dort 4.5 ausgewählt. Die Design Time Unterstützung bei .NET Fx 4.5 ist aber auf Office 2013 beschränkt. Office 2010 verlangt nach einen früheren Framework: 3.5 oder 4.0. Wird 4.0 eingestellt, haben wir unsere vermissten Projektvorlagen zurück:
Was aber, wenn 3.5 ausgewählt wird?
Warum das denn jetzt? Visual Studio 2012 hat keinen Design Time Support für Office Projekte auf Basis von Framework 3.5. Das geht nur mit Visual Studio 2010 (3.5 und 4.0) Runtime Support ist aber sehr wohl vorhanden. Man kann in Visual Studio 2012 erstellte Lösungen auch unter Office 2010 und sogar 2007 laufen lassen, selbst mit Framework 4.5 (natürlich nur, wenn keine neuen Features verwendet worden sind, die in der alten Version noch nicht zur Verfügung standen). Anders herum laufen VSTO Lösungen, die gegen Office 2007 (oder 2010) erstellt wurden, auch unter Office 2013.
Nachdem wieder einige Zeit ins Land ging, gibt es nun auch wieder eine (fast) neue Version von VSTO. Visual Studio 2012 enthält VSTO 2012 (deren Update hier zu finden ist). Damit kann man Add-Ins und Erweiterungen für Office 2010 und 2013 bauen, je nachdem, welche Office Version installiert ist. Während bei Office 2013 sowohl das .NET Framework 4.0 als auch 4.5 verwendet werden kann, beschränkt sich Visual Studio bei Office 2010 auf .NET 4.0.
Mit Visual Studio 2010 kann Office 2013 nicht direkt (also zur Design-Zeit) adressiert werden, allerdings laufen bestehende VSTO Lösungen auch weiterhin unter Office 2013, ohne neu kompiliert werden zu müssen.
Update: Solutions, die mit Visual Studio 2012 gegen Office 2013 gebaut wurden, laufen auch (und zwar mit .NET Framework 4.0 oder 4.5 und VSTO Runtime 4.0 update) unter Office 2007 und 2010! Ohne Downgrade oder Rekompilierung. Die einzige Einschränkung ist das Office Objektmodell. Hier läuft nur, was in der niedrigeren Version auf vorhanden ist. Man sollte sich deshalb entweder auf den kleinsten gemeinsamen Nenner einigen oder zur Laufzeit die Office-Version abfragen und entzprechend reagieren.
Office 2013 legt an der einen oder anderen Stelle ein etwas anderes Verhalten an den Tag. Während Word bzw. Excel 2010 noch mit einem neuen, leeren Dokument startet, zeigt die 2013er Version beim Start die Backstage:
Das hat Auswirkungen auf Eventing und RibbonUI. Konnte man im Ribbon_Load Event bisher noch einen bestimmten Tab anspringen, verläuft dieser Aufruf nun ins Leere. Also, einfach die bestehende Lösung mal unter dem neuen Office ausprobieren.
Außerdem: die oben angesprochenen “Office Developer Tools for Visual Studio 2012” enthalten ebenfalls die Unterstützung zum Erzeugen sog. Agaves, der neuen Office Apps, basierend auf HTML5 und Java Script.
Wer schon immer mal wissen wollte, wie Premier for Developers funktioniert und wo deren Fokusgebiete liegen, dem sei meine (gerade begonnene) Blog Serie dazu zu empfehlen:
- PSfD Blog
- Was macht eigentlich Premier for Developers?
- Sicher sein oder sicher fühlen? – Microsoft Security Development Lifecycle (SDL)
Das ist nur der Beginn. Im Laufe der Zeit werden alle Technologie-Cluster vorgestellt, damit sich jeder Interessierte ein Bild von PSfD machen kann.
Spricht man von coolem Zubehör für die Kamera, so geht es in der Regel um [lichtstarke] (Zoom-) Objektive. Meine letzte Errungenschaft war eine L-Schiene (L-Bracket) von Kirk. Die ist wirklich eine Erleichterung, wenn es darum geht, Aufnahmen im Hoch- und dann wieder im Querformat zu machen. Das verlangt vom Kugelkopf alles ab und ist meist nicht ohne weiteres machbar. Da auf der linken Seite der Kamera die Anschlüsse sitzen, gibt esdort eine entsprechende Aussparung, durch die selbst der abgeknickte Stecker des Auslöserkabels bequem Platz hat, selbst im Hochformat. Passt perfekt zum Ultimate Ballhead von Acratech.
5 Sterne!
Eine einfache Möglichkeit, die installierten Versionen des .NET Frameworks zu ermitteln bietet dieses kleine Tool von ASoft. Außerdem kann man auch gleich das benötigte Framework oder SDK von Microsoft herunterladen. Die Links sind eingebaut.
Kollege Aaron Stebner hat ein Code-Beispiel für C++ veröffentlicht, das sich ebenfalls diesem Thema widmet.
Wenn bei installiertem Visual Studio 2010 die VSTO PowerTools installiert werden, wird das Visual Studio Add-In ImageMSO Window zum Anzeigen und Auswählen des gewünschten in Office integrierten Icons nicht angezeigt. Mit folgendem kleinen Trick funktioniert es wieder:
In der Konfigurationsdatei ImageMsoVS2008.AddIn muss die Version des Hosts (also Visual Studio) geändert werden:
<HostApplication> <Name>Microsoft Visual Studio</Name> <Version>10.0</Version></HostApplication>
Die Datei befindet sich im Ordner C:\Users\<UserName>\AppData\Roaming\Microsoft\MSEnvShared\Addins\ (unter Windows 7).
(getestet mit der englischen Version von VS 2010)
Wir suchen engagierte Softwarespezialisten, die Spaß an Teamwork haben, gerne unterwegs bei Kunden sind und einen von folgenden Bereichen abdecken können:
SQL-Developer
C++/Debugging Expert
Testing Expert
Sharepoint Developer
.NET Developer
Bewerbt Euch entweder direkt über den Link oder meldet Euch bei mir.
Am 04.11.11 findet zum dritten Mal die Community Veranstaltung Office Business Applikation (OBA) Day in Unterschleißheim bei Microsoft Deutschland statt. Dieser Tag widmet sich der Entwicklung rund um MS Office. Themen sind VSTO, SharePoint und Office 365. Des Weiteren bleibt auch viel Zeit für das Netzwerken und den Erfahrungsaustausch. Als Sprecher sind dabei: Thorsten Hans, Tom Wendel, Helmut Obertanner, Andreas Aschauer, Mario Meir-Huber und Lars Keller.Weitere Informationen & die Anmeldung finden Sie unter http://www.oba-day.de
Nach 8 Jahren in der DPE als Technical Evangelist und Platform Strategy Manager (wie schnell doch die Zeit vergeht…) habe ich mich entschlossen, etwas anderes zu machen. Seit Kurzem bin ich jetzt bei Premier Services for Developers (@ Microsoft) als Application Development Manager tätig. Das wird auch bedeuten, dass in diesem Blog nur noch gelegentlich Informationen auftauchen.
Auf jeden Fall sage ich erst mal Danke an alle, die hier mehr oder weniger regelmäßig vorbeigesehen haben.
Ist das das Ende vom Lied? Gewiß nicht. Eigentlich schwebt mir vor, mal dort mitzuarbeiten, wo am Ende auch etwas heraus kommt, was man anfassen kann ;-) - nämlich reale Produkte. Im Product Management oder der Entwicklungsleitung eines ISV zum Beispiel. Wenn das dann noch im Münchner Raum ist und etwas mit Cloud Computing und Windows Azure zu tun hat, dann bin ich interessiert.
Also dann, bis demnächst mal. Und paßt auf Euch auf.
- Jens
Die Professional Developer’s Conference 2011 heißt //build. Diesen Termin unbedingt vormerken: 13.- 16. September 2011, in Anaheim, Ca. Es gibt spannende Neuigkeiten! Einen Vorgeschmack gibt es hier. Die Registrierung ist offen!
PS: Die //Build wird die PDC nicht ersetzen. Es handelt sich um eine neue Konferenz.
Highlights in Visual Studio vNext kurz als deutsches Video erklärt – 30 Minuten die sich lohnen!
Einen kurzen Überblick über die bisher angekündigten Highlights in Visual Studio vNext – der nächsten Version von Visual Studio – gibt ALM Architekt und Visual Studio-Experte Christian Binder in seinem 30 Minuten dauernden Webcast. Hier bekommen Sie einen Ausblick auf einige der neuen Funktionen von Visual Studio vNext.
Schwerpunkte:
Alle bisherigen und zukünftigen Beiträge zu Visual Studio vNext finden Sie immer aktuell unter diesem Link.
Ich muss zugeben, inzwischen hasse ich den Begriff Cloud. Inzwischen ist es ein Buzz Word, dass jeder in den Mund nímmt. Das ist wie damals mit .NET. Auf einmal muss alles in der Cloud sein, notfalls macht man diese dann einfach privat. Aber was bedeutet das überhaupt? Und welche Rolle spielen hierbei die Begriffe SaaS, IaaS und PaaS? Wo paßt Windows Azure hinein?
Also vielleicht zuerst zu SaaS – Software as a Service. Software wird auf einer Web-Plattform zur Verfügung gestellt. Kostenlos oder im Mietmodell über monatliche Abrechnung. Im einfachsten Fall ist das eine Web-Applikation. Beispiele sind Google Apps, SalesForec Sales Cloud oder Microsoft Office Web Apps bzw. CRM Online, also Applikationen, mit denen Dokumente erstellt oder Daten erfaßt und bearbeitet werden können. Die Ablage der Dokumente oder Daten liegt oftmals ebenfalls in der Cloud.
Als Infrastructure as a Service (IaaS) bezeichnet man die Möglichkeit, eigene Virtuelle Maschinen in einem zentralen Rechenzentrum zu hosten. Man nimmt i.d.R. eine vorgefertigte VM mit dem Betriebssystem seiner Wahl (der Dienstleister unterstützt normalerweise nur bestimmt Versionen), installiert seine Services oder Anwendungen und betreibt das Ganze in einem zentralen Rechenzentrum. Für Updates der Virtuellen Maschinen ist der Betreiber (also Sie) zuständig. Das Paradebeispiel hierfür ist Amazon mit EC2 (Elastic Compute Cloud ). Der Unterschied zum klassischen Hoster ist die Verfügbarkeit von globalen Services, wie Storage, Message Queues, relationales Datenbankservices und ähnlichem sowie Mechanismen zum automatischen Skalieren (bei Bedarf werden neue Instanzen meiner VM automatisch hochgefahren [Elastic BeansTalk bei Amazon]).
Zu Guter Letzt PaaS – Platform as a Service. Hier ist der Abstraktionsgrad höher als bei IaaS, weil es eine Art Betriebssystem über den vielen einzelnen PCs/Blades gibt und die einzelnen virtualisierten Betriebssysteminstanzen verwaltet. Der Entwickler schreibt seine Software also gegen ein Cloud-Betriebssystem und kümmert sich dort eigentlich nur um die Rollen, in die seine Services/Anwendungen fallen (Web, Worker). Das Cloud-Betriebssystem kümmert sich um die Ausführung der Anwendung nach den vorgegebenen Eckdaten oder Regeln. Load Balancing, OS-Servicing (Patches, Updates), Desaster Recovery, Fail Over, etc. wird durch die Cloud Platform durchgeführt. Desweiteren stehen Komponenten wie Storage (no-SQL like, wie Tables, Blobs und Queues) zur Verfügung. Manche Anbieter, wie Microsoft, bietet ebenfalls eine relationale Datenbank in der Cloud. PaaS hat immer auch Komponenten von IaaS enthalten, allerdings gekapselt und meist nicht direkt erreich- oder konfigurierbar.
David Chappell hat dazu einmal eine sehr aussagekräftige Matrix gezeigt, welcher Anbieter mit welchen Services in welcher Rubrik zuhause ist:
Ein typischer Vertreter für PaaS ist Microsoft Windows Azure. Es werden von Windows Azure auch weitere Komponenten für den Entwickler von Software geboten, die die Kommunikation zwischen dem eigenen Rechenzentrum und den Cloud Services regelt (Azure Connect, Service Bus) oder für Sicherheit und Zugriffsrechte (Access Control Service) bzw. Caching (Caching Services) zuständig sind. Nicht zu vergessen, die Tools für den Software LifeCycle: mit Visual Studio und .NET sind Sie dabei. Aber auch PHP, Ruby, Java, Eclipse, C++ u.a. werden unterstützt.
Je weiter man also die Abstraktion voran treibt, um so mehr fallen die Betriebskosten.
Einen guten allgemeinen Überblick über Windows Azure bieten folgende gut 7 Minuten Video:
Welche Szenarien sind für Windows Azure prädestiniert? Das sind Anwendungen, die
Das sind einige, sicher aber nicht alle Szenarien für eine Cloud Plattform.
Jetzt war es endlich soweit: Auf der D9 - All Things Digital-Konferenz wurde zum ersten Mal enthüllt, was uns alle mit Windows 8 erwartet.
Hier einige Schlagzeilen:
Man muss es selbst sehen, um einen Eindruck davon zu bekommen. Das offizielle Microsoft Video zum Windows 8 Sneak Peek ist hier zu finden.
Home Screen mit interaktiven Kacheln (Tiles), die wichtige Informationen aus den dahinter stehenden Applikationen zeigen:
Internet Explorer 10, hier im Eingabemodus:
… und im Full Screen Mode (Standard):
Eingabe per Touch-Keyboard:
umschaltbar auf ein Slate-Keyboard für die Bedienung mit zwei Daumen:
Side by side: Windows 8-optimierte Apps und herkömmliche Anwendungen, wie Excel
touch-optimierter FileBrowser:
Fazit:
Windows 8 wird für Touch-Bedienung optimiert sein, läßt sich aber auch ganz “normal” bedienen. Per HMTL 5 können Web Apps direkt mit den Live Tiles des Startbildschirms kommunizieren, ohne dass diese im Vordergrund sein muss.
In diesem sehr ausführlichen und interessanten Video zeigt Microsoft VP Mike Angiulo auf der Computex 2011 die ersten Highlights von Windows 8
Die offizielle Pressemitteilung findet sich hier.
But that’s not all we have to say …
Den nächsten Termin unbedingt vormerken: 13.- 16. September 2011, die Konferenz für Windows 8 [// Build Windows] von Microsoft in Anaheim, Ca. Die Registrierung ist offen!
Ich habe in der Vergangenheit einige Artikel, Samples und WebCasts zum Thema Open XML bereitgestellt. Jetzt tauchte die Frage – oder vielmehr die Beobachtung – auf, dass das Einfügen von 50.000 Datensätzen á 10 Werten einige Stunden benötige. Dabei soll doch Open XML so schnell sein. Wie paßt das zusammen?
Es kommt natürlich auf den Source Code an . Wie immer. Der in den Beispielen gezeigte Code war gedacht zum Einfügen einzelner Zellen in bestehende Tabellen. Dabei wurde Rücksicht auf viele Fälle genommen:
1. Die Zeile/Zelle existiert schon oder auch nicht2. Es gibt vor der Zeile/Zelle oder danach weitere Zeilen/Zellen
Demensprechend verändert sich die Art und Weise Wie und vor allem Wo die Zelle bzw. Zeile eingefügt werden muss. Ich habe den Fall nachgestellt und mit dem gezeigten Beispiel 50.000 Datensätzen geschrieben. Das dauerte knapp 27 Minuten.
Danach habe ich den Code umgeschrieben. Wenn so viele Datensätzen nacheinander geschrieben werden, dann kann man davon ausgehen (oder zumindest sollte man diese Voraussetzung schaffen), dass diese Schreibvorgänge in einen leeren Bereich fallen, also erstens in der zu schreibenden Zeile noch nichts steht und es zweitens keine Zeilen nach diesem Bereich gibt, die Daten enthalten. Ein entsprechendes Template vorausgesetzt, kann jetzt ohne jegliche Prüfungen geschrieben werden. Und das spart richtig Zeit. Mit diesem Code:
SheetData sd = new SheetData();ws.AppendChild(sd);char c;Row r = new Row();for (uint i = 1; i < 50000; i++ ){ r.RowIndex = i; for (int si = 65; si < 75; si++) { c = Convert.ToChar(si); string cellRef = c.ToString() + i.ToString(); r.AppendChild(CreateCellWithInlineString(cellRef, "Microsoft DPE Test")); } sd.AppendChild(r); r = new Row();}ws.Save(partWS);
habe ich die Zeit auf knapp 15 Sekunden gedrückt. Das sind gerade mal 0,9% der vorher benötigten Zeit.
Es kommt also immer darauf an, welchen Zweck der eingesetzte Code erfüllen soll und es lohnt sich, für verschiedene Einsatzgebiete angepaßte Voraussetzungen zu schaffen.
Gregor Biswanger (MVP für Client App Dev, Solution Architect & Silverlight Experte) hat seit gestern Nachmittag eine Aktion auf seinem Blog unter dem Titel „WPF 4 und Silverlight 4 gegen Krebs!“ gestartet. Bei seinem letzten Besuch in Redmond hatte er seine DVD von den WPF-, Silverlight- und Expression Blend-Produktteams signieren lassen. Diese signierte DVD wird auf eBay versteigert und der Erlös geht direkt an die Deutsche Krebshilfe e.V.. Weitere Informationen auf seinem Blog unter: http://www.dotnet-blog.net/post/2011/03/15/WPF-4-und-Silverlight-4-gegen-Krebs!.aspx
Also dann, wer helfen kann und will, bitte mitbieten.
Ab 21. März, jeweils ab 15 Uhr, begrüßt Sie Uwe Baumann auf der
RAPID APPLICATION DEVELOPMENT ROADSHOW 2011
Hier können Sie Visual Studio LightSwitch 2011 (Beta), Team Foundation Server 2010 und Windows Azure live erleben. Sprecher sind u.a. Christian Binder und Bernhard Frank.
“CLOUD Computing” ist wohl das Buzzword des Jahres. Aber was steckt wirklich dahinter? Macht das Sinn für mich? Welche Dienste gibt es? Was ist der Unterschied zwischen SaaS, PaaS, IaaS und was hat es mit Public und Private Cloud auf sich? Ist das Ganze eigentlich sicher? Muss ich als Entwickler oder Administrator alles neu lernen? Und was hat Microsoft eigentlich mit Cloud zu tun?
Antworten hierzu gibt es auf unserer Roadshow Microsoft TechSummit Cloud.
Vom 28. März bis zum 8. April 2011 touren Experten von Microsoft TechNet und MSDN durch sechs deutsche Metropolen um Best Practices und technische Hintergründe zu den neuesten Microsoft Cloud-Technologien zu vermitteln und Anwenderfragen zu beantworten. Wir geben einen Überblick über die Microsoft Cloud, stellen für Administratoren, Entwickler und IT-Manager die neuesten Produkte und Technologien vor und zeigen Ihnen, wie Sie Cloud Services in Ihre bestehende Infrastruktur integrieren und Ihre Software an die Cloud anpassen bzw. in der Cloud bereitstellen können.
Für IT-Professionals und Administratoren dürften besonders die Vorträge "Windows InTune" (Ralf Schnell), "Office 365 für Administratoren" (Steffen Krause, Daniel Melanchthon) oder "Private Cloud selber aufbauen" (Frank Koch) interessant sein. Relevant für Entwickler sind u.a. Vorträge zur "Migration von Anwendungen in die Cloud" (Dariusz Parys), zur "Entwicklung von SaaS Lösungen auf Basis der Windows Azure Platform" (Patric Boscolo) und zu dem "Betrieb und Entwicklung mit der Microsoft Cloud" (Holger Sirtl).
Im Februar habe ich mir mal die Zeit genommen, ein paar der Beispiele und Ideen aus meinem Blog in Video-Tutorial umzusetzen. Herausgekommen sind diese 4 WebCasts:
Dateimanipulation mit den Open XML SDK 2.0-Excel-Dateien (Teil 1 von 4) - Benannte Bereiche in Excel: Anlegen und Auslesen von Named Ranges
Dateimanipulation mit den Open XML SDK 2.0-Excel-Dateien (Teil 2 von 4) - Excel-Dateien erzeugen, Zellen und Zeilen in Excel-Tabellen anlegen und einfügen
Dateimanipulation mit den Open XML SDK 2.0-Excel-Dateien (Teil 3 von 4) - Die Handhabung von Shared Strings
Dateimanipulation mit den Open XML SDK 2.0-Excel-Dateien (Teil 4 von 4) - Formatvorlagen für Excel: Der Umgang mit StyleSheets
An alle Windows 7 Maschinisten und solche die es werden wollen.
Der Auftrag
Du bist der Maschinist und nur du kennst die Maschine wirklich. Welche Funktion suchst du in deinem System vergeblich? Die Mäusejagd, das Schneetreiben oder etwas ganz Anderes? Stell dich der Herausforderung und baue deine eigene verrückte Wunsch-Anwendung. Dabei sollte mindestens eine der vielen neuen Windows 7 Funktionen verwendet werden. Lade dir dein Werkzeug kostenlos herunter und zeig wie kreativ du bist. Wie dein Windows 7 aussehen soll – das kannst du jetzt selbst bestimmen.
Die Anforderungen:
Weitere Informationen und Teilnahmebedingungen
Der Hauptgewinn (3x) : Großer Las Vegas Gambling-Trip mit Besuch der MIX und Helikopterflug.
Dieser Trip besteht aus:
Also dann, ran an die Tastatur….
Softwaretester und -entwickler: Machen Sie mit beim Testing Leadership Award 2011
Unbestritten, IT-Systeme werden immer komplexer und sind damit anfälliger für Softwarefehler. Die Folgen sind Schäden in wirtschaftlicher Hinsicht, aber auch unzufriedene Kunden. Der Testing Leadership Award zeigt Ihren Kunden, dass die von Ihnen getestete Software den höchsten Ansprüchen an Bedienungskomfort, Qualität und Zuverlässigkeit entspricht.
Die BQI Leadership Awards sind Teil eines internationalen Award-Programms, das den Erfolg von Unternehmen bei Innovationen, Produkten oder Dienstleistungen würdigt und auszeichnet. Das BQI zeichnet Unternehmen aus, die in anerkannter Weise besondere Leistungen in den Bereichen Produktentwicklung, Innovation, Geschäftsprozesse oder Management erbracht haben.
Mit der Auszeichnung Leadership Award gewinnen Sie ein wertvolles Marketinginstrument, um Ihre Produkte und Dienstleistungen noch erfolgreicher am Markt zu positionieren.
Melden Sie sich jetzt zum Testing Leadership Award 2011 an. Meldeschluss ist 31.März 2011. Direkt zur Anmeldung: http://www.bqi-research.com/de-user-AwardRegistration-index-Testing+Leadership+Award.html?param1=Testing+Leadership+Award
Code-Beispiele sind für mich meist mehr wert als eine ausführliche Dokumentation. Nichts gegen Dokumentationen. Im Gegenteil. Wenn diese gut und aussagekräftig sind und natürlich Code Samples enthalten. Leider sind die kleinen Snippets oftmals viel zu kurz und aus dem Zusammenhang gerissen. Dem kann man jetzt etwas entgegen stellen. Für alle, die es noch nicht gesehen haben, Microsoft hat einen All-In-One Code Framework Sample Catalog veröffentlicht mit folgenden Inhalten:
Der MSDN on Tour-Bus ist vom 26.01. bis zum 05.03.2011 quer durch Deutschland unterwegs. Der Tour-Bus ist ein rollendes Präsentationsstudio, in dem ein breitgefächertes Vortragsangebot zu aktuellen Entwicklerthemen (zwei parallele oder ein gemeinsamer Vortrag) bereit steht. Der Eintritt ist frei.
Alle weiterführende Informationen: http://www.msdn-on-tour.de
Zeichenketten zu bestehenden Dateien hinzufügen
Bisher haben wir uns nur mit dem Neuerzeugen von Arbeitsmappen beschäftigt. Deshalb jetzt noch ein Blick hinter die Kulissen für den Fall, dass die Datei schon existiert.
private void AddSharedStringsToExistingWorksheet(string FName, string sheetName, string cellRef, string sharedString) { using (SpreadsheetDocument xlDoc = SpreadsheetDocument.Open(FName, true)) { // Gibt es einen SharedStringTablePart? // Nein ==> erzeugen // Gibt es das Worksheet? // Nein ==> erzeugen // Gibt es die gesuchte Zeile? // Nein ==> erzeugen // Gibt es die gesuchte Zelle in der Zeile? // Nein ==> erzeugen // String einfügen // Dokument speichern } } !-- code>
Gibt es einen SharedStringTablePart?
Wir könnten jetzt natürlich wieder mit Inline Strings arbeiten (und es spricht eigentlich nichts dagegen), aber wir wollen streng “by the book” vorgehen die Zeichenketten in die Shared Strings-Tabelle eintragen. Dazu muss zuerst einmal geschaut werden, ob diese überhaupt vorhanden ist. Wenn nicht, muss sie erzeugt werden. GetPartsOfType() liefert eine Sammlung aller gefundenen Parts eines bestimmten Typs. Da es nur einen geben kann, können wir mit der Extension Method FirstOrDefault() die gewünschte Referenz erhalten. First() kann hier nicht verwendet werden, da nur FirstOrDefault() bei leerer Liste null liefert. In dem Fall müssen wir den Part selbst erzeugen.
SharedStringTablePart sstPart = xlDoc.WorkbookPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault(); if (sstPart == null) sstPart = xlDoc.WorkbookPart.AddNewPart<SharedStringTablePart>(); !-- code>
Gibt es das Worksheet?
Zuerst müssen wir den WorkbookPart holen, da dort die Referenzen auf die Worksheets gehalten werden. Daraus läßt sich über Lambda Functions das Worksheet mit den gesuchten Namen ermitteln oder erzeugen, falls noch nicht vorhanden.
WorkbookPart wbPart = xlDoc.WorkbookPart; Workbook wb = wbPart.Workbook; Sheet sheet = wb.GetFirstChild<Sheets>().Elements<Sheet>() .Where(s => s.Name == sheetName).FirstOrDefault(); WorksheetPart wsPart; Worksheet ws = null; if (sheet == null) // nicht da ==> erzeugen { wsPart = InsertWorksheetPart(wbPart, tbWorksheetName.Text); ws = new Worksheet(new SheetData()); // worksheet.xml erzeugen } !-- code>
Wenn es schon existiert, könnten wir uns eigentlich zurücklehnen, da wir weiter ober ja schon das Sheet mit den entsprechenden Namen gefunden hatten. Weit gefehlt. Oben wird eine Referenz auf ein Sheet zurückgegeben, wir brauchen aber die Referenz auf das Worksheet. Das ist ein kleiner, aber feiner Unterschied. Über die ID des Sheets können wir den WorksheetPart extrahieren (GetPartById) und von dort kommen wir an das Worksheet.
else // es existiert { string rId = sheet.Id.Value; wsPart = (WorksheetPart)xlDoc.WorkbookPart.GetPartById(rId); ws = wsPart.Worksheet; } !-- code>
Aus dem Worksheet muss nun noch das Root Element der Tabelle geholt werden - da wir ja vorher nicht wissen können, ob das Worksheet existiert.
SheetData sd = ws.GetFirstChild<SheetData>();
Gibt es die gesuchte Zeile?
Eine Excel-Tabelle ist zeilenweise aufgebaut. Unterhalb des SheetData Elements befinden sich die Tabellen-Zeilen im Markup. Also müssen wir die mit dem vorgegebenen Zeilenindex finden oder erzeugen. Die Hilfsfunktion RemoveAllButNumbers ist eine eigene Extension Method, die aus einer Zelladresse (AB123) nur die Zeilenreferenz (123) zurück liefert.
UInt32 rowIndex = (UInt32)cellRef.RemoveAllButNumbers(); Row row = sd.Elements<Row>() .Where(r => r.RowIndex.Value == rowIndex) .FirstOrDefault(); if (row == null) row = CreateNewRow(ref sd, rowIndex); !-- code>
Gibt es die gesuchte Zelle?
In etwa das Gleiche machen wir mit der Zelle innerhalb der eben gefundenen Zeile.
Cell cell = row.Elements<Cell>() .Where(c => c.CellReference == cellRef) .FirstOrDefault(); if (cell == null) cell = CreateNewCell(ref row, cellRef); !-- code>
Zellwerte schreiben und speichern
Zum Abschluß wird die Zeichenkette in der Shared Strings-Tabelle gespeichert bzw. - falls dort schon vorhanden - deren Index ermittelt und dieser in die ermittelte oder erzeugte Zelle eingetragen. (Die Routine zum Eintragen des Shared Strings wurde schon in Teil 2 beschrieben.)
cell.DataType = CellValues.SharedString; cell.CellReference = cellRef; cell.CellValue = new CellValue(InsertSharedStringItem(sharedString, sstPart).ToString()); ws.Save(wsPart); !-- code>
So, wie man sieht, kann man sich auf nichts verlassen ;-) und muss ständig prüfen, ob bestimmte Teile schon vorhanden sind. Es wird in der Praxis wohl weitaus häufiger vorkommen, schon existierende Dateien abfragen oder ändern zu müssen als neue zu erzeugen. Nichtsdestotrotz lohnt sich ein Blick auch hinter diese Kulissen, um die Zusammenhänge zu verstehen. So ein Dateiformat ist verdammt komplex und man wird als Einzelner wohl nie alles verstehen können, was in einer Excel- oder Word-Datei so alles vor sich geht. Das Gute an der Sache ist, das man nie alles braucht, sondern einzelne Teilbereiche relativ gut von anderen abgetrennt sind. So kommt man dann Schritt für Schritt voran.
Das Beispielprojekt kann übrigens hier heruntergeladen werden.
[Fortsetzung folgt]
Stylesheets erzeugen und verwenden
So, ich hoffe, alle hatten ein einigermaßen geruhsames Weihnachtsfest und die Nachwirkungen des Silvesterparty sind überwunden.
Im letzten Teil haben wir bereits die Zusammenhänge aufgezeigt. Jetzt geht es weiter mit der Implementierung der Routine zum Erzeugen des Stylesheets. Wie schon mehrfach erwähnt muss das nicht immer getan werden. Verwendet werden kann auch ein Stylesheet aus einer vorhandenen Excel-Arbeitsmappe, sofern alle gewünschten Formatvorlagen dort definiert wurden.
Grob betrachtet gliedert sich die Routine in 7 Teile:
static class StylesClass { internal static Stylesheet GenerateStylesXml() { Stylesheet ssh = new Stylesheet(); #region +++ Add Number Formats +++ #region +++ Add Font Information +++ #region +++ Add Fill Styles +++ #region +++ Add Border Styles +++ #region +++ Add Cell Style Formats +++ #region +++ Add Cell Formats // Master Formatting Records +++ #region +++ Add Cell Styles +++ return ssh; } } !-- code>
Zuerst müssen die Zahlenformate definiert werden, die dann in der Zellformatierung und in Formatvorlagen verwendet werden. Darin wird eine ID verwendet, die jedes einzelne Format eindeutig identifiziert. Da Excel schon eingebaute Formate besitzt, müssen wir hinter deren ID starten. Die Werte von 0 bis 164 sind m.W. für interne Formate reserviert. Neben der ID ist der Formatcode erforderlich, den wir aus Excel selbst kennen.
Mit Hilfe der Object Initializer kann der Code verkürzt werden.
#region +++ Add Number Formats +++ uint NumFmtId = 165; NumberingFormats nfs = new NumberingFormats(); NumberingFormat nf1 = new NumberingFormat() { NumberFormatId = NumFmtId++, FormatCode = "0" }; nfs.Append(nf1); NumberingFormat nf2 = new NumberingFormat() { NumberFormatId = NumFmtId++, FormatCode = "#.##0,00 €;[Red]-#.##0,00 €" }; nfs.Append(nf2); NumberingFormat nf3 = new NumberingFormat() { NumberFormatId = NumFmtId++, FormatCode = "0 \"km/h\"" }; nfs.Append(nf3); NumberingFormat nf4 = new NumberingFormat() { NumberFormatId = NumFmtId++, FormatCode = "dd.mm.yyyy" }; nfs.Append(nf4); NumberingFormat nf5 = new NumberingFormat() { NumberFormatId = NumFmtId, FormatCode = "dddd, dd. mmmm yyyy" }; nfs.Append(nf5); nfs.Count = (uint)nfs.ChildElements.Count; ssh.Append(nfs); #endregion !-- code>
Nun folgen die Font-Informationen. Da Fonts per Index referenziert werden, ist keine ID nötig. Wenn allerdings derselbe Font zusätzlich jeweils mit dem Schnitt kursiv bzw. fett benötigt wird, so sind 3 verschiedene Font-Einträge erforderlich.
#region +++ Add Font Information +++ Fonts fts = new Fonts(); Font ft = new Font() { FontName = new FontName() { Val = "Calibri" }, FontSize = new FontSize() { Val = 11D } }; fts.Append(ft); ft = new Font() { FontName = new FontName() { Val = "Aharoni" }, FontSize = new FontSize() { Val = 18D } }; fts.Append(ft); ft = new Font() { FontName = new FontName() { Val = "Bradley Hand ITC" }, FontSize = new FontSize() { Val = 20D }, Bold = new Bold() }; fts.Append(ft); ft = new Font() { FontName = new FontName() { Val = "Bernard MT Condensed" }, FontSize = new FontSize() { Val = 16D } }; fts.Append(ft); fts.Count = (uint)fts.ChildElements.Count; ssh.Append(fts); #endregion !-- code>
Die Füllmuster der Zelle werden hier definiert und ebenfalls über den Index referenziert:
#region +++ Add Fill Styles +++ Fills fis = new Fills(); Fill fi = new Fill() { PatternFill = new PatternFill() { PatternType = PatternValues.None } }; fis.Append(fi); fi = new Fill() { PatternFill = new PatternFill() { PatternType = PatternValues.LightGray } }; fis.Append(fi); fis.Count = (uint)fis.ChildElements.Count; ssh.Append(fis); #endregion !-- code>
Die Randeinstellungen der Zelle sind hier nur gekürzt dargestellt:
#region +++ Add Border Styles +++ Borders bos = new Borders(); Border bo = new Border() { … }; bos.Append(bo); bo = new Border() { LeftBorder = new LeftBorder() { Style = BorderStyleValues.Thin }, RightBorder = new RightBorder() { Style = BorderStyleValues.Thin }, TopBorder = new TopBorder() { Style = BorderStyleValues.Thin }, BottomBorder = new BottomBorder() { Style = BorderStyleValues.Thin } }; bos.Append(bo); bos.Count = (uint)bos.ChildElements.Count; ssh.Append(bos); #endregion !-- code>
Für die weiter unten mit Namen definierten eingebauten Formatvorlagen werden hier Standardeinstellungen definiert. Diese können jederzeit durch direkte Formatierung (Master Cell Records) überschrieben werden.
#region +++ Add Cell Style Formats +++ CellStyleFormats cstfs = new CellStyleFormats(); CellFormat cfmt = new CellFormat() { NumberFormatId = 0, FontId = 0, FillId = 0, BorderId = 0 }; cstfs.Append(cfmt); cstfs.Count = (uint)cstfs.ChildElements.Count; ssh.Append(cstfs); #endregion !-- code>
Ein Beispiel aus einer Arbeitsmappe zeigt mögliche Definitionen:
Bei den Master Cell Records laufen alle Fäden zusammen. Jeder Zelle bezieht sich auf einen Index in dieser Tabelle. Hier werden die Abweichungen vom Standardformat „Normal“ definiert. Alles, was nicht definiert wurde, wird von „Normal“ übernommen. Über die FormatId wird auf die benannten Formatvorlagen verwiesen.
#region +++ Add Cell Formats // Master Formatting Records +++ CellFormats cfx = new CellFormats(); CellFormat cf; cf = new CellFormat() { NumberFormatId = nf1.NumberFormatId, FontId = 0 , FormatId = 0 }; cfx.Append(cf); cf = new CellFormat() { NumberFormatId = nf1.NumberFormatId, FontId = 0 , FormatId = 0 }; cfx.Append(cf); cf = new CellFormat() { NumberFormatId = nf3.NumberFormatId, FontId = 1 , FormatId = 0 }; cfx.Append(cf); cf = new CellFormat() { NumberFormatId = nf5.NumberFormatId, FontId = 2, BorderId = 1, FillId = 1 , FormatId = 0 }; Alignment al = new Alignment() { Horizontal = HorizontalAlignmentValues.Center }; cf.Append(al); cfx.Append(cf); cf = new CellFormat() { NumberFormatId = nf1.NumberFormatId, FontId = 2 , FormatId = 0 }; al = new Alignment() { Horizontal = HorizontalAlignmentValues.Right }; cf.Append(al); cfx.Append(cf); cf = new CellFormat() { NumberFormatId = nf1.NumberFormatId, FontId = 3 , FormatId = 0 }; cfx.Append(cf); cf = new CellFormat() { NumberFormatId = 12, FontId = 3 , FormatId = 0 }; al = new Alignment() { Horizontal = HorizontalAlignmentValues.Center }; cf.Append(al); cfx.Append(cf); cfx.Count = (uint)cfx.ChildElements.Count; ssh.Append(cfx); #endregion !-- code>!-->
#region +++ Add Cell Formats // Master Formatting Records +++ CellFormats cfx = new CellFormats(); CellFormat cf; cf = new CellFormat() { NumberFormatId = nf1.NumberFormatId, FontId = 0 , FormatId = 0 }; cfx.Append(cf); cf = new CellFormat() { NumberFormatId = nf1.NumberFormatId, FontId = 0 , FormatId = 0 }; cfx.Append(cf); cf = new CellFormat() { NumberFormatId = nf3.NumberFormatId, FontId = 1 , FormatId = 0 }; cfx.Append(cf); cf = new CellFormat() { NumberFormatId = nf5.NumberFormatId, FontId = 2, BorderId = 1, FillId = 1 , FormatId = 0 }; Alignment al = new Alignment() { Horizontal = HorizontalAlignmentValues.Center }; cf.Append(al); cfx.Append(cf); cf = new CellFormat() { NumberFormatId = nf1.NumberFormatId, FontId = 2 , FormatId = 0 }; al = new Alignment() { Horizontal = HorizontalAlignmentValues.Right }; cf.Append(al); cfx.Append(cf); cf = new CellFormat() { NumberFormatId = nf1.NumberFormatId, FontId = 3 , FormatId = 0 }; cfx.Append(cf); cf = new CellFormat() { NumberFormatId = 12, FontId = 3 , FormatId = 0 }; al = new Alignment() { Horizontal = HorizontalAlignmentValues.Center }; cf.Append(al); cfx.Append(cf); cfx.Count = (uint)cfx.ChildElements.Count; ssh.Append(cfx); #endregion !-- code>
Die Definition der Namen benannter Formatvorlagen erfolgt hier. Im Anhang H der ISO/IEC 29500 Formatbeschreibung findet man eine Auflistung der vordefinierten Formate. Die BuildInID verweist darauf. Es muss zumindest die Vorlage „Normal“ definiert werden, da alle Zellen erst mal diese Formatvorlage erhalten.
#region +++ Add Cell Styles +++ CellStyles csts = new CellStyles(); CellStyle cst = new CellStyle() { Name = "Normal", FormatId = 0, BuiltinId = 0 }; csts.Append(cst); csts.Count = (uint)csts.ChildElements.Count; ssh.Append(csts); #endregion !-- code>
Ein Beispiel aus einer Arbeitsmappe zeigt mögliche verwendete Formatvorlagen:
Der in Teil 1 schon einmal rudimentär aufgezeigte Code zum Erstellen der Zellinhalte sieht ausformuliert so aus:
// worksheet.xml erzeugen: Worksheet ws = new Worksheet(); SheetData sd = new SheetData(); ws.AppendChild(sd); Row r1 = new Row(); r1.RowIndex = 1; // Create InlineString: r1.AppendChild(CreateCellWithInlineString("A1", "Der Zellwert als String", 0)); // Create Number Entry: r1.AppendChild(CreateCellWithNumber("B1", "42", 2)); // Create Formula: r1.AppendChild(CreateCellWithFormula("C1", "B1*5", 1)); // Create SharedString: r1.AppendChild(CreateCellWithSharedString("D1", "Microsoft", partSharedStrings, 4)); Row r3 = new Row(); r3.RowIndex = 3; r3.AppendChild(CreateCellWithSharedString("A3", "Jens Häupel", partSharedStrings, 4)); r3.AppendChild(CreateCellWithSharedString("B3", "Microsoft", partSharedStrings, 5)); Row r5 = new Row(); r5.RowIndex = 5; // Create Date Entry: r5.AppendChild(CreateCellWithNumber("A5", "40536", 3)); // Create Fraction: r5.AppendChild(CreateCellWithNumber("B5", "2.75", 6)); sd.AppendChild(r1); sd.AppendChild(r3); sd.AppendChild(r5); ws.Save(partWS); !-- code>
Daraus ergibt sich diese Tabelle in Excel. Man sieht die verschiedenen Formatierungenin Bezug auf Zeichensatz, Schriftschnitt und –größe sowie Ausrichtung.
Aber halt, das ist eigentlich nicht das korrekte Ergebnis. Das sieht nämlich so aus:
Hier wird eine Limitierung deutlich: Open XML ist zwar sehr gut geeignet, Daten aus Dateien zu extrahieren oder auch Inhalte zu verändern oder erzeugen, auf eine Sache hat Open XML aber keinen Einfluss: das Rendering. Dafür werden Informationen über Drucker- und Grafikengine benötigt. Letztendlich ist es möglich, z.B. die optimale Breite einer Zelle, ausgehend von einer gegebenen Schrift (incl. Größe, Schnitt und Skalierung) zu berechnen und auch per Open XML einzustellen.
Die exakte Definition, wie die Berechnung zu erfolgen hat, findet sich in Abschnitt 18.3.1.13 der Open XML Dokumentation.
Die Spaltenbreite kann man wiederum setzen, indem einzelne Spalten definiert und der Columns Collection hinzugefügt werden:
Columns cols = new Columns( new Column() { Min = 1, Max = 1, Width = 12, CustomWidth = true }, new Column() { Min = 1, Max = 1, Width = 12, CustomWidth = true } ); ws.Append(cols); !-- code>
Ganz am Ende noch ein Blick in die Shared String Tabelle. Obwohl der Begriff „Microsoft“ zweimal verwendet wurde, erscheint er richtigerweise nur einmal als Shared String: