Daniel Hofer - Officehilfe.ch
Transcription
Daniel Hofer - Officehilfe.ch
Daniel Hofer Excel VBA Referenz © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 1 von 74 Seite 2 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Daniel Hofer Excel VBA Referenz © Bern, 2014 © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 3 von 74 Seite 4 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Impressum © 2014 Daniel Hofer Excel VBA-Referenz 978-3-906284-16-3 (PDF) 978-3-906284-13-2 (Print-Version A5) 978-3-906284-08-8 (Apple iBooks) Publiziert durch: Daniel Hofer, Bern, Schweiz E-Mail: [email protected] www.officehilfe.ch Alle Rechte vorbehalten. Die Verwendung der Texte und Bilder, auch auszugsweise, ist ohne die schriftliche Zustimmung des Autors und Verlags urheberrechtswidrig und strafbar. Das gilt insbesondere für die Vervielfältigung, Übersetzung, die Verwendung in Kursunterlagen oder elektronischen Systemen. Der Autor wie auch Verlag übernehmen keine Haftung für Folgen, die auf unvollständige oder fehlerhafte Angaben in diesem Buch zurückzuführen sind. Nahezu alle in diesem Buch behandelten Hard- und Softwarebezeichnungen sind zugleich eingetragene Warenzeichen. © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 5 von 74 Schreibkonventionen Die einzelnen Themen werden durch blaue Überschriften getrennt. Code-Beispiele werden grau schattiert dargestellt, zusätzlich erscheinen sie in der Schriftart „Courier New“. Hat eine Code-Zeile nicht auf einer Blatt-Zeile Platz, wird die Zeile mit einem _ umgebrochen, genau wie es im VB-Editor auch möglich ist. Einleitung Diese Kurz-Referenz zu Excel VBA entstand im Rahmen diverser Excel 2007 VBAKurses bei der Firma Digicomp Academy AG. Diese Referenz wird laufend überarbeitet. Neu besprochene Themen werden ergänzt. Fehler und Änderungswünsche melden Sie bitte unter [email protected]. Unterdessen enthält diese Referenz Infos zu sämtlichen gängigen Excel-Versionen. Passt die Info nur zu einer spezifischen Excel-Version, wird dies im entsprechenden Kapitel vermerkt. Letzte Änderungen Version Anpassungen 2.5 Import von verschiedenen Quellen (Textfiles, DBase) Zugriff auf Access-DB’s mittels DAO und ADO 2.6 Anpassungen der Homepages Neues Kapitel „Automation“ hinzugefügt 2.7 Fehlerkorrektur in Import-Kapitel Öffnen von Excel-Dateien im Allgemeinen Öffnen-Dialog inkl. Mehrfachauswahl Komplexer Import von acht Excel-Dateien (Anfügen der Daten untereinander) 2.8 Add-Ins aktualisiert Bild einfügen 2.9 Index am Ende des Dokuments eingefügt, div. Anpassungen, WITH, Textfunktionen, Binärer vs. Textvergleich, MsgBox in 2 Varianten, Zellen auf Gültigkeit prüfen 3.0 Fehlerbereinigungen 3.1 Datums-Funktionen, Formulare 3.2 Fehlerkorrekturen, Makrorekorder, Outlook öffnen und Email versenden geplant Pivot, relative vs. absolute Aufzeichnung Seite 6 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Inhalt Impressum ................................................................................................................. 5 Schreibkonventionen ................................................................................................. 6 Einleitung ................................................................................................................... 6 Letzte Änderungen ..................................................................................................... 6 Excel 2007/2010 Special ............................................................................................. 9 Zugriff auf Zellen und Bereiche ................................................................................ 11 Nur einen Teil eines Zellinhaltes (Text) farbig markieren ...................................... 12 Autofilter, Formeln ............................................................................................... 13 Suchen und Finden im Range-Objekt .................................................................... 13 Spezielle Zellen ..................................................................................................... 14 Zelleigenschaften ................................................................................................. 15 Zugriff auf Spalten oder Zeilen ............................................................................. 15 Performance-Vergleich ......................................................................................... 16 Der Makrorekorder .............................................................................................. 18 Tabellenblätter / Dateien ......................................................................................... 22 Excel-Dokumente speichern ................................................................................. 22 Excel-Dateien öffnen ............................................................................................ 23 Öffnen-Dialog anzeigen ........................................................................................ 24 Öffnen-Dialog und darin mehere Dateien auswählen ........................................... 25 Neue Excel-Mappe erstellen ................................................................................. 25 VBA-Grundlagen....................................................................................................... 26 Variablen .............................................................................................................. 26 Aufzählungen (Enumerationen) ............................................................................ 28 Funktionen ........................................................................................................... 29 Gültigkeitsbereiche............................................................................................... 30 Arrays ................................................................................................................... 31 WITH .................................................................................................................... 32 MsgBox ................................................................................................................. 33 vb-Konstanten ...................................................................................................... 34 Schleifen ............................................................................................................... 35 Fehler-Behandlung (z.B. InputBox) ....................................................................... 37 Text-Funktionen ................................................................................................... 42 Datums-Funktionen .............................................................................................. 44 © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 7 von 74 Zellen auf Gültigkeit überprüfen .......................................................................... 45 Formulare in Excel.................................................................................................... 48 Aufruf eines Formulars ......................................................................................... 48 Schliessen des Formulars, Variante 1 ................................................................... 49 Schliessen des Formulars, Variante 2 ................................................................... 49 Parameter-Übergabe mittels globaler Variable .................................................... 49 Textfeld nur zur Eingabe von Zahlen definieren ................................................... 50 Rotes Schliessen-Kreuz ignorieren ........................................................................ 50 Import aus anderen Quellen .................................................................................... 51 Einlesen einer Textdatei ....................................................................................... 51 Excel-Datei öffnen und Daten daraus kopieren .................................................... 52 Komplexe Datenübername aus mehreren Excel-Dateien ..................................... 53 Zugriff auf Access mittels DAO ............................................................................. 54 Einlesen einer dBase-Tabelle über ADO ............................................................... 56 Bild in Excel einfügen............................................................................................ 57 Diverses ................................................................................................................... 58 Verweise, Early-Binding und Late-Binding ............................................................ 58 Automation (Steuerung anderer Applikationen) .................................................. 59 Tabellenfunktionen .............................................................................................. 61 Zugriff auf das System .......................................................................................... 62 Einstellungen VBA-Editor...................................................................................... 62 Homepages zu Excel ................................................................................................ 67 Allgemein zu Office und VBA ................................................................................ 67 Excel ..................................................................................................................... 67 Homepages zu Access .......................................................................................... 67 Ribbons .................................................................................................................... 68 Datentypen .............................................................................................................. 72 Index ........................................................................................................................ 73 Seite 8 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Excel 2007/2010 Special Excel 2007/2010 Special Limitationen, die wichtigsten Änderungen Spalten Zeilen Sortierschlüssel Farben Zellformate Verschachtelungen in Funktionen Zeichen in Formeln Bedingungen von bedingten Formatierungen Argumente von Funktionen Elemente von Autofilter-Auswahllisten 16‘384 1‘048‘576 64 4‘294‘967‘296 65‘536 64 8‘192 Unbeschränkt 256 65‘536 3 56 Ca. 4‘000 7 1‘024 3 255 10‘000 30 1‘000 Quelle: www.xlam.ch Dateiendungen XLSX: „normale“ Excel-Datei ohne Makros (XML-Format) XLSM: Datei mit Makro (XML-Format XLTX: Vorlage ohne Makro (XML-Format XLTM: Vorlage mit Makros (XML-Format XLSB: Binäres Dateiformat (z.B. Personal.xlsb) XLAM: Add-In-Datei (siehe Kapitel „Zugriff auf Funktionen in Add-In Dateien“ auf Seite 65) PERSONAL.XLSB, Pfad unter Vista: C:\Users\[user]\AppData\Roaming\Microsoft\Excel\XLSTART Objektmodell, was fehlt? Diverse Bereiche in Excel 2007 können nicht über das Objektmodell angesprochen werden. Dies sind u.a. das Ribbon (die neuen Buttons) SmartArt-Grafiken FileSearch-Objekt (siehe "Scripting Runtime im Kapitel "Diverses") Schnellzugriffsleiste Erweiterte Zwischenablage © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 9 von 74 Excel 2007/2010 Special Was fehlt sonst noch? Smart Tags können nicht mit VBA erstellt werden ebenfalls Aufgabenbereiche (rechte Spalte) fehlen Gewisse Abhilfe schafft hier der Einsatz von: Visual Studio Tools for Office (VSTO) Probleme in Excel 2007 Steuerelemente direkt auf dem Tabellenblatt verursachen häufig unnachvollziehbare Fehler Logitech-Maus-Rad funktioniert in der VBA-Umgebung u.U. nicht Seite 10 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Zugriff auf Zellen und Bereiche Zugriff auf Zellen und Bereiche Wert 6.3 wird in Zelle A1 geschrieben Range("A1").Value = 6.3 Farbe des Bereichs „Testbereich“ wird gesetzt Range("Testbereich").Interior.ColorIndex = 7 Achtung! Der Verweis auf H21 stimmt nur in der Zelle A1. In den anderen wird er jeweils angepasst. Range("A1:B17").FormulaLocal = "=H21" Leicht andere Syntax Range("B4", "C10").Clear Zelle mit der Adresse A20 (Zeile 20, Spalte 1) Cells(20, 1).Interior.ColorIndex = 9 Bereich A20 bis B21 wird eingefärbt Range(Cells(20, 1), Cells(21, 2)).Interior.ColorIndex = 9 Range-Aufruf korrekt Worksheets(„Tabelle1“).Range(„B4“) Der Wert wird nicht aus der aktuellen Zelle, sondern aus einer darunter genommen vAlter = ActiveCell.Offset(1, 0) Aktueller Bereich um die aktuelle Zelle markieren ActiveCell.CurrentRegion.Select Erste Zeile in der „CurrentRegion“ ActiveCell.CurrentRegion.Row Anzahl Zeilen in der „CurrentRegion“ ActiveCell.CurrentRegion.Rows.Count Letzte Zeile im Bereich „CurrentRegion“ ActiveCell.CurrentRegion.Rows.Count – 1 Spalte A im Bereich „CurrentRegion“ markieren vRows = Range("a1").CurrentRegion.Rows.Count vRange = "A1:A" & vRows Range(vRange).Select © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 11 von 74 Zugriff auf Zellen und Bereiche Sämtliche Daten markieren ActiveSheet.UsedRange.Select Relative Bezüge Die aktive Zelle ist als Beispiel „I22“. Führt man folgende Codezeile aus, steht dann in dieser Zelle „=H21“ ActiveCell.FormulaR1C1 = "=R[-1]C[-1]" Alle Zeilen markieren, welche einen Kommentar enthalten Selection.SpecialCells(xlCellTypeComments).Select Nur einen Teil eines Zellinhaltes (Text) farbig markieren Cells(2,2).Characters(Start:=7, Length:=3).Font.ColorIndex=3 Seite 12 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Zugriff auf Zellen und Bereiche Autofilter, Formeln Autofilter ein- oder ausschalten ActiveCell.AutoFilter Autofilter der Spalte 3 Selection.AutoFilter Field:=3, Criteria1:="<40" Autofilter, die grössten 2 Werte Selection.AutoFilter Field:=3,Criteria1:="2", _ Operator:=xlTop10Items Datum formatieren vDatum = Format(Now(), "dd. mmmm yyyy") CHF-Formatierung von Zahlen Selection.NumberFormat = """CHF ""#,##0.00" Summenformel in VBA Dim rngArea As Range Set rngArea = Range(Cells(1, 1), Cells(5, 1)) Selection.Value = _ Application.WorksheetFunction.Sum(rngArea) Summenformel in Formula-Eigenschaft der Zelle schreiben Dim vSummeFormel vSummeFormel = "=SUM(r" & 1 & "c" & 1 & ":" & "r5c1)" Selection.Formula = vSummeFormel Resultat im Sheet: =SUMME($A$1: $A$5) Mit VBA einen Bereich summieren Wollen Sie direkt in VBA die Zellen A1 bis A10 addieren, suchen Sie vergebens eine VBA-Funktion, welche das erledigt. Hier müssen Sie auf die Excel-Funktion ausweisen: vSumme = Application.WorksheetFunction.Sum(Range("B15:B18")) Suchen und Finden im Range-Objekt Einfachste Variante, um nach dem Text „Datum“ zu suchen in der aktuellen Markierung Problem: Wird nichts gefunden, erscheint eine Fehlermeldung Selection.Find("Datum").Select © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 13 von 74 Zugriff auf Zellen und Bereiche Bessere Suchfunktion Dim rng As Range Set rng = Selection.Find("Datum") If rng Is Nothing Then MsgBox "Nix gefunden" Else rng.Select End If Noch besser: Der Text „Datum“ kann nur als Teil in einer Zelle vorkommen. Anstelle gleich auf den gefunden Text zu springen (mit .Select) kann dieser Bereich in eine Variable gelesen und weiterverarbeitet werden Dim rng As Range Set rng = Selection.Find("Datum", _ , , xlPart, , , False) If rng Is Nothing Then MsgBox "Nix gefunden" Else rng.Select End If Spezielle Zellen Max. Anzahl Zeilen ActiveSheet.Rows.Count Max. Anzahl Spalten ActiveSheet.Columns.Count Sprung zur letzten Zelle, welche entweder in der Spalte oder in der Zeile einen Inhalt hat Selection.SpecialCells(xlCellTypeLastCell).Select Achtung! Diese Funktion ist nicht zuverlässig! Mögliche Lösungen stehen hier: http://www.vb-fun.de/cgi-bin/loadframe.pl?ID=vb/tipps/tip0342.shtml Ergänzung 3.2.2008: Diese Methode funktioniert, wenn das Dokument vorgängig gespeichert wird, z.B. mit ActiveWorkbook.Save Seite 14 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Zugriff auf Zellen und Bereiche Zelleigenschaften Aktuelle Zelle farbig hinterlegen ActiveCell.Interior.ColorIndex = 6 Adresse der entsprechenden Zelle erfahren ActiveCell.Address ergibt " $A$8". Sollen die Dollarzeichen entfernt werden, geben wir ein: Debug.Print ActiveCell.Address(False, False) (ergibt "A8") Zugriff auf Spalten oder Zeilen Löschen von Spalten: Columns("B:D").Delete Range(„B:D“).Delete Das Gleiche gilt für Zeilen: Rows(„1:3“).Delete Range(„1:3“).Delete © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 15 von 74 Zugriff auf Zellen und Bereiche Performance-Vergleich Im Folgenden schauen wir nur ganz kurz die Performance an, wenn wir in 1000 Zeilen und 100 Spalten die Zahl 3 eintragen wollen. Schlechteste Variante mit Cell.Select For vSpalte = 1 to 100 For vZeile = 1 to 1000 Cells(vZeile,vSpalte).Select Selection.Value = 3 Next vZeile Next vSpalte Dieses Beispiel hat rund 3min zum Durchlaufen Dito, aber mit Application.ScreenUpdating = False Application.ScreenUpdating = False For vSpalte = 1 to 100 For vZeile = 1 to 1000 Cells(vZeile, vSpalte).Select Selection.Value = 3 Next vZeile Next vSpalte Application.ScreenUpdating = True Dieses Beispiel hat ca. 17 Sekunden! Auf Cell.Select verzichten For vSpalte = 1 to 100 For vZeile = 1 to 1000 Cells(vZeile, vSpalte).Value = 3 Next vZeile Next vSpalte Diese Version hat 19 Sekunden. Ist also interessanterweise 2 Sekunden langsamer als das „schlechte“ Select, wenn dort ScreenUpdating = False definiert ist Auf Cell.Select verzichten, ScreenUpdating ausschalten Application.ScreenUpdating = False For vSpalte = 1 to 100 For vZeile = 1 to 1000 Cells(vZeile, vSpalte).Value = 3 Next vZeile Next vSpalte Application.ScreenUpdating = True Seite 16 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Zugriff auf Zellen und Bereiche Diese Version ist noch ein wenig schneller, und zwar braucht sie rund 6-7 Sekunden. Gleichzeitig mehrere Excel-Dateien offen Vorsicht! Sind zwei Excel-Dateien geöffnet (und egal, ob die andere VBA-Code enthält oder nicht), dann wird die Abarbeitung des Codes extrem viel langsamer! Also bei Zeit-intensiven Abläufen immer nur eine Datei geöffnet haben. Vergleich IF und SELECT CASE If x = 1 then… If x = 2 then… If x = 3 then ist rund doppelt so langsam wie: Select Case x Case 1: … Case 2: … Case 3: … End Select Wobei folgende Anweisung im schlechtesten Fall gleich schnell wie die SELECTAnweisung, aber im besten Fall doppelt so schnell wie diese ist: If x = 1 then… Elseif x = 2 then… Elseif x = 3 then… Variablenvergleich If intVariable = 32 then blnTest = True Else blnTest = False End If ist rund doppelt so langsam wie: blnTest = (intVariable = 32) Boolean-Variable drehen Eine nicht selten gesehendes Szenario ist: If blnTest = True then blnTest = False Else blnTest = True End if Doppelt so schnell geht’s mit © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 17 von 74 Zugriff auf Zellen und Bereiche blnTest = not blnTest Potenzieren vTest = vTest^2 ist sage und schreibe rund 5x (fünf!) langsamer als: vTest=vTest*vTest Variant wenn möglich vermeiden Der Datentyp Variant unter VBA sollte wenn möglich vermieden werden. Dies ist allerdings nicht immer möglich, z.B. beim Abfangen der Inputbox auf Seite 39. Der Makrorekorder Der Makrorekorder von Excel ist einerseits in Programmierkreisen verpönt, andererseits wird er von VBA-Einsteigern häufig eingesetzt. Ich zeigen Ihnen hier, welcher Einsatzzweck des Rekorders Sinn macht, und wie Sie häufige Probleme beheben können. Unsinniger Einsatz des Rekorders Sie zeichnen einen Ablauf mit dem Rekorder auf, und weisen dem Makro am Schluss noch einen Button zu. Danach verwenden Sie das Makro blind, ohne es zu vorgängig zu kontrollieren. Dies ist die übliche Art, wie Einsteigerinnen und Einsteiger mit Makros umgehen. Einfach etwas aufzeichnen und dies dann ungefragt einsetzen. Das wird nie gut kommen. Sie müssen immer Ihr aufgezeichnetes Makro nachträglich kontrollieren und unsinnige Teile rauslöschen. Sinnvolle Einsatzmöglichkeiten des Rekorders Sie wissen als Beispiel nicht, wie man die Hintergrundfarbe auf ein schönes Rot ändert. Dazu verwenden Sie den Rekorder: Starten Sie den Rekorder mit den Standardeinstellungen: Seite 18 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Zugriff auf Zellen und Bereiche Ändern Sie die Hintergrundfarbe auf rot: Stoppen Sie sofort den Makrorekorder wieder Jetzt wechseln Sie in den VBA-Editor und suchen das aufgezeichnete Makro: In meinem Fall hat Excel dem Makro resp. der Prozedur den Namen Makro1 gegeben. Und Sie sehen, dass hier ziemlich viel drin steht. Das Einzige, was uns aber interessiert, ist die Eigenschaft Color = 255 (Hinweise zu WITH siehe Seite 32). Sie können diese Eigenschaft kopieren resp. sich merken und an der gewünschten Stelle in Ihrem anderen Code fügen Sie diese Eigenschaft so ein. Als Beispiel: Selection.Interior.Color = 255 oder als Alternative: ActiveCell.Interior.Color = 255 Oder Sie greifen direkt auf eine Zelle zu: Range(„A1“).Interior.Color = 255 resp. Cells(1,1).Interior.Color = 255 © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 19 von 74 Zugriff auf Zellen und Bereiche Nachträgliche Korrekturen nach dem Aufzeichnen Möchten Sie den Makrorekorder nicht nur dazu verwenden, um rasch rauszufinden, wie man wohl die Hintergrundfarbe ändert, sondern vielleicht auch mehr damit anstellen, dann ist es zwingend, dass Sie den Code kontrollieren. Beispiel 1 Bereits beim obigen Beispiel, als Sie lediglich die Hintergrundfarbe auf rot geändert haben, zeichnete der Makrorekorder viel mehr auf: With Selection.Interior .Pattern = xlSolid .PatternColorIndex = xlAutomatic .Color = 255 .TintAndShade = 0 .PatternTintAndShade = 0 End With Hier müssen Sie realisieren, dass Sie eben nur die Zeile mit der Eigenschaft Color benötigen. Beispiel 2 Ein schlimmeres Beispiel zeigt das Einfügen einer Rahmenlinie unten: Zeichnen Sie nämlich den Klick darauf auf, dann sieht der Code wie folgt aus: Selection.Borders(xlDiagonalDown).LineStyle = xlNone Selection.Borders(xlDiagonalUp).LineStyle = xlNone Selection.Borders(xlEdgeLeft).LineStyle = xlNone Selection.Borders(xlEdgeTop).LineStyle = xlNone With Selection.Borders(xlEdgeBottom) .LineStyle = xlContinuous .ColorIndex = 0 .TintAndShade = 0 .Weight = xlThin End With Selection.Borders(xlEdgeRight).LineStyle = xlNone Selection.Borders(xlInsideVertical).LineStyle = xlNone Selection.Borders(xlInsideHorizontal).LineStyle = xlNone Seite 20 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Zugriff auf Zellen und Bereiche Und genau hier haben wir ein Problem. Wir haben lediglich eine Rahmenlinie unten einfügen wollen. Aber mit diesem Code werden sämtliche andere Rahmenlinien in einer Zelle ausgeschaltet. Dies wollten wir aber nicht. Stellen Sie sich vor, Sie hätten bereits eine Zelle mit einer Rahmenlinie links und oben: Führen Sie den aufgezeichneten Code aus, entfernt Ihnen das Makro diese beiden Rahmenlinien. Das war aber ganz und gar nicht das, was wir wollten. Deshalb müssen Sie auch hier das Makro anpassen, damit nur noch die Zeilen übrig bleiben, die Sie wollen. Also hier als Beispiel: With Selection.Borders(xlEdgeBottom) .LineStyle = xlContinuous .ColorIndex = 0 .TintAndShade = 0 .Weight = xlThin End With Beispiel 3 Fügen Sie mittels Makrorekorder unter Excel 2013 ein Diagramm ein, wird folgender (eigentlich sehr schlanker) Code erzeugt: ActiveSheet.Shapes.AddChart2(201, xlColumnClustered).Select ActiveChart.SetSourceData Source:=Range("Tabelle2!$B$2:$G$6") Schauen Sie die zweite Code-Zeile an. Da müssen Sie definitiv den Bereich für eine spätere Verwendung anpassen. © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 21 von 74 Tabellenblätter / Dateien Tabellenblätter / Dateien Neues Tabellenblatt hinzufügen (standardmässig VOR dem aktuellen) Fokus wird auf das neue Sheet gewechselt Worksheets.Add Neues Tabellenblatt hinzufügen (NACH dem aktuellen) Fokus wird auf das neue Sheet gewechselt Worksheets.Add , ActiveSheet Namen des aktuellen Sheets ändern ActiveSheet.Name = "Gugus" Neues Sheet, danach Fokus wieder auf das alte Sheet zurück Dim wks As Worksheet Set wks = ActiveSheet Worksheets.Add , ActiveSheet ActiveSheet.Name = "Gugus1" wks.Select Worksheets löschen Dim wks As Worksheet For Each wks In Worksheets wks.Delete Next Worksheets löschen Warnmeldung ist deaktiviert. Das letzte Worksheet wird nicht gelöscht ( Fehlermeldung) Dim wks As Worksheet Application.DisplayAlerts = False For Each wks In Worksheets If Worksheets.Count = 1 Then Exit Sub wks.Delete Next Application.DisplayAlerts = True Excel-Dokumente speichern Anzeigen eines Speichern-Dialogs Dafür gibt es 2 Möglichkeiten: Application.Dialogs(xlDialogSave).Show dies ist die schlechte Variante. Beim Klick auf den Button "Speichern" wird die Datei effektiv gerade physisch gespeichert Die bessere Variante: Seite 22 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Tabellenblätter / Dateien vFile = _ Application.GetSaveAsFilename(FileFilter:="Microsoft Excel-Dateien (*.xl*), *.xl*") If vFile <> False then MsgBox vFile End If Die Methode "GetSaveAsFilename" des Application-Objekts gibt lediglich den Namen zurück. Was mit diesem Namen danach gemacht wird, müssen WIR entscheiden. Speichern der aktuellen Datei Da Office 2007 im Application-Objekt keine FileSearch-Methode mehr kennt, müssen wir auf die Scripting Runtime Bibliothek integrieren (siehe "Scripting Runtime" im Kapitel "Diverses") Dim vFS As New FileSystemObject On Error Resume Next vFS.CreateFolder "C:\hallo" On Error GoTo 0 If vFS.FileExists("c:\hallo\gugus.xlsm") = False Then ActiveWorkbook.SaveAs "c:\hallo\gugus.xlsm", xlOpenXMLWorkbookMacroEnabled Else ActiveWorkbook.Save End If Achtung! Microsoft Access bietet im Application-Objekt weder eine Auflistung „Dialogs“ noch eine Methode „GetSaveAsFilename“. Der Grund liegt darin, dass eine Access-Datenbank nicht im gleichen Sinne gespeichert wird wie ein Word- oder Excel-Dokument. In Access will man häufig die Datenbank resp. die komplette Applikation schliessen, und dabei gleich speichern. Das würde so aussehen: Application.Quit acQuitSaveAll Excel-Dateien öffnen In der einfachsten Variante wird ein einzelnes Excel-Dokument wie folgt geöffnet: Workbooks.Open(Filename:="D:\Daten\Kunden1.xlsx") Jetzt haben wir aber meist ein Problem: wir haben 2 Dateien gleichzeitig offen. Was geschieht nun, wenn wir z.B. ein Cells(1,1) = „Hallo“ schreiben? In welche Datei und in welches Tabellenblatt schreibt das VBA rein? © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 23 von 74 Tabellenblätter / Dateien Ganz einfach: in das gerade offene und aktive. Und genau das wollen wir häufig nicht! Aus diesem Grund müssen wir bei jedem Zugriff auf ein Excel-Objekt die Mappe und das Tabellenblatt sauber angeben. Also anstelle Cells(1,1) = „Hallo“ schreiben wir: Workbooks(„Kunden1.xlsx“).Worksheets(„Tabelle1“).Cells( 1,1) = „Hallo“ Korrekterweise sollten wir nicht einfach eine Excel-Mappe öffnen, sondern diese gleich in einer entsprechenden Variable speichern: Dim objSource As Workbook Set objSource = Workbooks.Open(Filename:="D:\Daten\Kunden1.xlsx") Ab sofort können wir auf die Variable objSource zugreifen: objSource.Sheets("Tabelle1").Cells(1,1) = „Hallo“ Nach unseren Arbeiten wollen wir vielleicht die Datei speichern: objSource.Save Oder wir wollen sie schliessen ohne zu speichern: objSource.Close False Damit dies aber keine lästige Nachfrage bringt, schalten wir die Warnmeldungen kurz aus: Application.DisplayAlerts = False objSource.Close False Application.DisplayAlerts = True Achtung! Diese gleich wieder einschalten danach! Sonst wird’s gefährlich! Öffnen-Dialog anzeigen Der Öffnen-Dialog kriegen wir so hin: Dim vFile As String vFile = Application.GetOpenFilename() Diese Variante lässt nur die Auswahl einer einzigen Datei. Seite 24 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Tabellenblätter / Dateien Öffnen-Dialog und darin mehere Dateien auswählen Wollen wir im Öffnen-Dialog gleich mehere Dateien auswählen, dann sieht der Code so aus: (diesmal als vollständige Prozedur) Beachte, dass diesmal vFile als Variant deklariert ist. Die Methode GetOpenFilename kann nämlich entweder ein FALSE zurückgeben, falls man den Dialog abbricht, oder aber ein Array von Einträgen (siehe Kapitel Arrays, Seite 31) Neue Excel-Mappe erstellen Eine Excel-Mappe lässt sich ganz einfach mit Workbooks.Add erstellen. Aber vorsicht! Bitte nie so machen. Excel öffnet dann zwar eine neue leere Arbeitsmappe und aktiviert diese. Aber wir haben mit VBA dann keinen sauberen Zugriff mehr drauf. Plötzlich machen wir mittels VBA Änderungen in der falschen Mappe (und das wäre ärgerlich…)! Besser ist es, gleich eine Objektvariable sauber zu definieren: Dim vWB As Workbook Set vWB = Workbooks.Add Jetzt haben wir sauber eine Variable vWB, mit welcher wir arbeiten können. Es ist nämlich einen himmelweiten Unterschied, ob wir unsere neue Mappe so speichern: ActiveWorkbook.Save oder so: vWB.Save Bei der ersten Version hoffen wir einfach, dass die gerade aktive Mappe die vorher neu erstellte ist. Bei der zweiten Version wissen wir, was wir tun. © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 25 von 74 VBA-Grundlagen VBA-Grundlagen Variablen Variablen sind global definiert Das heisst, sie sind erreichbar von beiden Prozeduren Prozedur „Subtrahiere“ überschreibt den Wert von C! Dim a, b, c As Integer Sub Addiere() a = 1 b = 3 c = a + b Subtrahiere End Sub Sub Subtrahiere() c = a - b End Sub Variablen sind nur innerhalb der Prozedur „Addiere“ sichtbar. Sie müssten in der Prozedur „Subtrahiere“ neu definiert werden Kein Überschreiben der Variablen möglich Sub Addiere() Dim a, b, c As Integer a = 1 b = 3 c = a + b Call Subtrahiere End Sub vUebergabe in „Transfer“ wird durch „Addiere“ überschrieben Variablenübergabe standardmässig „ByRef“ Sub Transfer() Dim vUebergabe As Integer vUebergabe = 10 Addiere vUebergabe Debug.Print vUebergabe End Sub Sub Addiere(vEmpfange As Integer) Seite 26 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer VBA-Grundlagen vEmpfange = vEmpfange + 5 End Sub vUebergabe bleibt bis am Schluss 10, da die Variable als Wert, und nicht als Pointer resp. als Referenz auf die entsprechende Speicherstelle, übergeben wird. Sub Transfer() Dim vUebergabe As Integer vUebergabe = 10 Addiere vUebergabe Debug.Print vUebergabe End Sub Sub Addiere(ByVal vEmpfange As Integer) vEmpfange = vEmpfange + 5 End Sub ByRef wäre normal, allerdings wird der Parameter der Prozedur in Klammern aufgerufen. Somit findet eine ByVal-Übergabe statt vUebergabe wird nicht überschrieben Sub Transfer() Dim vUebergabe As Integer vUebergabe = 10 Addiere (vUebergabe) Debug.Print vUebergabe End Sub Sub Addiere(vEmpfange As Integer) vEmpfange = vEmpfange + 5 End Sub Wird zusätzlich zu den eckigen Klammern der Aufruf mittels dem Schlüsselwort „CALL“ gemacht, findet das Ganzw wieder ByRef statt. vUebergabe wird überschrieben Sub Transfer() Dim vUebergabe As Integer vUebergabe = 10 Call Addiere(vUebergabe) Debug.Print vUebergabe End Sub Sub Addiere(vEmpfange As Integer) vEmpfange = vEmpfange + 5 © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 27 von 74 VBA-Grundlagen End Sub Variablendeklaration korrekt Dim vGanzeZahl as integer Variablendeklarationen falsch Fehlt der Typ, wird automatisch „Variant“ genommen Dim vGanzeZahl Dim vGanzeZahl% Abkürzung für Integer Dim vNachname Dim vNachname$ Abkürzung für String Warum nicht Variant als Typ einsetzen? Integer benötigt 2 Bytes Double benötigt 8 Bytes Variant benötigt in JEDEM Fall 16 Bytes Aufzählungen (Enumerationen) Definition Enum SchuelerQualitaet sqSaumässigSchlecht = 1 sqNaEsGehtSo = 2 sqGarNichtMalSoSchlecht = 3 sqWowDerTypHatsDrauf = 4 End Enum Sub Transfer() Dim vSchuelerQualitaet As SchuelerQualitaet vSchuelerQualitaet = sqGarNichtMalSoSchlecht End Sub Ansicht im Code bei der Variablen-Deklaration Seite 28 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer VBA-Grundlagen Ansicht im Code bei der Variablenzuweisung Konstanten Konstanten, falsch definiert Const mwst = 0.076 Konstanten, korrekt definiert Konstanten-Name GROSS Konstante mit dem korrekten Typ deklarieren! Const MWST as double = 0.076 Funktionen Die Funktion überschreibt die Variable der Prozedur „Transfer1“. Dies ist sehr schlecht! Das darf so nicht geschehen. Sub Transfer1() Dim vErsteZahl As Double, vZweiteZahl As Double, vResultat As Double vErsteZahl = 55 vZweiteZahl = 35 Debug.Print "vErsteZahl: " & vErsteZahl Debug.Print "vZweiteZahl: " & vZweiteZahl vResultat = fMittelwert(vErsteZahl, vZweiteZahl) Debug.Print "vErsteZahl: " & vErsteZahl Debug.Print "vZweiteZahl: " & vZweiteZahl End Sub Function fMittelwert(vZahl1 As Double, vZahl2 As Double) As Double fMittelwert = (vZahl1 + vZahl2) / 2 vZahl1 = 99 End Function © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 29 von 74 VBA-Grundlagen Funktionsdeklaration korrekt Funktionsname mit Sinn und „f“ als Präfix vGebDat ist korrekt deklariert Rückgabewert ist korrekt deklariert Function fGetAlter(vGebDat as date) as integer End Function Funktionsdeklaration falsch Präfix „int“ soll entweder bei Variablen ODER bei Funktionen verwendet werden, aber nicht an beiden Orten Die Typendeklaration mit % ist unschön und nicht leserlich Function intGanz%(a%) End Function Optionale Parameter Der dritte Parameter wird als optional definiert Function fMittelwert(vZahl1 As Double, vZahl2 As _ Double, Optional vZahl3 as Double) As Double Andere Reihenfolge der Parameter Durch die Angabe des Parameter-Namens plus „:=“ können die Parameter beliebig in der Reihenfolge gewählt werden Anstelle vResultat = fMittelwert(vErsteZahl, vZweiteZahl) können die Parameter benannt, und entsprechend die Reihenfolge vertauscht werden: vResultat = fMittelwert(vZahl2:=vZweiteZahl, vZahl1:=vErsteZahl) Gültigkeitsbereiche Variable ist im gleichen Modul von allen Funktionen und Prozeduren aus zu sehen und änderbar Aus einem anderen Modul ist die Variable allerdings nicht sichtbar Dim vAlter as integer Modul1: Sub pTestprozedur() Seite 30 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer VBA-Grundlagen End Sub Sub pTestprozedur2() End Sub Variable ist durch die Kennung „Global“ aus allen Modulen sichtbar Global vAlter as integer Modul1: Sub pTestprozedur() Modul2: Sub pTestprozedur2() End Sub End Sub Falsche Deklaration Eine „Global“-Deklaration innerhalb einer Prozedur ist nicht möglich, und führt entsprechend zu einem Fehler Sub pTestprozedur() Global vAlter as integer End Sub „Private“ macht, dass die Prozedur in der Excel-Makroliste nicht erscheint Private Sub Workbook_Open() End Sub Arrays Arrays werden in VBA als "Felder" bezeichnet. Deklaration einer fixen Grösse Dim vMonate(12) As String Dim i As Integer For i = 1 To 12 vMonate(i) = Format(DateSerial(2008, i, 1), "MMMM") Next Diese Variante funktioniert, wenn wir die Grösse eines Arrays fix vorhersagen können. © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 31 von 74 VBA-Grundlagen Deklaration einer unbestimmten Grösse Häufig wissen wir nicht, wie gross ein Array wird. In diesem Fall müssen wir die Grösse bei der Deklaration nicht bestimmen. Vor dem eigentlichen Gebrauch der Variablen müssen wir die Grösse allerdings angeben. Dim a() as integer Beim ersten Gebrauch verwenden wir: Redim a(10) Achtung! Der Inhalt des Arrays wird gelöscht! Stellen wir fest, dass das Array noch zu klein war, definieren wir es neu, allerdings macht es Sinn, hier das Schlüsselwort "preserve" einzusetzen: Redim preserve a(20) So werden die bestehenden Einträge im Array nicht gelöscht. Array-Inhalt löschen Erase vMonate() Untere und obere Grenze eines Arrays ausgeben Debug.Print LBound(vMonate()) Debug.Print UBound(vMonate()) Unterer Arraywert beginnt bei 0 Definieren wir ein Array Dim a(10) as integer enthält dieses Array 11 Speicherplätze, da die Zählung bei 0 beginnt. Wollen wir dies verhindern, müssen wir in einem Modul zuoberst folgendes eintragen: Option Base 1 WITH Vor allem beim Aufzeichnen mit dem Makro-Rekorder entdeckt man häufig Code wie der folgende: With Selection.Interior .Pattern = xlSolid .PatternColorIndex = xlAutomatic .Color = 65535 .TintAndShade = 0 .PatternTintAndShade = 0 End With Dieser Codeblock dient lediglich als Abkürzung und besserer Lesbarkeit. Hätten wir das WITH nicht, müssten wir obigen Code so schreiben: Selection.Interior.Pattern = xlSolid Selection.Interior.PatternColorIndex = xlAutomatic Seite 32 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer VBA-Grundlagen Selection.Interior.Color = 65535 Selection.Interior.TintAndShade = 0 Selection.Interior.PatternTintAndShade = 0 Wir müssen also das Objekt Selection.Interior jedes Mal wiederholen. Wenn wir in einer Zelle resp. in einem Bereich viel anpassen, dann wird der Code recht unleserlich. Wir können jederzeit selber entscheiden, ob wir mittels WITH abkürzen wollen oder nicht. MsgBox Die Messagebox ist ein Windows Meldefenster, welches z.B. so aussehen kann: Wir benötigen dies immer, wenn wir mit unseren Benutzerinnen und Benutzer kommunizieren wollen. Das heisst, immer dann, wenn wir ihnen eine böse Fehlermeldung mitteilen wollen, oder sie auffordern wollen, etwas zu tun, oder wenn wir von ihnen eine Frage beantwortet haben möchten. MsgBox als Prozedur Der einfachste Aufruf sieht so aus: MsgBox "Bitte auf OK klicken" Dann erscheint obige Meldung. Als Variante können wir als zweiten Parameter z.B. ein Zeichen einblenden: MsgBox "Bitte auf OK klicken", vbCritical Nun erscheint die gleiche Meldung, aber: Wir haben bei dieser Art Messagebox keine Möglichkeit, auf die Eingabe der Anwender zu reagieren. Wir können zwar folgende Messagebox bringen: MsgBox "Willst du wirklich alle Daten löschen?", vbYesNo © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 33 von 74 VBA-Grundlagen Was uns folgendes Fenster beschert: Wir können aber nicht rausfinden, ob die Person JA oder NEIN geklickt hat. Und aus diesem Grund verwendet man die MsgBox häufig als Funktion anstelle Prozedur. MsgBox als Funktion Verwenden wir die MsgBox-Funktion, dann erwarten wir von ihr eine Antwort zurück. Dafür definieren wir zuerst eine Integer-Variable vResult. Entsprechend wird dann nach dem Aufruf der MsgBox-Funktion diese Variable mit einem Wert gefüllt: Dim vResult As Integer vResult = MsgBox("Willst du wirklich alle Daten löschen?", vbYesNo) Hat die Person auf JA geklickt, steckt in der Variable vResult der Wert 6 drin (das finden wir in der Online-Hilfe). Also können wir nach obigem Aufruf danach abfragen: If vResult = 6 Then Range("A:F").ClearContents End If vb-Konstanten Microsoft hat, damit man sich wie beim obigen Beispiel nicht solche Zahlen wie 6 für den Knopf JA merken muss, Konstanten vordefiniert. Diese beginnen alle mit den beiden Buchstaben vb… Obiges MsgBox-Beispiel wird sofort lesbarer, wenn wir eben mit einer solchen Konstante arbeiten können: If vResult = vbYes Then Range("A:F").ClearContents End If Seite 34 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer VBA-Grundlagen Schleifen Es gibt 3 Schleifentypen: For Next Do Loop While Wend For Next Diese Schleife wird verwendet, wenn die Anzahl der Durchgänge bekannt ist. For i = 1 to 12 Cells(i,1) = Format(DateSerial(2008, i, 1), "MMMM") Next i Diese Schleife kann mit Exit For verlassen werden. While Wend Diese Schleife wir bei Nichterfüllen der Bedinung NIE ausgeführt: While i < 13 Cells(i,1) = Format(DateSerial(2008, i, 1), "MMMM") i = i + 1 Wend Diese Schleife kann nicht verlassen werden! Do Loop Hier gibt es 2 Möglichkeiten: Bedingung am Anfang prüfen (bei Nichterfüllung kein Durchlaufen, identisch mit While Wend) Bedingung am Schluss prüfen (ein Durchlaufen ist zwingend) Bedingung am Anfang prüfen: Do While i < 13 Cells(1,i) = Format(DateSerial(2008, i, 1), "MMMM") i = i + 1 Loop Bedingung am Schluss prüfen Do Cells(i,1) = Format(DateSerial(2008, i, 1), "MMMM") i = i + 1 Loop Until I > 12 Do Loop Schleifen können verlassen werden. © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 35 von 74 VBA-Grundlagen : Performance-mässig wird entweder die For Next oder die Do Loop Schleife empfohlen Zellzugriff mit Range Wollen Sie obige Beispiele nicht mittels der Cells-Auflistung, sondern mit Range lösen, dann schreiben Sie das wie folgt (am Beispiel der For-Schleife): For i = 1 to 12 Range(„A“ & i) = Format(DateSerial(2008, i, 1), "MMMM") Next i Seite 36 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer VBA-Grundlagen Fehler-Behandlung (z.B. InputBox) Standard-Fehlerbehandlung in einer Prozedur Am Anfang einer Prozedur wird mit „On Error…“ definiert, was geschehen soll, wenn ein Laufzeit-Fehler auftaucht. Im folgenden Beispiel wird zur Sprungmarke „err_pTest“ gesprungen.1 Sub pTest On Error Goto err_pTest Debug.Print „Hallo“ goto exit_pTest wichtig! err_pTest: MsgBox err & “ “ & err.Description exit_pTest: End Sub (Achtung! die Zeile “goto exit_pTest“ muss sein, sonst wird die Fehlerbehandlung abgearbeitet, was wenig Sinn macht. Alternative zur „goto“-Anweisung Eine Alternative mit dem „goto exit_pTest“ ist folgende Variante: Sub pTest On Error Goto err_pTest Debug.Print „Hallo“ exit Sub err_pTest: MsgBox err & “ “ & err.Description ‘offizieller Fehler von MS MsgBox „Lieber User. Da ging leider was schief…“ End Sub Anstelle eines „goto“-Befehls wird die Prozedur einfach mit einem „exit Sub“ verlassen. Die Meinungen gehen hier auseinander, was besser ist. Ich finde grundsätzlich „Exit“-Anweisung nicht so toll. Andere finden dafür „Goto“Anweisungen schlecht. 1 auch hier macht eine saubere Namenskonvention Sinn! err als Präfix, danach der Name der Prozedur resp. Funktion. © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 37 von 74 VBA-Grundlagen Fehler ignorieren Sollen Fehler ignoriert werden, schreiben wir On Error resume next Achtung! Diese Zeile nicht am Anfang einer Prozedur einsetzen, sondern wirklich nur vor der Anweisung, bei der wir wissen, dass sie einen Fehler verursacht, und dies uns egal ist Sub pTest On Error Goto err_pTest Debug.Print „Hallo“ On Error Resume Next Debug.print 100/0 ‘erzeugt Fehler “Division durch 0 Sheets("Tabelle1").Delete ‘Vielleicht gibt’s die Tabelle gar nicht On Error Goto err_pTest ‘Muss sein!2 Debug.Print “Hallo” exit Sub err_pTest: MsgBox err & “ “ & err.Description End Sub Fehlerbehandlung auf VBA-Standard zurücksetzen Folgende Anweisung setzt die Fehlerbehandlung auf den Original-Zustand zurück: On Error goto 0 Vorsicht! Die benutzerdefinierte Fehlerbehandlung ist nun ausgeschaltet! Besser als Fehler ignorieren Obige Methode zum Ignorieren einer einzelnen Zeile ist zwar üblich, aber es geht noch ein wenig schöner. Und zwar, wenn wir erst in der FehlerBehandlung den Fehler abfragen: Sub pTest On Error Goto err_pTest Debug.Print „Hallo“ On Error Resume Next Debug.print 100/0 ‘erzeugt Fehler “Division durch 0 On Error Goto err_pTest Debug.Print “Hallo” 2 Nach der Fehler-trächtigen Zeile wollen wir unsere Fehler-Behandlung wieder aktivieren Seite 38 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer VBA-Grundlagen exit Sub err_pTest: If err = 11 then ‘Division durch 0 Resume Next Else MsgBox err & “ “ & err.Description End If End Sub So ignorieren wir nicht jeden beliebigen Fehler (was wir mit “On Error resume next” machen), sondern ignorieren lediglich den Laufzeitfehler 11. Dies ist wohl die sauberste Variante. InputBox sauber abfangen Das Aufrufen einer InputBox ist zwar äusserst bequem, aber sehr heikel. Erscheint nämlich folgende Box: Dann können die Anwenderinnen und Anwender nämlich irgend einen Blödsinn eingeben. Auch wenn wir vielleicht eine Zahl wünschen. Im Folgenden zeige ich drei Möglichkeiten auf, wie das Abfragen nach einer Zahl möglich ist. Der Probleme verursachende Code ist folgender: Falls ein String daher kommt, kriegen wir einen Laufzeitfehler. Version 1, mit der Funktion IsNumeric() © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 39 von 74 VBA-Grundlagen Wir benötigen zuerst eine neue Variable, welche als String definiert ist. Erst danach schauen wir, ob sich diese Variable in eine Zahl umwandeln lassen würde. Dies geschieht mit der Funktion IsNumeric(). Diese Version ist in der Regel die beste. Version 2, mit der Methode Application.InputBox Anstelle der Funktion InputBox() nehmen wir die Methode InputBox des Objekts Application: Hier sehen wir gelb markiert, dass man den Typ mitgeben kann. Type=1 heisst, Excel kontrolliert, dass man nur Zahlen eingibt. Hinweis: Weder Word noch Access kennen die Methode Inputbox des Application-Objekts! Es erscheint sonst automatisch folgende Meldung: ¨ Version 3, mit Error-Handling Dies ist die unschönste Version, aber manchmal trotzdem elegant: Seite 40 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer VBA-Grundlagen Mit der ersten rot markierten Zeile wird quasi die interne Error-Meldung von VBA ausgeschaltet. Danach produzieren wir einen Fehler, weil wir der Variablen vRadius einen String übergeben wollen (aber es erscheint keine interne Fehlermeldung durch unser Ausschalten). Nun fragen wir in der gelb markierten Zeile ab, ob eventuell der Fehler 13 aufgetreten ist (Typen unverträglich). Wenn ja, reagieren wir wieder entsprechend drauf. Wichtig! Das Error-Handling muss unbedingt wieder eingeschaltet werden! (zweite rot markierte Zeile) © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 41 von 74 VBA-Grundlagen Text-Funktionen Text-Funktionen in VBA werden sehr häufig benötigt. Unter Excel existieren auch solche Funktionen wie: TEIL LINKS RECHTS LAENGE etc. Haben wir als Beispiel eine Liste in Excel, welche eine Spalte mit Vor- und Nachnamen enthält: Wir möchten diese Spalte auftrennen in zwei separate Spalten, dann werden obige Funktionen häufig benötigt (wir können natürlich auch den Assistenten „Text in Spalten“ verwenden, aber ev. wollen wir ja nicht jeden Tag diese Arbeiten manuell durchführen, sondern einen gewissen Automatismus für uns arbeiten lassen. Unter VBA brauchen wir diese Funktionen für die gleichen Arbeiten. Vielleicht rattern wir ja die Excel-Liste mittels einer Schleife von oben nach unten durch und ändern das Ganze mittels einer VBA-Funktion. Warum mit VBA und nicht mit TEIL, LINKS, SUCHEN, etc.? Tabellenfunktionen in Excel werden häufig, sobald sie verschachtelt sind, extrem unübersichtlich. Ein VBA-Programm ist in den allermeisten Fällen viel lesbarer und übersichtlicher. Text-Funktionen im Überblick Für die folgenden Beispielen arbeiten wir mit folgenden Variablen: vName=“Müller Fritz“ vNachname = „“ vVorname = „“ vLaenge = 0 Unter VBA heissen diese Funktionen wie folgt: Funktion Beschreibung / Beispiel Seite 42 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer VBA-Grundlagen Funktion Left Right Len Mid Trim InStr InStrRev Beschreibung / Beispiel Gibt von einem String von links an gerechnet die angegebenen Zeichen zurück. vNachname = Left(vName,6) Gibt von einem String von rechts an gerechnet die angegebenen Zeichen zurück. vVorname = Right(vName,5) Gibt die Länge eines Strings zurück. vLaenge = Len(vNachname) Gibt von einem String gewisse Zeichen zurück. Im Gegensatz zu Left oder Right kann man hier irgendwo starten. Debug.Print Mid(vName, 3, 4) Ergibt: Entfernt in einem String die führenden resp. nachfolgenden Leerzeichen und gibt das Resultat als String zurück. vNameSauber = Trim(„ Hans Huber „) In vNameSauber steckt nun korrekt: „Hans Huber“ Sucht einen bestimmten Text und gibt die Position zurück. vPos = InStr(1, vName, " ", vbTextCompare) gibt die Zahl 7 zurück, weil der Leerschlag an Pos. 7 von links steckt. Gleich wie InStr, nur dass von rechts mit der Suche begonnen wird. Achtung! Die Syntax ist nicht gleich wie bei InStr. Hier beide Funktionen im Vergleich: Die erste beginnt ganz links mit der Suche, die zweite ganz rechts. Beide geben als Resultat die Zahl 7 zurück. Die Funktionen kombiniert Die Textfunktionen werden erst brauchbar, wenn man sie kombiniert. Um den Nachnamen rauszufiltern aus vName, ist ein Left(vName, 6) Blödsinn, weil der Nachname ja nicht immer 6 Zeichen lang ist. Haben wir aber vorher die Position des Leerschlages rausgefunden, dann haben wir eine variable Länge: © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 43 von 74 VBA-Grundlagen Dummerweise haben wir so in der Variablen vNachname am Ende noch ein Leerzeichen, weil vPos um ein Zeichen zu gross ist. Also korrigieren wir das noch: vbTextCompare, vbBinaryCompare Geben wir in der InStr- resp. InStrRev-Funktion vbTextCompare an, dann unterscheidet VBA Gross- und Kleinschreibung in der Suche nicht. vbBinaryCompare hingegen beachtet die Gross- und Kleinschreibung. Datums-Funktionen Ich zeige im Folgenden nur zwei eher unbekanntere Funktionen, welche in Excel gelegentlich benötigt werden: WeekdayName vWochentag = 1 MsgBox WeekdayName(vWochentag) Bringt entsprechend: MonthName vMonat = 3 MsgBox MonthName(vMonat) Bringt entsprechend: Seite 44 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer VBA-Grundlagen Bei beiden Funktionen muss allenfalls als weitere Parameter wie der erste Tage der Woche angegeben werden: Zellen auf Gültigkeit überprüfen Häufig müssen wir Zellen oder auch Variablen auf ihre Gültigkeit überprüfen. Ich spreche jetzt nicht davon, ob eine Zelle eine Zahl zwischen 10 und 20 enthält, sondern grundsätzlich, ob es eine Zahl ist, oder dummerweise Text (siehe auch das Thema „InputBox sauber abfangen“ auf Seite 39). Oder wir wollen überprüfen, ob in einer Zelle ein korrektes Datum steckt. Warum machen wir das? Ganz einfach: vergessen wir solche Überprüfungen, treten plötzlich Laufzeitfehler auf, weil wir als Beispiel versuchen, die Zahl 2 mit dem Text „Hallo“ zu multiplizieren, was nicht geht. Is…-Funktionen VBA hat bereits einige interessante Funktionen zur Überprüfung eingebaut. Die meisten sind mehr oder weniger selbsterklärend. IsNumeric Der Code könnte z.B. so aussehen: vZahl = 3 If IsNumeric(vZahl) Then End If Wir weisen also einer Variablen eine Zahl zu, und danach überprüfen wir, ob es sich wirklich um eine Zahl handelt. Wenn obige Variable korrekt vorgängig deklariert wurde: Dim vZahl As Long dann können wir ja nur eine Zahl drin speichern. Manchmal kriegen wir aber auch Text und wir wissen es wirklich nicht (z.B. die beschriebene InputBoxProblematik, oder bei einem Import aus anderen Quellen). Dann deklarieren wir die Variable vZahl als Variant (ausnahmsweise!!): Dim vZahl As Variant © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 45 von 74 VBA-Grundlagen Und danach: vZahl = "test" If Not IsNumeric(vZahl) Then MsgBox "Bitte Zahl eingeben" End If Beachte hier das NOT vor der Funktion. Das dreht den Sinn der Funktion quasi um. Selbstverständlich funktioniert die Abfrage auch mit einer Zelle in Excel: If IsNumeric(Range("A1")) Then resp. korrekter: If IsNumeric(Range("A1")).Value Then IsEmpty Diese Funktion ist mit Vorsicht zu geniessen (siehe Online-Hilfe). If IsEmpty(vZahl) Then ‚falls Variable nicht initialisiert Sie gibt meist nicht die gewünschten Resultate zurück. Besser ist der Einsatz der Funktion IsNull. IsNull If Not IsNull(vZahl) Then ‚falls Variable nicht leer Auf leere Zeichenfolge prüfen Häufig müssen wir prüfen, ob in einer Variablen eine leere Zeichenfolge steckt. Das prüfen wir so: If vInput = „“ then IsEmpty, IsNull und „“ sind nicht identisch und liefern jeweils andere Ergebnisse! IsDate Manchmal ist es praktisch, eine Zelle darauf zu überprüfen, ob ein korrektes Datum drin steckt: If IsDate(Range("A1").Value) Then Hier gehts nicht um die Formatierung des Datums, sondern lediglich, ob der Inhalt der Zelle im Grundsatz ein Datum ist. Soll ein bestimmtes Datumsformat überprüft werden, geht das so: If Range("A1").NumberFormat = “tt.mm.jjjj” Then Vorsicht! Es existieren zwei Eigenschaften: NumberFormat Seite 46 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer VBA-Grundlagen NumberFormatLocal Schreiben wir in der Zelle A1 das aktuelle Datum in der Form „28.07.2013“ rein, und geben mit Debug.Print obige Eigenschaften aus, dann schreibt VBA: NumberFormat= m/d/yyyy NumberFormatLocal= TT.MM.JJJJ Also aufgepasst, nach was gesucht wird. Noch ein Hinweis: geben wir die gleichen Infos in einer als „Standard“ definierten Zelle aus, erscheint: NumberFormat=General NumberFormatLocal=Standard © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 47 von 74 Formulare in Excel Formulare in Excel In VBA können Formulare ähnlich wie in Access erstellt werden. Dies kann als Beispiel für eine benutzerdefinierte InputBox verwendet werden: Erstellt werden Formulare als Beispiel über den folgenden Menüpunkt: Aufruf eines Formulars Um aus einer Prozedur ein Formular aufzurufen, schreibt man meistens: frmInputbox.Show Diese Zeile zeigt das Formular an. Vorsicht! Der nachfolgende Code wird normalerweise fortgesetzt. Meistens wollen wir dies jedoch nicht. Wir warten nämlich auf eine Eingabe im Formular, und erst nach dem Schliessen des Formulars soll der Code fortgesetzt werden. Aus diesem Grund macht es Sinn, obige Codezeile anzupassen auf: frmInputbox.Show 1 Diese Zahl 1 hinter dem Show dient dazu, dass das Formular als sog. modales Fenster geöffnet wird. Der gesamte Code einer Testprozedur könnte so aussehen: Sub pDialogTest() Dim vInput As Single frmInputbox.Show 1 vInput = frmInputbox.eZahl Debug.Print vInput End Sub Seite 48 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Formulare in Excel Schliessen des Formulars, Variante 1 Dazu können wir als Beispiel einen Button mit dem Namen „cmdOK“ verwenden. Im Ereignis „Beim Klicken“ schreiben wir: Private Sub cmdOK_Click() frmInputbox.Hide End Sub Dieser Code funktioniert zusammen mit der obigen Testprozedur pDialogTest. Das Formular wird nicht wirklich geschlossen, sondern nur unsichtbar gemacht. Wir dürfen es nicht schliessen, weil wir in der Testprozedur pDialogTest auf das Textfeld eZahl im Formular zugreifen. Schliessen des Formulars, Variante 2 Wir können das Formular auch komplett schliessen, zerstören oder beenden: Private Sub cmdOK_Click() Unload frmInputbox End Sub Jetzt ist das Formular aber nicht mehr vorhanden, d.h. obige Testprozedur pDialogTest funktioniert nicht mehr. Wir greifen dort ja auf ein Formularfeld zu: vInput = frmInputbox.eZahl Aber dieses existiert zu diesem Zeitpunkt gar nicht mehr. Dementsprechend müssen wir den Code in der Testprozedur auch anpassen. Parameter-Übergabe mittels globaler Variable Wollen wir das Formular wirklich mit Unload frmInputbox beenden, muss die Übergabe der Variablen über globale Variablen geschehen. In der Regel sind globale Variablen unschön, aber dies hier ist eine Ausnahme. Die Testprozedur sieht nun wie folgt aus: Global vInput as Single Sub pDialogTest() frmInputbox.Show 1 Debug.Print vInput End Sub Jetzt haben wir die Variable vInput oberhalb, d.h. ausserhalb der Prozedur pDialogTest deklariert. Sie ist für jedes Modul (und somit auch für das Formular) sichtbar. Im OK-Button des Formulars schreiben wir nun: Private Sub cmdOK_Click() vInput = Me.eZahl Unload frmInputbox End Sub © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 49 von 74 Formulare in Excel Bevor wir das Formular zerstören, übergeben wir der globalen Variablen vInput den Wert des Textfeldes. Textfeld nur zur Eingabe von Zahlen definieren Dazu verwenden wir das Ereignis „Beim Tastendruck“ des Textfeldes: Private Sub eZahl_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger) If KeyAscii < 48 Or KeyAscii > 58 Then KeyAscii = 0 End If End Sub Rotes Schliessen-Kreuz ignorieren Soll ein Klick auf das rote Kreuz oben rechts im Formular verhindert werden, kann dies wie folgt geschehen: Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer) Cancel = 1 End Sub Seite 50 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Import aus anderen Quellen Import aus anderen Quellen Excel kann mit verschiedenen Techniken auf fremde Daten zugreifen. Im Groben unterscheiden wir folgende Technologien: Native-Import (direkter Import) von Daten Import über DAO Import über ADO ODBC Einfach ausgedrückt sind es verschiedene Produkte, über welche man einen Import resp. einen Zugriff auf fremde Daten erstellen kann. DAO und ADO wurden beiden im Hause Microsoft entwickelt. Jahrelang wurde DAO stiefmütterlich behandelt. Seit der Office Version 2010 gilt DAO offiziell als Zukunftsprodukt, während ADO nicht mehr weiterentwickelt wird. Aber warten wir mal ab. In einigen Jahren kann sich das Blatt wieder wenden. Ich zeige auf den nächsten Seiten folgende Import-Möglichkeiten: Einlesen einer Textdatei Zugriff auf eine Access-Datenbank über DAO Einlesen einer dBase-Tabelle über ADO Einlesen einer Textdatei Diese Möglichkeit benötigt keinerlei Treiber oder Programm von Drittherstellern. VBA hat die Möglichkeit direkt eingebaut. Allerdings sind die Möglichkeiten hier sehr rudimentär. Der Code sieht so aus: In der While-Schlaufe lesen wir Zeile für Zeile ein. In diesem Beispiel geben wir diese Zeile einfach in das Direktfenster aus. In der Praxis würden wir stattdessen © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 51 von 74 Import aus anderen Quellen eine Array-Variable (ein Feld) füllen. Jede Position der Array-Variable enthält eine Zeile. Als zweiten Schritt müssen wir diese Zeile in der Array-Variable in Excel-Zellen abfüllen. Dies kann manuell mittels Text-Funktionen (Left, Right, Mid, Len, InStr) geschehen. Oder wir nehmen den „Text-in-Spalten-Assistenten“ und schauen, was der Makrorekorder aufzeichnet: Selection.TextToColumns Destination:=Range("A1"), DataType:=xlDelimited, TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=True, Semicolon:=False, Comma:=False, Space:=False, Other:=True, OtherChar:="|", FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 1), Array(4, 1), Array(5, 1), Array(6, 1)), TrailingMinusNumbers:=True Excel-Datei öffnen und Daten daraus kopieren Folgender Code öffnet eine bestimmte Excel-Datei, kopiert den Datenbereich in die Zwischenablage, und fügt diesen in der aktuellen Datei wieder ein: Dieser Code ist insofern sauber, als dass wir die Excel-Arbeitsmappen gleich sauber in Variablen (objSource und objTarget) speichern. Das ist auch im Kapitel „Excel-Dateien öffnen“ auf Seite 23 beschrieben. Zwei Seiten weiter hinten zeige ich eine Variante, welche ohne diese Zuweisung auskommt. Aber die Version auf dieser Seite hier ist definitiv besser und professioneller. Sollen die kopierten Daten nicht in der aktuellen Datei (wo ja auch der VBA-Code steckt) eingefügt werden, müssen wir zuerst eine neue Arbeitsmappe erstellen und am besten gleich speichern: Seite 52 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Import aus anderen Quellen Komplexe Datenübername aus mehreren Excel-Dateien Im Folgenden zeige ich eine komplexere Import-Methode. Wir öffnen innerhalb einer Schleife acht Dateien „Kunden1.xlsx“ bis „Kunden8.xlsx“ und fügen die Daten jeweils zuunterst an der bestehenden Liste an. Eine solche Liste zum Importieren sieht so aus: Der Trick oder das Problem ist, dass die erste Datei ganz normal mit dem Bereich Range(„A1“).CurrentRegion importiert werden kann. Die weiteren Dateien wollen wir aber erst ab der Zeile 2 importieren. Sonst hätten wir ja jedes Mal die Kopfzeile mit eingefügt; das wollen wir vermeiden. © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 53 von 74 Import aus anderen Quellen Wie vor zwei Seiten erklärt, ist dieser Code zwar in Ordnung, aber nicht ganz sauber. Dies aus dem Grund, weil wir nicht mit Objekt-Variablen wie objSource und objSheet, etc. arbeiten. Zugriff auf Access mittels DAO Der Zugriff auf eine Access-DB mittels DAO sieht immer genau gleich aus: Sub pAccessImport() Dim vDB As DAO.Database Dim vRS As DAO.Recordset Dim vFilename As String Dim i As Integer i = 2 vFilename = "P:\tn\V8A\Daniel\TopFood.mdb" Set vDB = OpenDatabase(vFilename) Set vRS = vDB.OpenRecordset("tblKunden", dbOpenDynaset) If Not (vRS.BOF And vRS.EOF) Then vRS.MoveFirst While Not vRS.EOF Cells(i, 1) = vRS.Fields("tNachname") Cells(i, 2) = vRS.Fields("tVorname") Cells(i, 3) = vRS.Fields("tOrt") vRS.MoveNext i = i + 1 Wend End If Seite 54 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Import aus anderen Quellen End Sub Erklärung des Codes Die gelb markierten Zeilen gehören zum eigentlichen Access-Zugriff. Wir benötigen ein Database-Objekt, in welchem die MDB-Datei steckt. Dann benötigen wir ein Recordset-Objekt, welches die Tabelle oder die Abfrage enthält. If Not (vRS.BOF And vRS.EOF) Then Mit dieser Zeile kontrollieren wir, ob überhaupt Daten vorhanden sind. Die Eigenschaft BOF (Begin of file) ist True, falls der Datensatzzeiger am Beginn der Tabelle ist. EOF (End of file) ist True, wenn der Datensatzzeiger am Ende der Tabelle ist. Wenn beides gleichzeitig zutrifft, sind keine Daten vorhanden. vRS.MoveFirst Wir wissen nicht genau, wo sich der Datensatzzeiger befindet, also gehen wir sicherheitshalber an den Anfang. While Not vRS.EOF Solange das Ende der Tabelle nicht erreicht ist, machen wir irgendetwas, z.B. Daten einlesen;-). vRS.MoveNext Es ist wichtig, in der Schleife jeweils einen Datensatz weiter zu springen, sonst landen wir in einer Endlosschleife, was allenfalls unser PC zum Absturz bringen kann. © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 55 von 74 Import aus anderen Quellen Einlesen einer dBase-Tabelle über ADO Erklärung des Codes Die gelben Zeilen sind wieder zwingend nötig für die Verbindung. Anstelle eines Database-Objekts wie bei DAO haben wir nun ein Connection-Objekt. Aber sonst ist es ähnlich. ConnectionString Die grüne Zeile ist äusserst speziell. In dieser Zeile geben wir den sog. Connection-String zur Datenbank an. Dies ist meist ein recht komplizierter Ausdruck, den man sich nicht merken kann. Aus diesen Grund gibt es dafür eine Homepage3, welche sämtliche Connection-Strings auflistet, welche man in der Praxis so braucht. Benötigen Sie einen Zugriff auf einen Oracle- oder SQLServer? Dann suchen Sie sich einfach den entsprechenden Connection-String raus. SELECT * FROM TBLADRES.DBF WHERE tNachname = 'Huber' Wir können direkt in der Open-Methode des Recordsets die Datenmenge bereits einschränken. Das macht den Zugriff auf die externen Daten extrem viel schneller; vorausgesetzt, wir benötigen nur einen Teil der Daten. Set rsAdressen = Nothing 3 Sie Kapitel „Homepages“ auf Seite 56. Seite 56 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Import aus anderen Quellen Set dbConnection = Nothing Diese Zeilen dienen exemplarisch als Hinweis, dass einmal erstellte Objekte (das erkennen wir am SET-Befehl vor der ersten Zuweisung zu Beginn der Prozedur) am Ende der Prozedur unbedingt wieder „zerstört“ resp. geschlossen werden sollten! Bei kleinen Code-Fragmenten spielt das keine Rolle. Aber bei grösseren Projekten können wir irgendwann in ein Speicher-Problem geraten, wenn wir immer neue Objekte erstellen, die alten aber nie beenden. Bild in Excel einfügen Der folgende Code fügt ein Bild aus einer Datei in die aktuell selektierte Zelle ein. Damit die Position stimmt, muss das Bild noch korrekt positioniert werden. Dim vPath As String Dim vFile As String Dim vPic As Picture vPath = "P:\Logos\Unterschriften\" vFile = vPath & „Unterschrift1.png" Set vPic = ActiveSheet.Pictures.Insert(Filename:=vFile) vPic.Left = ActiveCell.Left vPic.Top = ActiveCell.Top © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 57 von 74 Diverses Diverses Verweise, Early-Binding und Late-Binding Erstellen wir eine Verbindung zu einer anderen Applikation wie Word oder Powerpoint, oder greifen wir mittels DAO auf eine Access-DB, dann können wir den Zugriff auf diese Fremdapplikation auf zwei Arten definieren: Early-Binding Late-Binding Early-Binding Early-Binding heisst einfach ausgedrückt, dass wir schon zur Laufzeit eine Verbindung, hier als Beispiel zu ADO, herstellen. Damit wir dies machen, müssen wir in den Verweisen den entsprechenden Eintrag setzen: Besteht diese Verbindung (im unteren Bereich des Fensters sehen wir die Angabe, um welche DLL-Datei es sich handelt), können wir wie gewohnt in der Prozedur die Variablen deklarieren: Dim dbConnection As ADODB.Connection Dim rsAdressen As ADODB.Recordset Und weiter unten geschieht die Zuweisung: Set dbConnection = New ADODB.Connection Set rsAdressen = New ADODB.Recordset Late-Binding Wenden wir Late-Binding an, ist das manuelle Setzen eines Verweises im Extras-Menü nicht nötig! Dafür sieht die Deklaration der Objekt-Variablen wie folgt aus: Dim dbConnection As Object Dim rsAdressen As Object Und die Zuweisung der Variablen: Set dbConnection = CreateObject("ADODB.Connection") Set rsAdressen = CreateObject("ADODB.Recordset") Seite 58 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Diverses Was ist nun der Unterschied? Bei Early-Binding haben wir den Vorteil, dass VBA bereits zur Laufzeit die Methoden und Eigenschaften der Objektvariablen kennt. D.h. schreiben wir in unserem Code dbConnection., erscheint das Dropdown-Menü: Dafür ist der Verweis von Hand manuell gesetzt. Gehen wir mit unserer MakroDatei an einen Computer, bei welchem dieser Verweis nicht gesetzt ist, kriegen wir eine Fehlermeldung. Beim Late-Binding müssen wir auf obiges Dropdown-Menü verzichten, dafür muss der Verweis nicht manuell gesetzt sein. Automation (Steuerung anderer Applikationen) In VBA können andere Applikationen gesteuert werden. Wir können z.B. innerhalb von Access-VBA Word starten und mit Daten füllen. Wir können aber auch in Word-VBA ein Excel-Sheet öffnen und Daten rausholen. Da funktioniert fast jede Variante. Aus Excel Word automatisieren Als Beispiel erstellen wir eine Prozedur, welche ein bestehendes WordDokument öffnet, und dort in der Textmarke „bAdresse“ die Zelle A1 vom aktuellen Sheet einfügt. Der Code dazu sieht etwa so aus: Sub pWordBrief() Dim objWord As Word.Application Dim objDoc As Word.Document Dim rng As Word.Range Set objWord = CreateObject("Word.Application") Set objDoc = objWord.Documents.Open(Filename:="d:\Testdokument.doc ") Set rng = objDoc.Bookmarks("bAdresse").Range rng.Text = Range("A1") objWord.Visible = True Set objWord = Nothing End Sub © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 59 von 74 Diverses Der Aufruf geschieht mit Early Binding, d.h. der Verweis zur Microsoft Word Library muss vorgängig gesetzt sein! (siehe Abschnitt Early-Binding auf Seite 58) Wir sehen, dass ein Range-Objekt in Word einfach einen Textbereich widespiegelt, während in Excel das Range-Objekt ein Zellbereich ist. Fehler und Fehler und Fehler… Obiger Code muss allerdings noch sehr viel verbessert werden, da bei der Automation unmögliche Fehler auftauchen können. Z.B. macht es Sinn, die Existenz der Textmarke zu prüfen: If objDoc.Bookmarks.exists("bAdresse") = True Then Weiter sollte überprüft werden, ob der Verweis zur Microsoft Word Library überhaupt existiert. Dies ist komplizierter und kann mit VBA nur gemacht werden, wenn in den Sicherheitseinstellungen von Excel die Option auch aktiv ist: Hier ist es unter Umständen einfacher, nun wirklich mit der Fehlerbehandlung zu tricksen. Dummerweise kann in diesem Fall Early Binding nicht verwendet werden. Hier prüft schon der Editor vor dem Kompilieren, ob die Library sauber verlinkt ist. Es ändert sich einerseits die Variablendeklaration: Dim objWord As Object Dim objDoc As Object Dim rng As Object Bevor nun die Instanzierung von objWord geschieht, kommt die FehlerIgnorierung: On Error Resume Next Set objWord = CreateObject("Word.Application") If Err > 0 Then MsgBox "Das war nix mit Word" Exit Sub End If Somit wird zwar nicht, wie oben erwähnt, der Verweis auf die Library getestet, sondern einfach, ob der Zugriff auf Word möglich ist. Seite 60 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Diverses Fazit zur Automation: Die Entwicklung von Code zur Automation braucht sehr viel Gedult und z.T. tagelanges Testen und Suchen nach Lösungen in Büchern oder im Internet, da es sehr Fehler-anfällig ist. Aktuelle Datei als Email versenden Sub pEmailVersand() Dim vDatei As String Dim objOutloook As Object Dim objMail As Object vDatei = ThisWorkbook.FullName Set objOutlook = CreateObject("Outlook.Application") Set objMail = objOutlook.CreateItem(0) With objMail .To = "[email protected]" .Subject = "Betreff" .Body = "Die Nachricht" .Attachments.Add vDatei 'Anhängen der aktuellen Datei .Display 'Email nicht senden, sondern öffnen '.Send 'Oder Email ungefragt senden End With End Sub Tabellenfunktionen Eingebaute Tabellenfunktionen aufrufen Eingebaute Tabellenfunktionen können wie folgt aufgerufen werden: Flaeche = Application.WorksheetFunction.Acos(x) Eine eigene Tabellenfunktion erstellen Dazu benötigen wir ein Modul. Es handelt sich dabei um eine ganz normale Funktion: Function Flaeche(x As Single) As Single Flaeche = Application.WorksheetFunction.Acos(x) End Function In der Tabelle erscheint dann diese neu erstellte Funktion sofort: © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 61 von 74 Diverses Einer selbst erstellten Excel-Funktion einen Hilfetext hinzufügen (muss nur 1x gemacht werden) Applications.MacroOptions macro:=“fKreisflaeche“, Description:=“Na aber hallo, das kennste doch!“ Zugriff auf das System Registry bearbeiten Irgendeine Variable (hier „vNachname“) in der Registry speichern SaveSetting appname:="Digicomp", section:="Kurs E7V", Key:="Nachname", setting:=vNachname Speicherpfad: \\HKCU\Software\VB and VBA Program Settings\.. Wert „Nachname“ aus der Registry holen Optimierung: gesuchter Wert als Parameter der Funktion fLoadSettings definieren Function fLoadSettings() As String fLoadSettings = GetSetting(appname:="Digicomp", section:="Kurs E7V", Key:="Nachname") End Function Aktuellen Benutzer erfahren vPfad = Environ(“USERNAME”) So können sämtliche Umgebungsvariablen erfragt werden, welche in der Eingabeaufforderung mittels SET erscheinen. Einstellungen VBA-Editor Es gibt einige Einstellungen im VBA-Editor, die für die tägliche Arbeit Sinn machen. Option Explicit Der Ausdruck "Option Explicit" zu Beginn eines Moduls dient dazu, dass sämtliche Variablen vor dem ersten Gebrauch sauber deklariert werden müssen, d.h. wir müssen zuerst schreiben: Dim intZahl as integer bevor wir schreiben können: intZahl = inputbox(….) Seite 62 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Diverses Damit wir nicht vergessen, bei jedem Modul immer "Option Explicit" anzugeben, können wir in den Optionen des VBA-Editors dies als Voreinstellung definieren. Hierzu gehen wir auf "Extras/Optionen": Symbolleiste sinnvoll ergänzen In der Regel fehlen in der Symbolleiste die Symbole für das Auskommentieren sowie das "Einzug vergrössern" und Verkleinern von Zeilen. Finden tun wir diese Symbole unter "Ansicht/Symbolleisten/Anpassen": Die vier gelb markierten Symbole können wir nach oben in die Symbolleiste nehmen. Einzug vergrössern und Verkleinern Kleiner Tipp: mehrere Zeilen um eine Tabposition nach rechts verschieben geht nicht nur mit den obigen Symbolen, sondern auch durch das einfache Drücken der Tabulator-Taste. Den Einzug wieder verkleinern erreichen wir durch Drücken von Shift + Tabulator. © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 63 von 74 Diverses Scripting Runtime (2007) Da in Excel 2007 die FileSearch-Methode im Application-Objekt fehlt, müssen wir mit der Scripting Runtime arbeiten (Windows Scripting Host). Dazu wählen wir im VBA-Editor den Menüpunkt "Extras / Verweise…" aus. Im erscheinenden Dialog wählen wir folgenden Punkt aus: Makros Icon in der Schnellzugriffsleiste Das Icon ist nur für den aktuellen Benutzer sichtbar! Wird eine Arbeitsmappe mit Makros weitergegeben, sind die Makros zwar enthalten, die Icons fehlen allerdings Seite 64 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Diverses Zugriff auf Funktionen in Add-In Dateien Wir sprechen hier nicht von DLL-Dateien, sondern von XLA- resp.XLAM-Dateien. Zum Erstellen einer solchen Add-In-Datei öffnen wir Excel ganz leer und gehen in den VBA-Editor. Dort fügen wir ein Modul ein. Als Beispiel erfassen wir folgende Excel-Funktion: Nun speichern wir die Datei unter „dhFunctions.xlam“. Ich wähle jeweils die Kurzzeichen dh vorab, damit ich weiss, dass das Add-In von mir stammt. Entsprechend kann dies angepasst werden. Der Standard-Pfad für Add-In-Dateien lautet: C:\Users\[Username]\AppData\Roaming\Microsoft\AddIns Achtung! Damit das Add-In funktioniert, muss es im Add-In-Manager noch aktiviert warden: © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 65 von 74 Diverses Dieses Fenster lässt sich in den Optionen finden: Alternativer Speicherpfad Die XLAM-Datei kann auch in den XLSTART-Ordner gelegt werden. Diese Version ist sogar besser als obige, weil das Addin dann sofort aktiv ist ohne jegliche Aktivierung. Seite 66 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Homepages zu Excel Homepages zu Excel Allgemein zu Office und VBA www.officehilfe.ch Meine eigene Seite mit Tipps & Tricks zu Office. Diese Seite lebt! Schreiben Sie mir ein Email mit Themen, die Sie interessieren. Dann werde ich einen Beitrag dazu schreiben. www.office-loesung.de www.held-office.de www.schmittis-page.de Extrem umfangreiches Diskussionsforum FAQ's und Tipps von Bernd Tipps von Marcus www.connectionstrings.com Eine grosse Auswahl an Connections-Strings, um eine Verbindung auf fremde Datenquellen mittels ADO resp. ODBC zu erstellen. www.vb-tec.de Häufige VB-Probleme erläutert, kurz und bündig (deutsch) www.vbarchiv.net Sehr umfangreiche VB-Seite auf deutsch Excel www.xlam.ch www.herber.de www.hajo-excel.de www.excelformeln.de excelabc.de www.xl-faq.de www.online-excel.de Homepages zu Access www.donkarl.com www.access-imunternehmen.de www.access-tutorial.de © 2014 Daniel Hofer Limitationen in Excel, extrem umfangreiche Seiten Äusserst umfangreiche VBA-Seite speziell für Excel Seite von Hajo mit vielen Infos Kein VBA, aber dafür ausführliche Infos zu komplexen Formeln (meist Matrix-Funktionen) Berti's Tipps & Tricks FAQ-Seite von Frank Diverse Tipps zu VBA Wohl die beste FAQ-Seite für Access auf deutsch Profi-Tipps. Die vollständigen Artikel sind nur nach Kauf einer Zeitschrift verfügbar (lohnt sich aber für Access-Leute) Allgemeine (aber sehr gute) Access-Seite, nicht VBA Version 3.2 / 14.10.2014 Seite 67 von 74 Ribbons Ribbons Geniales Ribbon-Blog MSDN-Welcome Seite über Ribbons Offizielle MSDN-Seite über Ribbons, Teil 1 Custom UI Editor Tool Ganz schlichtes Tool, in welchem eine Excel-Datei geöffnet werden kann. Danach ein Beispiel-Ribbon laden aus dem Menü „Sample“ und anpassen. Am Schluss wieder speichern Ribbon Customizer Kann die originalen Ribbons auch anpassen! Bilder der Icons können folgendermassen gefunden werden: Im Anpassen-Dialog der Einstellungen mit der Maus auf einen Befehl fahren und schauen, was er im Tooltip schreibt Infos zu 2010 Updates http://www.excelguru.ca/blog/2006/ 12/01/ribbon-example-table-ofcontents/ http://msdn2.microsoft.com/enus/office/aa905530.aspx http://msdn2.microsoft.com/enus/library/aa338202.aspx http://openxmldeveloper.org resp. http://openxmldeveloper.org/blog/b /openxmldeveloper/archive/2010/08/ 10/23248.aspx http://pschmid.net/office2007/ribb oncustomizer/index.php http://www.rondebruin.nl/ribbon.ht m Grundsätzliches zu Ribbons Unter Office 2007 kann die Multifunktionsleiste mittels VBA überhaupt nicht angepasst werden Unter 2010 ist dies wieder möglich. Trotzdem macht es auch unter 2010 und 2013 häufig Sinn, nach der „alten“ Methode von 2007 vorzugehen. Seite 68 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Ribbons Meistens will man das Menüband (wie es seit 2010 heisst) nicht grundsätzlich ändern, sondern nur für eine bestimmte Excel-Mappe. Und deshalb passt man am besten diese Datei an, indem man mittels XML die Anpassungen vornimmt (siehe nächster Abschnitt). Geeignete Datei-Formate Grundsätzlich muss es ein Dateiformat sein, das Makros speichern kann (sonst ist die Anpassung der Ribbons ja sinnlos, v.a. wenn man eigene Knöpfe einfügen will). Soll das angepasste Ribbon für mehrere Dateien gelten, dann nimmt man am besten das Add-In-Format XLAM (diese Mappe wird dann im Hintergrund und unsichtbar geöffnet). Kurze Anleitung für die Anpassung des Ribbons unter Office 2007/2010 Als erstes müssen wir den CustomUI Editor (Link oben) runterladen und installieren. Das ergibt ein Icon auf dem Desktop: Jetzt kann im CustomUI Editor die entsprechende Word- oder Excel-Datei geöffnet werden. Achtung, es muss sich um eine Datei mit Makros handeln, d.h. mit einem „m“ in der Endung, also docm oder xlsm als Beispiel. Nun sollte das etwa so aussehen: Jetzt können wir unter „Insert“ gehen.: © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 69 von 74 Ribbons Je nach Version wählen wir 2007 oder 2010 aus. Danach sollte das Bild etwa so aussehen: Jetzt klicken wir wieder auf „Insert“ und wählen ein Beispielcode aus: Danach erscheint im rechten Fenster der entsprechende XML-Code: Es macht Sinn, das Fenster breit genug zu machen, damit der Code auch sinnvoll formatiert erscheint. Hier können nun die entsprechenden Änderungen vorgenommen werden (viel Spass) Seite 70 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Ribbons Am Schluss speichern wir die Datei und öffnen die Excel-Datei in Excel. Es sollte nun ein neues Register „Contoso“ erscheinen: Nun folgt noch der letzte Schritt. Wir müssen nämlich auf den Klick der Buttons ganz rechts reagieren. Wie wir oben im XML-Code sehen, steht dort unter OnAction als Beispiel die Ereignisprozedur „conBoldSub“. Also fügen wir diese Prozedur in einem Modul in unserer Excel-Datei ein: Und schon erscheint obige Messagebox, wenn wir auf den Button „ConBold“ klicken. © 2014 Daniel Hofer Version 3.2 / 14.10.2014 Seite 71 von 74 Datentypen Datentypen 1 Byte 0 bis 255 2 Bytes True oder False 2 Bytes -32‘768 bis 32‘768 4 Bytes -2‘147‘483‘648 bis 2‘147‘483‘647 Fliesskommazahl mit 8 Stellen Genauigkeit Ganzzahl Wahrheitswert Ganzzahl Ganzzahl 4 Bytes Gleitkommazahl einfacher Genauigkeit 8 Bytes Fliesskommazahl mit 16 Stellen Genauigkeit 8 Bytes Festkommazahlen mit 15 Stellen vor und 4 Stellen nach dem Komma Untertyp von Variant. Genauigkeit 28 Stellen (Anzahl Stellen vor dem Komma müssen abgezogen werden) Gleitkommazahl doppelter Genauigkeit Skalierte Ganzzahl 14 Bytes Skalierte Ganzzahl 8 Bytes Datum 10 Bytes plus Länge Länge Grösse nur durch RAM beschränkt Bis ca. 65‘400 Zeichen 16 Bytes Gross und langsam! Variable Länge Feste Länge (Zahlen) 22 Bytes plus Länge (Text) 4 Bytes Seite 72 von 74 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer Index Access und DAO ............................... 54 ActiveCell ......................................... 11 ActiveSheet ...................................... 12 ActiveWorkbook.Save ...................... 14 Add-In .................................... 9, 65, 69 Aktuellen Benutzer erfahren ............ 62 Andere Reihenfolge der Parameter .. 30 Anzeigen eines Speichern-Dialogs .... 22 Application.InputBox ........................ 40 Application.ScreenUpdating ............. 16 Arrays ............................................... 31 Auf leere Zeichenfolge prüfen .......... 46 Autofilter.......................................... 13 Automation ...................................... 59 Bild in Excel einfügen ....................... 57 ........................................... 72 ByRef ................................................ 27 ................................................. 72 Cells(20, 1) ....................................... 11 ColorIndex ........................................ 11 Dateiendungen................................... 9 Datentypen ...................................... 72 Datumsformat .................................. 47 dBase ............................................... 56 Dialoge selber gemacht .................... 48 Do Loop ............................................ 35 Early-Binding .................................... 58 Einlesen einer Textdatei ................... 51 Einstellungen VBA-Editor ................. 62 Enumerationen ................................ 28 Excel importieren ............................. 52 Excel-Dateien öffnen ........................ 23 Fehler-Behandlung ........................... 37 Felder ............................................... 31 For Next ........................................... 35 Formulare in Excel ............................ 48 Funktionen ....................................... 29 Geeignete Datei-Formate für eine Ribbon-Anpassung ........................ 69 © 2014 Daniel Hofer Gleichzeitig mehrere Excel-Dateien offen ............................................. 17 Gross- und Kleinschreibung .............. 44 Gültigkeitsbereiche .......................... 30 Homepages ...................................... 67 IF 17 Import .............................................. 51 Access ........................................... 54 Bild ................................................ 57 dBase ............................................ 56 Excel .............................................. 52 Textdatei ....................................... 51 Inhalt .................................................. 7 Inputbox ........................................... 18 InputBox sauber abfangen ............... 39 InStr ................................................. 43 InStrRev............................................ 43 Is…-Funktionen................................. 45 IsDate ............................................... 46 IsEmpty ............................................ 46 IsNull ................................................ 46 IsNumeric ......................................... 45 IsNumeric()....................................... 39 Konstanten ....................................... 29 LAENGE ............................................ 42 Late-Binding ..................................... 58 Left ................................................... 43 Len ................................................... 43 Limitationen ....................................... 9 LINKS ................................................ 42 Makrorekorder ................................. 18 Mid ................................................... 43 Monatsname .................................... 44 MonthName ..................................... 44 MsgBox ............................................ 33 MsgBox als Funktion ........................ 34 MsgBox als Prozedur ........................ 33 Nachträgliche Korrekturen nach dem Aufzeichnen .................................. 20 Namen des aktuellen Sheets ändern 22 Version 3.2 / 14.10.2014 Seite 73 von 74 Index Neue Excel-Mappe erstellen ............ 25 Neues Tabellenblatt hinzufügen ...... 22 NOT.................................................. 46 NumberFormat ................................ 47 NumberFormatLocal ........................ 47 Öffnen-Dialog anzeigen.................... 24 On Error ........................................... 37 On Error goto 0 ................................ 38 On Error resume next....................... 38 Option Explicit .................................. 62 Performance-Vergleich .................... 16 PERSONAL.XLSB ................................. 9 Range statt Cells............................... 36 Range("A1") ..................................... 11 RECHTS ............................................ 42 Registry bearbeiten .......................... 62 Ribbons ............................................ 68 Right................................................. 43 Schleifen .......................................... 35 Schnellzugriffsleiste ......................... 64 Scripting Runtime............................. 64 SELECT CASE..................................... 17 Selection .......................................... 12 SET ................................................... 62 Sinnvolle Einsatzmöglichkeiten des Rekorders...................................... 18 Speichern der aktuellen Datei .......... 23 Speichern-Dialogs ............................ 22 Spezielle Zellen ................................ 14 Sprung zur letzten Zelle.................... 14 Standard-Fehlerbehandlung ............ 37 ............................................... 72 Suchen und Finden im Range-Objekt 13 Summenformel in VBA ..................... 13 Seite 74 von 74 System-Zugriff .................................. 62 Tabellenfunktionen .......................... 61 TEIL .................................................. 42 Teil einer Zelle farbig markieren....... 12 Text farbig markieren ....................... 12 Textdatei importieren ...................... 51 Text-Funktionen ............................... 42 Trim.................................................. 43 Unsinniger Einsatz des Rekorders .... 18 Variablen .......................................... 26 Variant ............................ 18, 28, 45, 72 vb… .................................................. 34 VBA-Editor ....................................... 62 vbBinaryCompare............................. 44 vbCritical .......................................... 33 vb-Konstanten .................................. 34 vbTextCompare ................................ 44 vbYesNo ........................................... 33 WeekdayName................................. 44 While Wend ..................................... 35 WITH ................................................ 32 Wochentag....................................... 44 Word automatisieren ....................... 59 Workbooks.Add ............................... 25 Worksheets löschen ......................... 22 Worksheets(„Tabelle1“) ................... 11 Worksheets.Add............................... 22 XLAM................................................ 69 XLSB ................................................... 9 Zellen auf Gültigkeit überprüfen ...... 45 Zugriff auf Access mittels DAO ......... 54 Zugriff auf das System ...................... 62 Zugriff auf Zellen und Bereiche ........ 11 Version 3.2 / 14.10.2014 © 2014 Daniel Hofer