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