Excel mit C# Automatisieren PDF Drucken E-Mail
Geschrieben von: Seishiroa   
Donnerstag, den 18. Juni 2009 um 16:24 Uhr

Da ich mich vor kurzem aus gewisser not heraus mit Excel beschäftigen musste will ich euch meine Erkenntnisse nicht vorenthalten, schließlich war es lästig genug mir alles zusammenzusuchen.

Was also braucht man um Excel mit C# zu automatisieren?

  • Irgendeine Office Installation 2003 oder 2007 ( Office XP a.k.a 2002 sollte theoretisch zwar auch gehen aber es ist recht lästig)
  • Irgendein Visual Studio (ich benutze hier 2008)

Bevor wir weiter ins Detail gehen sei noch gesagt das Excel den ein oder anderen Bug in seinen Schnittstellen hat, so ist es z.B. nur möglich mit Excel zu kommunizieren wenn man den en-us Zeichensatz von Windows benutzt. Um also erheblichen Programmieraufwand zu vermeiden einfach die Lokalität des PC´s auf English(United-States) stellen.

Wie ist Excel´s Datenstruktur aufgebaut?
Wie wahrscheinlich jeder der schon mal mit Excel gearbeitet hat weis setzt sich eine Excel Arbeitsmappe (Workbook) aus beliebig vielen Blättern (Worksheets) zusammen die wiederum einzelne Zellen (Cells) enthalten.

So jetzt erst mal genug bla bla, legen wir los.
Erstellt einfach ein neues WindowsForms Projekt. Klickt dann im Projektmappenexplorer auf Verweise und wählt hinzufügen aus. Im "Hinzufügen" Fenster klickt auf den Reiter "COM" und sucht euch die "Microsoft Excel [VERSIONS NR.] Object Library"
Die verfügbaren Versionsnummern sind von der/den installierten Office Versionen abhängig:

  • Office 2002 (XP)  = v. 10
  • Office 2003           = v. 11
  • Office 2007           = v. 12

Jetzt ergänzt ihr noch die using direktiven:

 using Excel = Microsoft.Office.Interop.Excel;
 using System.Reflection; 
 


Wir könnten jetzt zwar schon drauf los schreiben aber mit ein paar kleinen Helfern tun wir uns wesentlich leichter.
Diese Klasse enthält alle Parameter die zum sauberen öffnen einer Excel - Datei nötig sind. (Dank an Stefan Macke)
 /// <summary>
 /// Diese Klasse enthält alle Konstanten, die beim Zugriff auf Excel
 /// benötigt werden. Die Beschreibungen sind von Microsoft übernommen
 /// und daher noch auf Englisch.
 /// </summary>
 public static class ExcelKonstanten
 {
 /// <summary>
 /// Specifies the way links in the file are updated. If this
 /// argument is omitted, the user is prompted to specify how
 /// links will be updated. Otherwise, this argument is one of
 /// the values listed in the following table.
 /// </summary>
 public enum UpdateLinks
 {
 DontUpdate = 0,
 ExternalOnly = 1,
 RemoteOnly = 2,
 ExternalAndRemote = 3
 };
 
 /// <summary>
 /// True to open the workbook in read-only mode.
 /// </summary>
 public const bool ReadOnly = true;
 public const bool ReadWrite = false;
 
 /// <summary>
 /// If Microsoft Excel is opening a text file, this argument
 /// specifies the delimiter character, as shown in the following
 /// table. If this argument is omitted, the current delimiter
 /// is used.
 /// </summary>
 public enum Format
 {
 Tabs = 1,
 Commas = 2,
 Spaces = 3,
 Semicolons = 4,
 Nothing = 5,
 CustomCharacter = 6
 };
 
 /// <summary>
 /// True to have Microsoft Excel not display the read-only
 /// recommended message (if the workbook was saved with the
 /// Read-Only Recommended option).
 /// </summary>
 public const bool IgnoreReadOnlyRecommended = true;
 public const bool DontIgnoreReadOnlyRecommended = false;
 
 /// <summary>
 /// If the file is a Microsoft Excel 4.0 add-in, this argument
 /// is True to open the add-in so that its a visible window.
 /// If this argument is False or omitted, the add-in is opened
 /// as hidden, and it cannot be unhidden. This option doesn't
 /// apply to add-ins created in Microsoft Excel 5.0 or later.
 /// If the file is an Excel template, True to open the specified
 /// template for editing. False to open a new workbook based on
 /// the specified template. The default value is False.
 /// </summary>
 public const bool Editable = true;
 public const bool NotEditable = false;
 
 /// <summary>
 /// If the file cannot be opened in read/write mode, this
 /// argument is True to add the file to the file notification
 /// list. Microsoft Excel will open the file as read-only, poll
 /// the file notification list, and then notify the user when
 /// the file becomes available. If this argument is False or
 /// omitted, no notification is requested, and any attempts to
 /// open an unavailable file will fail.
 /// </summary>
 public const bool Notify = true;
 public const bool DontNotifiy = false;
 
 /// <summary>
 /// The index of the first file converter to try when opening
 /// the file. The specified file converter is tried first; if
 /// this converter doesnt recognize the file, all other converters
 /// are tried. The converter index consists of the row numbers
 /// of the converters returned by the FileConverters property.
 /// </summary>
 public enum Converter
 {
 Default = 0
 };
 
 /// <summary>
 /// True to add this workbook to the list of recently used files.
 /// The default value is False.
 /// </summary>
 public const bool AddToMru = true;
 public const bool DontAddToMru = false;
 
 /// <summary>
 /// True saves files against the language of Microsoft Excel
 /// (including control panel settings). False (default) saves
 /// files against the language of Visual Basic for Applications
 /// (VBA) (which is typically US English unless the VBA project
 /// where Workbooks.Open is run from is an old internationalized
 /// XL5/95 VBA project).
 /// </summary>
 public const bool Local = true;
 public const bool NotLocal = false;
 
 public enum CorruptLoad
 {
 NormalLoad = 0,
 RepairFile = 1,
 ExtractData = 2
 };
 }
 


Bevor wir nun ein Workbook öffnen können brauchen wir erst einmal eine Instanz von Excel außerdem brauchen wir speicher für Workbook, Worksheet, und Range Objekte.  Das Range Objekt enthält einen bereich von Zellen also enthät z.b. Range("A1", "A4") die Zellen A1 bis A4.
 // Benötigte Objekte erzeugen
 Excel.Application exl1;
 Excel.Application exl2;
 Excel._Workbook wbQuelle;
 Excel._Workbook wbZiel;
 Excel._Worksheet wsQuelle;
 Excel._Worksheet wsZiel;
 
 Excel.Range cellZiel;
 Excel.Range cellQuelle;
 


Wer sich schon ein wenig mit Intellisens gespielt hat wird sich jetzt sicher fragen wofür wir Range eigentlich brauchen, schließlich haben die Worksheets ja die .Cells Eigenschaft. Die könnte man ja einfach mit z.b. wsQuelle.Cells[1,1] ansprechen?
Nun leider Jein, schreiben kann man so aber das wars. Wenn wir aus Zellen lesen wollen müssen wir das über .Range.Value2 tun. Auch vergleiche funktionieren nur mit .Range.Value2.
Dazu aber später mehr. Jetzt wollen wir erst mal eine oder zwei Dateien öffnen:
 try
 {
 exl1 = new Excel.Application();
 exl1.Visible = true;
 
 exl2 = new Excel.Application();
 exl2.Visible = true;
 
 
 // Quelldatei öffnen
 wbQuelle = (Excel._Workbook)(exl1.Workbooks.Open(
 TextBoxQuelldateiPfad.Text,
 ExcelKonstanten.UpdateLinks.DontUpdate,
 ExcelKonstanten.ReadOnly,
 ExcelKonstanten.Format.Nothing,
 "", // Passwort
 "", // WriteResPassword
 ExcelKonstanten.IgnoreReadOnlyRecommended,
 Excel.XlPlatform.xlWindows,       
 "", // Trennzeichen
 ExcelKonstanten.Editable,
 ExcelKonstanten.DontNotifiy,
 ExcelKonstanten.Converter.Default,
 ExcelKonstanten.DontAddToMru,
 ExcelKonstanten.Local,
 ExcelKonstanten.CorruptLoad.NormalLoad));
 
 // Zieldatei öffnen
 wbZiel = (Excel._Workbook)(exl2.Workbooks.Open(
 TextBoxZieldateiPfad.Text,
 ExcelKonstanten.UpdateLinks.DontUpdate,
 ExcelKonstanten.ReadWrite,
 ExcelKonstanten.Format.Nothing,
 "", // Passwort
 "", // WriteResPassword
 ExcelKonstanten.IgnoreReadOnlyRecommended,
 Excel.XlPlatform.xlWindows,
 "", // Trennzeichen
 ExcelKonstanten.Editable,
 ExcelKonstanten.DontNotifiy,
 ExcelKonstanten.Converter.Default,
 ExcelKonstanten.DontAddToMru,
 ExcelKonstanten.Local,
 ExcelKonstanten.CorruptLoad.NormalLoad));
 


Jetzt sollten wir noch einen Worksheet je Workbook auswählen. Hier gleich mal zwei möglickheiten, die erste öffnen den gerade aktiven Sheet die zweite den ersten Sheet im Workbook. (Ja der index beginnt hier bei 1)
Den aktiven Sheet zu benutzen hat einige vorteile, weis man z.b. nicht welchen Sheet man benutzen will kann man es einfach dem Nutz überlassen, er muss nur einen Sheet auswählen und die Datei speichern.

 // Arbeitsblätter öffnen                  
 wsQuelle = (Excel._Worksheet)wbQuelle.ActiveSheet;
 wsZiel = (Excel._Worksheet)wbZiel.Sheets[1];
 

Nun können wir schon in die Worksheets und deren Zellen schreiben zum lesen allerding müssen wir per Range darauf zugreifen. Also erst mal die beiden Range Objekte füllen  z.B. so:

cellQuelle = (Excel.Range)wsQuelle.get_Range("A1", Missing.Value);

Wenn wir aber sagen wir mal in einer Schleife auf Zellen zugreifen wollen dann benutzen wir am besten eine Funktion die uns numerische Adressen in Buchstaben-Zahlen Adressen umwandelt. Hier ist so eine Funktion, sie funktioniert aber nur bis 676. (Ich arbeite noch an einer Rekursiven Version)

/// <summary>
 /// Gibt eine Excel Adresse der Form A1 bis ZZ1 als Sting zurück.
 /// Zur verwendung mit Excel.Range gedacht.
 /// </summary>
 /// <param name="zeile">Index der Zeile</param>
 /// <param name="spalte">Index der Spalte</param>
 /// <returns></returns>
 private string ExlAdr(int zeile, int spalte)
 {
 char letter = 'A';
 char letter2 = '@'; // '@' ist das Zeichen vor 'A'

 int hilf = 0;

 if (spalte < 27)
 {
 for (int l = 1; l < spalte; l++)
 {
 letter++;
 }
 return letter + zeile.ToString();
 }
 else
 {
 for (int l = 1; l < spalte; l++)
 {
 hilf++;
 if (hilf == 26)
 {
 hilf = 0;
 letter2++;
 letter = '@';
 }
 letter++;
 }

 return letter2.ToString() + letter.ToString() + zeile.ToString();
 }
 }

Das sollte eigentlich für so ziemlich alle einfacheren Excel anwendungen genügen, sollte ihr noch Fragen haben schreibt mir einfach eine eMail.

 
Valid XHTML & CSS | Template Design ah-68 | Copyright © 2009 by Seishiroa