Tipps und Tricks zu VBA

VBA: Ausführen von Makros unter Office 2000 und höher


Aufgrund der immer größer werdenden Virengefahr werden Dateien mit Makros in Office 2000 und höher (bzw. Access 2003 und höher) nicht mehr standardmäßig mit mittlerem Virenschutz ausgeführt, bei dem nachgefragt wird, ob Makros aktiviert werden sollen. Haben Sie keinen unterstützten Virenscanner installiert, hat Office nach der Installation den Virenschutz auf „hoch“ eingestellt. Damit werden alle Makros ohne Rückfrage deaktiviert. Wenn Sie VBA-Programmierung in einigen Dateien nutzen möchten, müssen Sie den Virenschutz über Extras/Makro/Sicherheit auf „mittel“ einstellen.

VBA 6.0: Zeichen innerhalb von Zeichenketten ersetzen

Neu in VBA 6.0 ist die Funktion Replace. Sie ist ganz hilfreich, bspw. wenn Sie in einer Zeichenkette einzelne Zeichen ersetzen möchten.

Sie können damit bspw. ein Dezimalkomma in einer Zeichenkette durch einen Dezimalpunkt ersetzen, indem Sie die Funktion wie folgt aufrufen:

replace("10,75",",",".")

Als ersten Parameter übergeben Sie die Zeichenkette, als zweiten das zu suchende Zeichen und als drittes das Zeichen, das das zu suchende Zeichen ersetzen soll. Sowohl der zweite als auch der dritte Parameter können aber auch mehrere Zeichen enthalten und dürfen unterschiedlich lang sein.

VBA 6.0: Endlosschleifen nutzen

Trotz der Objektorientierung von VBA und der damit einhergehenden Unterstützung von Ereignissen gibt es immer noch Einsatzbereich für Endlosschleifen. Problematisch sind solche Schleifen nur dann, wenn sie ungeschickt verwendet werden, weil Sie dann dazu führen, dass die Anwendung nicht mehr reagiert, weil die Anwendung ausschließlich mit der Ausführung der Schleife beschäftigt ist. Sie können das vermeiden, indem Sie innerhalb der Schleife die Anweisung DoEvents verwenden. Sie sorgt dafür, dass zwischendurch eintretende Ereignisse, wie Benutzereingaben abgearbeitet werden. Eine solche Schleife könnte bspw. wie folgt aussehen:

Sub test()
Do
'HIER STEHEN DIE SCHLEIFENANWEISUNGEN
DoEvents
Loop
End Sub

VBA: Schrittweite in for-Schleifen

In For-To-Next-Schleifen können Sie nicht nur Anfangs- und Endwert der Schleife bestimmen, sondern auch die Schrittweite, in dem Sie die optionale Angabe Step verwenden. Möchten Sie bspw. nur Gerade Zahlen als Werte für die Zählvariable verwenden, muss die Schleife dazu wie folgt aussehen.

For I=2 to 10 Step=2
...
Next I

Schutz vor Viren

Es gibt verschiedene Verhaltensweisen, die Ihnen helfen sich auch ohne Firewall und teure Virenscanner weitestgehend vor E-Mail-Viren und Würmern zu schützen:

  • Löschen Sie E-Mails, wenn Sie sie von einem Absender bekommen, von dem Sie keine E-Mail erwarten oder den Sie gar nicht kennen und leeren Sie hinterher auch den Papierkorb.
  • Klicken Sie niemals wahllos auf Links in E-Mails.
  • Öffnen Sie keinen Anhand einer E-Mail, wenn Sie nicht einen Anhang von diesem Empfänger erwarten
  • Vor allem bei folgenden Dateitypen in Anhängen sollten Sie vorsichtig sein: EXE, VBS, JS, HTML, HTM sowie den Office 97/2000 Dateien.
  • Wenn Sie Excel 5/7 oder Office 97/2000 Dateien öffnen, die Sie per E-Mail bekommen haben, sollten Sie vorher unbedingt den Virenschutz des Programms aktivieren und mindestens auf „Mittel“ stellen. Deaktivieren Sie dann Makros, falls Sie danach gefragt werden und sehen Sie sich die Makros genau an, falls Sie Ahnung von VBA haben.
  • Verwenden Sie den Regelassistenten von Outlook oder Outlook Express um bekannt Viren vollautomatisch beim Download zu löschen.

Wenn Sie diese grundlegenden Regeln beachten, reduzieren Sie die Virengefahr schon erheblich. Ein aktuellen Virenscanner verbessert diesen Schutz noch, kann jedoch neue Viren in der Regeln nicht erkennen. Immerhin sind Sie damit vor den schon bekannten Viren geschützt.

VBA: Listenfelder leeren

Wenn Sie in UserForms Listenfelder verwenden, stellt sich oft das Problem, dass diese irgendwann geleert werden müssen. Dazu ist es nicht notwendig, dass alle Einträge einzeln gelöscht werden. Sie können die ganze Liste mit der Clear-Methode leeren. Vorausgesetzt das Listenfeld hat den Namen lstListe, können Sie den Inhalt mit lstListe.Clear löschen.

VBA: Objektvariablen deklarieren und verwenden

Wenn Sie in VBA Objektvariablen verwenden möchten, also Variablen die auf Objekte verweisen, sollten Sie diese natürlich auch deklarieren. Wichtig ist, dass diese Variablen vom Typ Object definiert wird. Dazu müssten die Deklaration z.B. wie folgt lauten:

Dim objVar As Object

Sie können jedoch auch eine bestimmte Klasse angeben, wenn Sie bereits wissen, welches Objekt die Variable später speichern soll. Die folgende Deklaration definiert z.B. eine Objektvariable der Klasse Range.

Dim objRange As Range

Diese Objektvariable kann dann nur Objekt vom Typ Range aufnehmen. Wenn Sie einer Objektvariablen einen Wert zuweisen möchten, müssen Sie dazu die Set-Anweisung verwenden. Die folgende Anweisung weist der Objektvariablen objRange die Zelle A1 des aktiven Excel-Tabellenblattes zu:

Set objRange=ActiveWorkbook.ActiveSheet.Range("a1")

Verweise erstellen

Wenn Sie programmübergreifende VBA-Anwendungen erstellen oder z.B. unter Excel Objekte von Word, PowerPoint oder Access nutzen möchten, gibt es da im Prinzip zwei Möglichkeiten. Die eine besteht darin, Sie definieren eine Variable als mit dem Datentyp Object und weisen ihm dann das von der CreateObject-Methode zurückgegebene Application-Objekt zu. Sie können dann alle Eigenschaften und Methoden des Application-Objekts nutzen. Allerdings müssen Sie dann die Objekte und Methoden auswendig kennen. Wenn Sie eine Variable nur vom allgemeinen Typ Object definieren, weiß IntelliSense nicht, welche Eigenschaften und Methoden das Objekt hat. Daher werden Sie auch nicht aufgelistet. Möchten Sie IntelliSense nutzen, müssen Sie dazu einen Verweis auf die entsprechende Objektbibliothek der anderen Anwendung erstellen. Dazu wählen Sie im Menü der IDE Extras/Verweise aus. Alle möglichen Objektbibliotheken werden nun in einem Dialog aufgelistet. Um einen Verweis darauf zu erstellen, aktivieren Sie einfach das Kontrollkästchen vor dem Eintrag. Wenn Sie z.B. in einer anderen Office-Anwendung Word nutzen möchten, aktivieren Sie den Listeneintrag „Microsoft Word 9.0 Object Library“. Nach Schließen des Dialogs mit OK steht die Objektbibliothek zur Verfügung. Sie könnten dann z.B. eine Objektvariable wie folgt definieren:

Dim objAppWord As Word.Application

Wenn Sie nun objAppWord gefolgt von einem Punkt eingeben, zeigt die IDE IntelliSense für das Word.Application-Objekt an. Neben der Verfügbarkeit der Programmierhilfen hat die Verwendung von Verweise noch einen Vorteil. Der Zugriff auf das Objekt, auf das die Objektvariable verweist, ist wesentlich schneller, wenn VBA der Type des Objektes bekannt ist. Sie können also gerade bei Verwendung vieler Objekte einen großen Geschwindigkeitszuwachs Ihrer Anwendung erreichen.

VBA: Office-Anwendungen starten

Wenn Sie bspw. aus Excel Word starten möchten, oder aus Access Excel starten möchten, gibt es dazu verschiedene Möglichkeiten, einmal die Shell-Methode und zum anderen die CreateObject-Methode. Letztere hat den Vorteil, dass Sie damit auch auf das Objektmodell der entsprechenden Anwendung zugreifen und diese mit VBA steuern können. Das funktioniert natürlich nur dann, wenn es sich um eine VBA-Hostanwendung handelt; Mit Shell können Sie jedoch jede beliebige Anwendung starten. Um mit CreateObject eine Anwendung zu starten, benötigen Sie lediglich deren Klassenbezeichnung. Folgende Anweisung startet z.B. Word 97:

Set WWApp=CreateObject("Word.Application.8")

WWApp ist eine Variable vom Typ Object oder Word.Application. Wenn Sie sie als Word.Application definieren möchten, müssen Sie dazu einen Verweis auf die Objektbibliothek von Word einrichten. Die Variable verweist nun auf das Application-Objekt von Word und Sie können nun dessen Methoden verwenden. Mit WWApp.Quit können Sie z.B. Word beenden.

Tabelle 1: Klassennamen der Office-Anwendungen
Klassen-Name Anwendung
Word.Application Word
Excel.Application Excel
Access.Application Access
PowerPoint.Application PowerPoint
Outlook.Application Outlook

Rechtliche Rahmenbedingungen

Alle Inhalte wurden nach bestem Wissen und Gewissen zusammengestellt. Ich übernehme für Fehlerfreiheit allerdings keine Gewähr und hafte keinesfalls für Folgen, die sich aus Fehlern oder unsachgemäßem Gebrauch des Codes und der Inhalte ergeben.
Code und Beispiele dürfen für den privaten Gebrauch frei verwendet werden. Eine Veröffentlichung (auch auszugsweise) sowohl online wie auch auf klassischen Medien ist nur nach meiner ausdrücklichen Zustimmung möglich. Verlinkung der Artikel ist jedoch erlaubt und erwünscht, solange immer auf die Webseite und nicht die PDF-Datei verlinkt wird.

Performance-Optimierung: DAO versus SQL

Autor: Helma Spona
Betrifft: Access, Datenbanken, SQL, Datenabfragen, Datenmanipulation
Systemanforderungen: Access 97 oder höher
Download: PDF-Version

Große Datenmengen lassen sich mit Access auf vielfältige Weise bearbeiten. Die zwei am häufigsten Verwendeten sind das DAO-Objekt-Modell und SQL. Darüber hinaus gibt es aber auch noch die Möglichkeit, Abfragen auszuführen, um Daten zu manipulieren. Die Frage, welches die schnellste Möglichkeit ist, werden viele Entwickler prompt mit „SQL“ beantworten. Aber ist das wirklich in jedem Fall so?

Die Frage nach der schnellsten Methode zur Datenmanipulation ist nicht so leicht zu beantworten, wie es auf den ersten Blick scheint. Nicht immer entspricht das Ergebnis nämlich den Erwartungen. Zu erwarten wäre beispielsweise, dass die Nutzung von Aktionsabfragen oder SQL-Anweisungen gleich schnell ist, da in beiden Fällen von Access die gleiche SQL-Anweisung ausgeführt wird. Dennoch ist dies nur beim Ändern vorhandener Datensätze der Fall. Ganz anders sieht es beim Erzeugen und Löschen von Tabellen und beim Löschen und Einfügen der Datensätze aus.

Performance vergleichen

Am besten lässt sich die Geschwindigkeit der einzelnen Methoden vergleichen, wenn Sie mehrmals hintereinander die gleiche Aktion durchführen und die dazu notwendige Zeit messen und berechnen. Dabei hat sich ergeben, dass vor allem beim Erstellen von Tabellen SQL die Nase vorn hat. Um 1000 mal eine Tabelle zu erstellen und wieder zu löschen, werden mit Hilfe von SQL nur 9 Sekunden, bei Nutzung der CreateTableDef-Methode jedoch ganz 15 Sekunden benötigt. DAO ist somit über 33% langsamer. Auch wenn Sie eine Tabellenerstellungsabfrage verwenden, die die gleiche SQL-Anweisung verwendet, wie sie beim Test mit SQL verwendet wurde, ergibt sich noch eine Zeitdifferenz von drei Sekunden gegenüber SQL.

Wenn Sie in eine Tabelle, die aus drei Feldern, einem numerischen, einem Textfeld und einem Memo-Feld besteht, 10.000 Datensätze einfügen, ergibt sich ein ganz anderes Bild. Hier ist SQL mit Abstand am langsamsten. Mit einer SQL-Anweisung benötigen Sie nämlich 38 Sekunden, während DAO hier mit 31 Sekunden auskommt und eine Anfügeabfrage, die 10.000 Mal aufgerufen wird, immer noch mit 30 Sekunden auskommt.

Tabelle 1: Übersicht über den Zeitbedarf für definierte Aktionen
Aktion SQL DAO Abfragen
Tabellen erstellen und löschen 1000 x 9 15 12
Datensätze einfügen 10.000 Stk. 38 31 30
Datensätze ändern (20.000) 1 2 1
Datensätze abhängig von einer Bedingung ändern (20.000) 1 2 1
Datensätze löschen (20.000) 2 0 1

Wenn Sie vorhandene Datensätze ändern möchten, werden Sie dabei feststellen, dass hier SQL und Aktionsabfragen gleich schnell sind und DAO am langsamsten. Das lässt sich allerdings dadurch erklären, dass Sie mit DAO alle Datensätze durchlaufen und einzeln ändern müssen, während das mit SQL und somit auch in Aktionsabfragen mit einer einzigen SQL-Anweisung für alle Datensätze möglich ist. Das gleiche Ergebnis ergibt sich, wenn Sie nur bestimmte Datensätze abhängig von einem Kriterium ändern möchten. Allerdings wäre hier zu erwarten, dass das länger dauert als ohne Kriterium. Dem ist aber nicht so. Die Geschwindigkeit ist zumindest bei 20.000 Datensätzen noch gleich.

Wenn Sie allerdings mit der gleichen VBA-Schleife alle Datensätze nacheinander löschen, gibt es die nächste Überraschung. Bei 20.000 Datensätzen brauchen Sie mit DAO dazu weniger als eine Sekunde, mit SQL hingegen zwei Sekunden und eine Löschabfrage braucht immer noch eine Sekunde dafür. Hier ist DAO also am schnellsten.

Wie wurde getestet?

Jeder Performance-Test ist natürlich relativ. Er ist nicht nur abhängig von der Hardware auf der getestet wurde, sondern auch vom Betriebssystem, der Access-Version und dem Code, der zum Testen verwendet wurde.

Listing 1: Die Zeitmessung

Sub Zeitmessung(datAnf As Date, datEnde As Date)
Dim datZeit As Date
datZeit = datEnde - datAnf
Debug.Print "Zeit: " & Format(datZeit, "")
End Sub

Sub testTabellen()
Dim datAnfang As Date
Dim lngAnz As Long
lngAnz = 1000
'SQL
Debug.Print "SQL"
datAnfang = Now
For lngI = 1 To lngAnz
TabelleErstellenSQL
TabelleLoeschenSQL
Next lngI
Zeitmessung datAnfang, Now

...
End Sub

Für die vorstehenden Vergleiche wurde ein Rechner mit Intel Pentium III Mobile mit 500 Mhz verwendet. Als Betriebssystem wurde Windows XP Service Pack 1 eingesetzt und der Code wurde unter Access 2002 ausgeführt.

Wichtig ist aber natürlich auch, welcher Code für die Zeitmessung und die Ausführung der SQL-Anweisungen und Abfragen eingesetzt wurde. Listing 1 zeigt die Prozedur für die Zeitmessung. An diese Prozedur werden Anfangszeit und aktuelle Zeit als Parameter übergeben. Prozedur berechnet die Differenz und gibt die Zeit im Testfenster aus.

Diese Prozedur wird dann in einer weiteren Prozedur aufgerufen, die in einer Schleife, die von 1 bis zu einem per Variablen definierten Endwert läuft, die Testprozeduren aufruft. Am Ende der Schleife wird die Prozedur Zeitmessung aufgerufen. Nach der Ausgabe der Zeit, wird die gleiche Schleife für eine andere Prozedur ausgeführt, die dann eine andere Methode bspw. DAO verwendet.

SQL-Anweisungen und Abfragen ausführen

Wie die SQL-Anweisungen und Abfragen ausgeführt werden, spielt natürlich auch eine Rolle. Listing 2 zeigt beispielhaft zwei Prozeduren zum Ändern der Datensätze. Die Prozedur DatenAendernSQL verwendet dazu eine UPDATE-Anweisung, die über die Execute-Methode des Database-Objekts ausgeführt wird. Auf gleiche Weise verfährt die Prozedur DatenAendernAbfrage. Sie übergibt an die Execute-Methode aber keine SQL-Anweisung, sondern den Namen der auszuführenden Abfrage. Analog werden auch die SQL-Anweisungen bzw. die Abfragen zum Löschen und Erstellen der Daten definiert. Da beide Prozeduren sich so ähnlich sind, ist es natürlich um so verwunderlicher, dass es solche erheblichen Unterschiede beim Einfügen von Datensätzen gibt.

Listing 2: SQL-Anweisungen und Abfragen ausführen

Sub DatenAendernSQL()
Const strSQL = "UPDATE test SET test.ID = 2, test.Zeichen = ""XYZ"""
Dim objDB As DAO.Database
Set objDB = Application.CurrentDb
objDB.Execute strSQL
Set objDB = Nothing
End Sub

Sub DatenAendernAbfrage()
Dim objDB As DAO.Database
Set objDB = Application.CurrentDb
objDB.Execute "DatAendern"
Set objDB = Nothing
End Sub

Sub DatenAendernDAO()
Dim objDB As DAO.Database
Dim objRS As DAO.Recordset
Set objDB = Application.CurrentDb
Set objRS = objDB.OpenRecordset("test3")
objRS.MoveFirst
Do While objRS.EOF = False
objRS.Edit
objRS.Fields("ID") = 2
objRS.Fields("Zeichen") = "XYZ"
objRS.Fields("langerText") = "Text im Memofeld"
objRS.Update
objRS.MoveNext
Loop
objRS.Close
Set objRS = Nothing
Set objDB = Nothing
End Sub

Die Prozedur DatenAendernDAO öffnet die Tabelle als Recordset-Objekt und durchläuft die einzelnen Datensätze in einer While-Schleife. Obwohl der Code daher wesentlich aufwändiger aussieht, ist die Zeitdifferenz verhältnismäßig gering.

Fazit


Wenn Sie Datenzugriffe und Anweisungen zur Datenbankmanipulation hinsichtlich Performance optimieren möchten, sollten Sie SQL-Anweisungen verwenden, wenn Sie Tabellen erstellen, manipulieren und löschen möchten. Zum Einfügen von Datensätzen sind hingegen Aktionsabfragen optimal. Wo dies nicht möglich ist, weil komplexe Berechnungen erforderlich sind, die nicht mit SQL zu bewältigen ist, stellt DAO eine Alternative dar. Zum Ändern von Datensätzen sollten Sie wahlweise SQL oder Aktionsabfragen einsetzen, zum Löschen hingegen wieder DAO.

Rechtliche Rahmenbedingungen

Alle Inhalte wurden nach bestem Wissen und Gewissen zusammengestellt. Ich übernehme für Fehlerfreiheit allerdings keine Gewähr und hafte keinesfalls für Folgen, die sich aus Fehlern oder unsachgemäßem Gebrauch des Codes und der Inhalte ergeben.
Code und Beispiele dürfen für den privaten Gebrauch frei verwendet werden. Eine Veröffentlichung (auch auszugsweise) sowohl online wie auch auf klassischen Medien ist nur nach meiner ausdrücklichen Zustimmung möglich. Verlinkung der Artikel ist jedoch erlaubt und erwünscht, solange immer auf die Webseite und nicht die PDF-Datei verlinkt wird.

XML-Dateien mittels XML-Dom bearbeiten

Autor: Helma Spona
Betrifft: VBA, XML, MSXML-DOM
Systemanforderungen: VBA-Host-Anwendung + Internet Explorer 5 oder höher

Download: PDF-Version

XML ist zurzeit in aller Munde. Nicht nur dass es ein wesentlicher Teil der .Net-Strategie von Microsoft ausmacht, auch im Alltag von Office- und Datenbankentwicklern können XML-Dateien sinnvoll eingesetzt werden. Eine Möglichkeit XML-Dateien zu lesen und zu schreiben ist das XML-DOM, das hier vorgestellt werden soll.

Anwendungsmöglichkeiten und Systemvoraussetzungen

XML-Dateien können eingesetzt werden, um Programmeinstellungen speichern, Daten für die Verwendung in anderen Anwendungen zu exportieren oder auch Daten in die Datenbank zu importieren. Gerade in Access spielt die Speicherung von Programmeinstellungen in externen Dateien zwar eine untergeordnete Rolle, weil dazu sehr gut auch Tabellen verwendet werden können, dennoch macht dies in Einzelfällen Sinn, nämlich dann, wenn die Einstellungen auch von anderen Programmen genutzt werden sollen.

Einlesen lässt sich eine XML-Datei natürlich auch als ganz normale Textdatei. Allerdings ist die Ermittlung der Tags und Attribute dann mit sehr viel Aufwand verbunden. Besser ist da die Möglichkeit über das XML-DOM auf die einzelnen XML-Nodes und ihre Attribute zuzugreifen.

Was ist XML?

Eine XML-Datei besteht ähnlich wie HTML-Dateien aus einzelnen Tags, die in spitze Klammern eingefasst werden. Innerhalb der Tags können Werte oder wieder andere Tags stehen. Zudem kann ein Tag Attribute enthalten, die innerhalb des öffnenden Tags definiert werden. Der Ausdruck <export format=“excel5″>daten.xls</export> stellt beispielsweise einen gültigen XML-Tag dar. Dabei ist daten.xls der Inhalt oder Wert des Tags und excel5 der Wert des Attributs format. XML-Dateien sind hierarchisch geordnet. Das heißt Tags können ineinander verschachtelt werden. Gültig ist eine XML-Datei allerdings nur dann, wenn es auf oberster Ebene nur einen Tag gibt, dem alle anderen untergeordnet sind. Dieser oberste Tag wird Wurzelelement oder Root-Element genannt.

XML ist ein W3C-Standard, genau wie das XML DOM. Dabei handelt es sich um das Objektmodell in dem eine XML-Datei dargestellt werden kann. Es verfügt über Methoden und Eigenschaften, um Elemente der XML-Datei auszulesen oder zu ändern. Ein XML-Tag oder ein Wert des Tags wird im DOM als XML-Node-Objekt bezeichnet. Jedes Element der XML-Datei, egal ob es sich um einen Tag oder einen Textwert handelt, wird auch als Knoten bezeichnet.

Um das XML-DOM zum Zugriff auf die XML-Datei verwenden zu können, ist ein ActiveX-Objekt erforderlich, dass der Internet Explorer 5 und höher bereits mitbringt, nämlich das MSXML-Objekt. Dieses wird nachfolgend zum Lesen der XML-Datei eingesetzt. Damit das problemlos und einfach funktioniert muss in der Datenbank ein Verweis auf die Objektbibliothek Microsoft XML, Version 2.0 gesetzt werden, der über Extras/Verweis in der Modulansicht oder Entwicklungsumgebung erstellt werden kann.

Eine XML-Datei lesen

Da eine XML-Datei einen hierarchischen Aufbau hat und nach dem Einlesen der Datei nicht auf Anhieb zu ermitteln ist, wie viele untergeordnete Knoten der Wurzelknoten hat und wie tief die Verschachtelung ist, lässt sich eine XML-Datei nur rekursiv auslesen.

Für jeden Knoten kann dazu über die hasChildNodes-Methode ermittelt werden, ob er untergeordnete Knoten hat. Wenn ein Knoten erreicht ist, der keine untergeordneten Knoten hat, wird die Prozedur einfach nicht mehr aufgerufen.

Bevor die Knoten rekursiv durchlaufen werden können, muss aber zunächst einmal das XML-DOM-Objekt erzeugt und die Datei geladen werden. Das erledigt im Listing 1 die Prozedur XMLLesen. Sie definiert zunächst die benötigten Variablen xmlRoot und xmlDoc. Die Variable xmlRoot soll das Wurzelelement der Datei speichern und muss daher den Typ MSXML.IXMLDOMNode haben. Dieses Objekt stellt ein allgemeines Node-Objekt dar, dem auch speziellere Node-Objekte zugewiesen werden können. Dies können bspw. XML-Elemente wie normale Tags, Textelemente wie die Inhalte von Tags, Kommentare oder auch XML-Attribute sein. Die Variable xmlDoc stellt das XML-DOM dar. Mit dem Schlüsselwort New wird es zunächst als leeres DOM erzeugt. Eventuell muss New durch CreateObject ersetzt werden

In älteren VBA-Versionen, in denen der New-Operator noch nicht zur Verfügung steht muss das DOMDocument-Objekt mit den folgenden Anweisungen erzeugt werden.

Dim xmlDoc As MSXML.DOMDocument
Set xmlDoc = createObject("MSXML.DOMDocument")

Wenn das Objekt erzeugt ist, kann die Methode Load verwendet werden, um eine XML-Datei zu laden. Liegt der zu lesende Code nicht in Dateiform vor, kann dieser Code ebenfalls eingelesen werden, nämlich über die LoadXML-Methode. Kann der Code bzw. die Datei eingelesen werden, wird dadurch das DOMDocument-Objekt gefüllt. Sollte der einzulesende Text oder die Datei keinen gültigen XML-Code enthalten, tritt allerdings beim Einlesen des Codes ein Laufzeitfehler auf.

Das Wurzelelement kann nach dem Einlesen über die documentElement-Methode ermittelt werden, dieses wird dann an die Prozedur ZweigLesen übergeben. Sie durchsucht die Datei rekursiv und gibt Namen und Werte der Knoten im Direktfenster aus.

Listing 1: Einlesen einer XML-Datei

Const strPFAD = "C:\TreeView_XML_0402\"
Sub XMLLesen()
Dim strFilename As String
Dim xmlRoot As MSXML.IXMLDOMNode
Dim xmlDoc As New MSXML.DOMDocument
strFilename = "buecher.xml"
xmlDoc.Load (strPFAD & strFilename)
Set xmlRoot = xmlDoc.documentElement()
ZweigLesen xmlRoot
Set xmlRoot = Nothing
Set xmlDoc = Nothing
End Sub

Wie jede rekursive Prozedur besteht auch die hier verwendete aus zwei Teilen. Der eine wird ausgeführt, wenn der aktuelle XML-Knoten den Knotentyp NODE_ELEMENT hat. In diesem Fall handelt es sich um einen XML-Tag und nicht nur um einen Kommentar oder den Inhalt eines Tags.

Hinweis:

Ob diese Prüfung sinnvoll ist, hängt vom Zweck der Prozedur ab. Wenn nur Knotenname und Werte ausgegeben werden sollen, ist dies notwendig. Im Beitrag zum TreeView-Steuerelement wird eine Abwandlung der Prozedur verwendet, wo eine andere Bedingung definiert ist. In jedem Fall muss der erste Teil der rekursiven Prozedur unabhängig davon ausgeführt werden, ob es untergeordnete Elemente gibt.  Wenn im ersten Teil der Prozedur die Prozedur wieder aufgerufen wird, erfolgt die Ausgabe von unten nach oben, das heißt das letzte Element wird zuerst ausgegeben.

Handelt es sich um einen XML-Tag, wird geprüft ob es das Wurzelelement ist. Wenn nicht wird der Name des Elements mit der nodeName-Eigenschaft ausgegeben.  Als nächstes wird dann geprüft, ob es für den XML-Tag einen Inhalt gibt. Auch Textinhalte werden als Node-Objekte behandelt. Das heißt, nur wenn die Methode hasChildNodes den Wert true liefert kann das Element einen Text haben. Dies ist in der Regel das erste untergeordnete Element, das mit der firstChild-Eigenschaft ermittelt werden kann. Handelt es sich dabei um ein Textelement, gibt die Eigenschaft die Konstante NODE_TEXT zurück. In diesem Fall soll der Wert des ersten untergeordneten XML-Elements ausgegeben werden, wenn nicht, eine leere Zeichenkette. Im zweiten Teil der Prozedur wird nun geprüft, ob das als Parameter übergebene XML-Element untergeordnete Knoten hat. Wenn ja, wird die ChildNodes-Auflistung in einer Schleife durchlaufen und die Prozedur für jeden untergeordneten Knoten erneut aufgerufen.

Listing 2: Rekursives lesen der XML-Knoten

Sub ZweigLesen(xmlNode As MSXML.IXMLDOMNode)
Dim xmlTmp As MSXML.IXMLDOMNode
Dim xmlTmpElem As MSXML.IXMLDOMElement
If xmlNode.nodeType = NODE_ELEMENT Then
Set xmlTmpElem = xmlNode
If xmlTmpElem.baseName <> _
xmlNode.ownerDocument.documentElement.baseName Then
Debug.Print xmlTmpElem.nodeName;
If xmlTmpElem.hasChildNodes Then
If xmlTmpElem.firstChild.nodeType = NODE_TEXT Then
Debug.Print ":" & xmlTmpElem.firstChild.nodeValue
Else
Debug.Print ""
End If
Else
Debug.Print ""
End If
End If
End If
If xmlNode.hasChildNodes = True Then
For Each xmlTmp In xmlNode.childNodes()
ZweigLesen xmlTmp
Next xmlTmp
End If
End Sub

XML-Dateien ändern


Das XML-DOM kann allerdings nicht nur zum Lesen der XML-Dateien verwendet werden, es stellt auch eine Save-Methode zur Verfügung mit der die XML-Datei gespeichert werden kann. Diese kann zudem vorher geändert und ergänzt werden. Die Prozeduren  in Listing 3 zeigen, wie ein Datensatz an eine bestehende XML-Datei angehängt werden kann.

Jeder Datensatz wird dabei durch einen <buch>-Knoten dargestellt, für den über das id-Attribut ein eindeutiger Schlüssel definiert ist. Um einen neuen Datensatz anzufügen, muss also zunächst der letzte Schlüssel ermittelt und der nächste berechnet werden. Dazu wird die Datei eingelesen und wieder das Wurzelelement ermittelt. Hat es untergeordnete Elemente, sind Datensätze vorhanden. In diesem Fall wird über die lastChild-Eigenschaft das letzte Element zurückgegeben und es wird geprüft, ob dieses Element Attribute hat. Die Attribute werden in der Attributs-Auflistung verwaltet, deren length-Eigenschaft ihre Anzahl angibt. Enthält der XML-Tag Attribute, wird mit der Methoode getNamedItem das id-Attribut zurückgegeben und dessen Wert über die nodeValue-Eigenschaft in der Variablen strTmp gespeichert. Wenn es kein id-Attribut gibt, kommt es dabei jedoch zu einem Laufzeitfehler, der mit On Error Resume Next ignoriert wird. In diesem Fall wird als Nummer der Wert 1 ermittelt. Gibt es das Attribut wird der Wert nach dem Zeichen # ermittelt in eine Zahl konvertiert und 1 dazu addiert. Dies ist dann die Nummer für den neuen Datensatz.

Anschließend wird der neue Datensatz eingefügt. Dazu muss zunächst das übergeordnete <buch>-Element erzeugt werden. Das geschieht, wie auch bei den untergeordneten Elementen über die CreateElement-Methode des DOMDocument-Objekts. Das so erzeugt Element existiert nun zwar, ist jedoch noch nicht in die Knotenhierarchie eingeordnet, sondern existiert quasi freischwebend im XML-DOM. Mit der AppendChild-Methode kann es dann an das übergeordnete Element angehängt werden. Für die einzelnen Tags <titel>, <autor> etc. ist dies das <buch>-Element. Das <buch>-Element wird hingegen an das Wurzelelement der Datei angehängt.

Bevor das jedoch passiert wird noch das Attribut erzeugt. Attribute können analog zur Methode createElement mit der createAttribute-Methode erzeugt werden. Deren Wert lässt sich dann über die Eigenschaft Value bestimmen. Um das Attribut dem XML-Tag hinzuzufügen, wird es der setNamedItem-Methode übergeben. Mit Aufruf der Save-Methode wird die Datei dann gespeichert.

Listing 3: Hinzufügen von Datensätzen zur XML-Datei

Sub Aufruf()
DatensatzAnfuegen strPFAD & "buecher.xml", "3815821762", _
"Helma Spona", "Das Große Buch Access 2002 Programmierung", "40,88"
End Sub

Sub DatensatzAnfuegen(strDatei As String, strISBN As String, strAutor As String, strTitel As String, strPreis As String)
'ID ermitteln
Dim xmlRoot As MSXML.IXMLDOMNode
Dim xmlNode As MSXML.IXMLDOMNode
Dim xmlDoc As New MSXML.DOMDocument
Dim xmlElem As MSXML.IXMLDOMElement
Dim xmlElem2 As MSXML.IXMLDOMElement
Dim xmlAttr As MSXML.IXMLDOMAttribute
Dim lngID As Long
Dim bytPos As Byte
Dim strTmp As String

xmlDoc.Load (strDatei)
Set xmlRoot = xmlDoc.documentElement
If xmlRoot.hasChildNodes Then
Set xmlNode = xmlRoot.lastChild()
If xmlNode.Attributes.length > 0 Then
On Error Resume Next
strTmp = xmlNode.Attributes.getNamedItem("id").nodeValue
bytPos = InStr(1, strTmp, "#", vbTextCompare)
If bytPos > 0 Then
strTmp = Mid(strTmp, bytPos + 1)
End If
If strTmp <> "" Then
lngID = Val(strTmp) + 1
Else
lngID = 1
End If
End If
Else
lngID = 1
End If

'Datensatz anfügen
Set xmlElem = xmlDoc.createElement("buch")
Set xmlElem2 = xmlDoc.createElement("ISBN")
xmlElem2.Text = strISBN
xmlElem.appendChild xmlElem2
Set xmlElem2 = xmlDoc.createElement("titel")
xmlElem2.Text = strTitel
xmlElem.appendChild xmlElem2
Set xmlElem2 = xmlDoc.createElement("autor")
xmlElem2.Text = strAutor
xmlElem.appendChild xmlElem2
Set xmlElem2 = xmlDoc.createElement("preis")
xmlElem2.Text = strPreis
xmlElem.appendChild xmlElem2
'id-Attribut hinzufügen
Set xmlAttr = xmlDoc.createAttribute("id")
xmlAttr.Value = "buch#" & lngID
xmlElem.Attributes.setNamedItem xmlAttr
xmlRoot.appendChild xmlElem
'Datei speichern
xmlDoc.Save strDatei
Set xmlNode = Nothing
Set xmlRoot = Nothing
Set xmlDoc = Nothing
End Sub

Fazit

Schreiben und lesen von XML-Dateien ist mit Hilfe des passenden MSXML-Dom-Objekts gar nicht schwer und eignet sich vor allem bei großen Dateien, weil der Zugriff auf die Knoten einer XML-Datei wesentlich schneller ist, das ein Textdatei sequentiell zu lesen und die Werte über entsprechenden String-Funktionen zu ermitteln. Vor allem wenn XML-Dateien zur Speicherung von Programmeinstellungen verwendet wird, lassen sich auch sehr gut im TreeView-Steuerelement als Baumstruktur darstellen, wie im Beitrag zu diesem Steuerelement noch gezeigt wird.

Rechtliche Rahmenbedingungen

Alle Inhalte wurden nach bestem Wissen und Gewissen zusammengestellt. Ich übernehme für Fehlerfreiheit allerdings keine Gewähr und hafte keinesfalls für Folgen, die sich aus Fehlern oder unsachgemäßem Gebrauch des Codes und der Inhalte ergeben.
Code und Beispiele dürfen für den privaten Gebrauch frei verwendet werden. Eine Veröffentlichung (auch auszugsweise) sowohl online wie auch auf klassischen Medien ist nur nach meiner ausdrücklichen Zustimmung möglich. Verlinkung der Artikel ist jedoch erlaubt und erwünscht, solange immer auf die Webseite und nicht die PDF-Datei verlinkt wird.

Tipps und Tricks zu Excel

Die Arbeit mit Excel ist oft mit Zellfunktionen, Formeln und VBA verbunden. Daher finden Sie den größten Teil der Excel-Tipps auch in der VBA-Rubrik.

Excel 95 und höher: Arbeitsmappen per Mausklick aus Arbeitsmappenvorlagen erzeugen

Nicht immer ist es notwendig, eine Arbeitsmappenvorlage („*.xlt“) in das Vorlagenverzeichnis zu kopieren, um daraus neue Arbeitsmappen zu erzeugen. Es genügt, im Arbeitsplatz oder Explorer mit der rechten Maustaste auf die Datei zu klicken und dann „Neu“ aus dem Kontextmenü auszuwählen. Alternativ können Sie auch einen Doppelklick auf die XLT-Datei ausführen.

Excel: Zeilen fixieren

In längeren Tabellen ist es oft sehr mühsam, Daten einzugeben, weil die Spaltenbeschriftungen nicht sichtbar sind. Das können Sie abstellen, indem Sie die Zeile mit den Spaltenbeschriftungen fixieren. Markieren Sie dazu die Zeile unterhalb der Zeile mit den Spaltenbeschriftungen und wählen Sie Fenster/fixieren aus dem Menü aus.

Excel: Zahlen als Texte in Zellen eingeben

Wenn Sie in Excel Werte in Zellen eingeben, richtet Excel sie automatisch aus, je nachdem ob es sich um Zahlen oder Text handelt. Wenn Sie Zahlen in eine Zelle eingeben möchten, die wie Text behandelt werden soll, können Sie das erreichen, indem Sie ein Hochkomma ‚ vor die Zahl setzen. Dieses kennzeichnet die Zahl als Zeichenkette.

Excel: Verwendung der Wenn-Funktion

Wenn Sie in einer Tabelle eine Liste berechnen, bspw. die Rückzahlung eines Kredites und nicht wissen, wie viele Zeilen notwendig sind, ist es ganz nützlich in den berechneten Zellen nur dann einen Wert auszugeben, wenn dieser eine Bedingung erfüllt. Eine solche Bedingung könnte z.B. sein, dass der berechnete Wert ungleich als 0 ist. Dazu fügen Sie in die Zellen die Wenn-Funktion ein. Die Syntax der Funktion lautet: =Wenn(Bedingung;Formel Ja-Zweig; Formel Nein-Zweig)>

Möchten Sie bspw. in der Spalte C: die Differenz der Spalten A und B berechnen und diese nur dann ausgeben, wenn Sie ungleich 0 ist, müssten Sie in Spalte C folgende Formel eingeben:

=WENN(A1-B1<>0;A1-B1;"")

Die Formel berechnet also zunächst A1-B1 und prüft, ob dieser Wert größer als 0 ist. Wenn ja, wird die Formel A1-B1 berechnet und zurückgegeben. Wenn nicht, wird eine leere Zeichenkette „“ zurückgegeben. Der Rückgabewert der Funktion ist gleichzeitig der Wert der Zelle.

Rechtliche Rahmenbedingungen

Alle Inhalte wurden nach bestem Wissen und Gewissen zusammengestellt. Ich übernehme für Fehlerfreiheit allerdings keine Gewähr und hafte keinesfalls für Folgen, die sich aus Fehlern oder unsachgemäßem Gebrauch des Codes und der Inhalte ergeben.
Code und Beispiele dürfen für den privaten Gebrauch frei verwendet werden. Eine Veröffentlichung (auch auszugsweise) sowohl online wie auch auf klassischen Medien ist nur nach meiner ausdrücklichen Zustimmung möglich. Verlinkung der Artikel ist jedoch erlaubt und erwünscht, solange immer auf die Webseite und nicht die PDF-Datei verlinkt wird.