ACCESS GRAFIKEN MIT SVG IM UNTERNEHMEN. In diesem Heft: VEREINSVERWALTUNG: FORMULARE, TEIL 1 REFACTORING EINFACHE REIHENFOLGE SEITE 66

Größe: px
Ab Seite anzeigen:

Download "ACCESS GRAFIKEN MIT SVG IM UNTERNEHMEN. In diesem Heft: VEREINSVERWALTUNG: FORMULARE, TEIL 1 REFACTORING EINFACHE REIHENFOLGE SEITE 66"

Transkript

1 Ausgabe 03/2018 GRAFIKEN MIT SVG Peppen Sie Ihre Anwendung mit grafischen Darstellungen Ihrer Daten auf und nutzen Sie SVG dazu (ab S. 37) In diesem Heft: VEREINSVERWALTUNG: FORMULARE, TEIL 1 Entwickeln Sie die Benutzeroberfläche für die Vereinsverwaltung. REFACTORING Passen Sie die übrigen Elemente einer Anwendung an neue Objektund Feldnamen an. EINFACHE REIHENFOLGE Lernen Sie eine einfache Methode zum Festlegen einer individuellen Reihenfolge von Daten kennen. SEITE 66 SEITE 55 SEITE 13 Mat-Nr. H

2 INHALTSVERZEICHNIS/IMPRESSUM FORMULARE UND STEUERELEMENTE Löschen in Formularen: Ereignisse 2 Reihenfolge einfach festlegen 13 Löschen im Listenfeld per Tastatur 22 Zeiträume per Listenfeld und InputBox 28 INTERAKTIV SVG als Grafikkomponente 37 VBA UND PROGRAMMIERTECHNIK Objekt- und Feldnamen refaktorieren 55 LÖSUNGEN Vereinsverwaltung: Formulare, Teil 1 66 SERVICE Impressum U2 Editorial 1 DOWNLOAD Die Downloads zu dieser Ausgabe finden Sie wie gewohnt unter: Benutzername: listenfeld Kennwort: loeschen Die Direktlinks zu den Downloads befinden sich am unteren Rand des jeweiligen Beitrags. Impressum ISBN ISSN: Mat.-Nr Access im Unternehmen Haufe-Lexware GmbH & Co. KG, Munzinger Str. 9, Freiburg Kommanditgesellschaft, Sitz Freiburg Registergericht Freiburg, HRA 4408 Komplementäre: Haufe-Lexware Verwaltungs GmbH, Sitz Freiburg, Registergericht Freiburg, HRB 5557; Martin Laqua Geschäftsführung: Isabel Blank, Sandra Dittert, Jörg Frey, Birte Hackenjos, Dominik Hartmann, Markus Reithwiesner, Joachim Rotzinger, Dr. Carsten Thies Beiratsvorsitzende: Andrea Haufe Steuernummer: 06392/11008 Umsatzsteuer-Identifikationsnummer: DE Redaktion: Dipl.-Ing. André Minhorst (Chefredakteur, V.i.S.d.P.), Sabine Wißler (Redaktionsassistentin), Sascha Trowitzsch (Fachlektorat) Herausgeber: Dipl.-Ing. André Minhorst Autoren: Dipl.-Ing. André Minhorst, Sascha Trowitzsch Anschrift der Redaktion: Postfach , Freiburg, Tel. 0761/898-0, Fax: 0761/ , computer@haufe.de, Internet: Druck: Schätzl Druck & Medien GmbH & Co. KG, Donauwörth Auslieferung und Vertretung für die Schweiz: H.R. Balmer AG, Neugasse 12, CH-6301 Zug, Tel. 041/ , Fax: 041/ Das Update-Heft und alle darin enthaltenen Beiträge und Abbildungen sind urheberrechtlich geschützt. Jede Verwertung, die nicht ausdrücklich vom Urheberrechtsgesetz zugelassen ist, bedarf der vorherigen Zustimmung des Verlags. Das gilt insbesondere für Vervielfältigungen, Bearbeitungen, Übersetzungen, Mikroverfilmung und für die Einspeicherung in elektronische Systeme. Wir weisen darauf hin, dass die verwendeten Bezeichnungen und Markennamen der jeweiligen Firmen im Allgemeinen warenzeichen-, marken- oder patentrechtlichem Schutz unterliegen. Die im Werk gemachten Angaben erfolgen nach bestem Wissen, jedoch ohne Gewähr. Für mögliche Schäden, die im Zusammenhang mit den Angaben im Werk stehen könnten, wird keine Gewährleistung übernommen. Seite U2

3 EDITORIAL Ein Bild sagt tausend Worte Die grafische Darstellung von Sachverhalten unter Access wird etwas stiefmütterlich behandelt. Es gibt keine wirklich brauchbare und einfach bedienbare Möglichkeit, etwa schnell ein benutzerdefiniertes Diagramm auf das Formular oder in den Bericht zu zaubern. Dies wollen wir ändern und liefern in dieser Ausgabe den ersten Beitrag über die Erstellung von Grafiken über SVG (Scalable Vector Graphics). In diesem Beitrag namens SVG als Grafikkomponente, den Sie ab Seite 37 finden, liefert Sascha Trowitzsch zuerst die Grundlagen zum Erstellen grafischer Objekte und zeigt dann, wie Sie ein Diagramm erstellen, dessen Elemente sogar anklickbar sind. Dort zeigen wir beispielsweise den Gesamtumsatz nach Ländern an und erlauben per Mausklick den Zugriff auf die ID des jeweiligen Landes. Das können Sie leicht ausbauen, indem Sie etwa ein Detailformular mit weiteren Informationen zu den in diesem Land getätigten Umsätzen anzeigen. Seien Sie auch gespannt auf die Fortsetzung in der nächsten Ausgabe! In der aktuellen Ausgabe führen wir außerdem unsere Reihe zum Thema Vereinsverwaltung fort. Nachdem wir in Ausgabe 6/2017 gezeigt haben, wie Sie das Datenmodell für die Vereinsverwaltung herleiten und die Daten von Excel nach Access migrieren, gehen wir in dieser Ausgabe einen Schritt weiter. Unter dem Titel Vereinsverwaltung: Formulare, Teil 1 zeigen wir ab Seite 66, wie Sie das Detailformular zur Anzeige von Mitgliedern aufbauen. Der Beitrag Zeiträume per Listenfeld und InputBox zeigt ab Seite 28, wie Sie Zeiträume wie etwa Vereinszugehörigkeiten in einem Listenfeld verwalten können. Die Details zu diesen Zeiträumen zeigen wir zum Bearbeiten diesmal nicht in einem eigenen Formular an, sondern liefern mit der InputBox-Funktion eine schlankere Möglichkeit. Außerdem zeigt dieser Beitrag, wie Sie prüfen können, ob neue Zeiträume zu den vorhandenen Zeiträumen passen und sich nicht etwa damit überschneiden. Darüber hinaus beschäftigen wir uns mit dem Löschen von Datensätzen in Formularen genauer gesagt mit den dabei ausgelöste Ereignissen. Es gibt nämlich nicht nur ein Ereignis, das beim Löschen ausgelöst wird, sondern in der Regel gleich drei. Mit diesen können Sie den Löschvorgang beeinflussen und beispielsweise die eingebauten Meldungen zur Löschbestätigung durch benutzerdefinierte Meldungen ersetzen. Wie das gelingt, erfahren Sie unter dem Titel Löschen in Formularen: Ereignisse ab Seite 2. Ein weiterer Beitrag rund um das Löschen von Daten, diesmal mit dem Listenfeld als Mittelpunkt, startet unter dem Titel Löschen im Listenfeld auf Seite 22. Hier erfahren Sie, wie Sie ein Listenfeld um Funktionen erweitern, mit denen der Benutzer einen oder mehrere Einträge einfach mit der Entf-Taste löschen kann. Der Beitrag Reihenfolge einfach festlegen liefert ab Seite 13 eine kleine Lösung, mit der Sie einem Formular in der Datenblatt- oder Endlosansicht eine einfache Funktion zum Sortieren der angezeigten Daten hinzufügen können. Und schließlich kümmern wir uns noch um das Refactoring nach dem Umbenennen von Objekt- und Feldnamen: Welche Schritte erledigt Access automatisch, wenn Sie etwa einen Tabellennamen geändert haben und welche müssen Sie selbst noch durchführen, damit der Rest der Anwendung an die neue Tabellenbezeichnung angepasst wird? Danke an dieser Stelle an Lorenz Hölscher für die Anregung! Und nun: Viel Spaß beim Lesen! Ihr André Minhorst Seite 1

4 FORMULARE UND STEUERELEMENTE LÖSCHEN IN FORMULAREN: EREIGNISSE Löschen in Formularen: Ereignisse Das Löschen von Datensätzen in einem Formular ist eigentlich kein großes Problem: Man markiert den Datensatz über den Datensatzmarkierer und klickt auf die Entfernen- Taste. Manch ein Benutzer verzweifelt vielleicht daran, den Datensatz über den Datensatzmarkierer zu selektieren, weshalb er ihn dann nicht löschen kann dann baut man ihm halt eine Schaltfläche, die auch den Datensatz löscht, der aktuell den Fokus hat. Was aber, wenn wir noch Aktionen durchführen wollen, bevor der Datensatz gelöscht wird beispielsweise das Archivieren des Datensatzes oder das Ausführen weiterer Aktionen nach dem Löschen? Wie das funktioniert und was Sie beachten müssen, zeigt der vorliegende Beitrag. Als Beispiel greifen wir die Datenbank aus dem Beitrag Reihenfolge einfach festlegen auf ( Hier verwenden wir ein Formular mit einem Unterformular, um die Daten der Tabelle tblartikel in der Datenblattansicht anzuzeigen. Ereignisse beim Löschen Beim Löschen eines Datensatzes in einem Formular werden verschiedene Ereignisse ausgelöst. Wenn wir im Eigenschaftsfenster des Unterformulars zum Bereich Ereignis wechseln, finden wir einige Ereignisse, die möglicherweise in Zusammenhang mit dem Löschen eines Datensatzes ausgelöst werden. Genau das wollen wir untersuchen, also legen wir für die in Bild 1 mit dem Wert [Ereignisprozedur] versehenen Eigenschaften entsprechende Ereignisprozeduren an. Die dadurch automatisch im Klassenmodul des entsprechenden Formulars angelegten Ereignisprozeduren statten wir mit jeweils einer Debug.Print-Anweisung aus, welche lediglich den Namen der Ereignisprozedur im Direktfenster ausgibt. Die Prozeduren sehen dann wie folgt aus: Private Sub Form_AfterDelConfirm(Status As Integer) Debug.Print "AfterDelConfirm" Private Sub Form_AfterUpdate() Debug.Print "AfterUpdate" Bild 1: Ereignisse, die möglicherweise beim Löschen ausgelöst werden Private Sub Form_BeforeDelConfirm(Cancel As Integer, _ Response As Integer) Debug.Print "BeforeDelConfirm" Seite 2

5 FORMULARE UND STEUERELEMENTE LÖSCHEN IN FORMULAREN: EREIGNISSE Bild 2: Meldung beim Löschen eines Datensatzes Private Sub Form_BeforeUpdate(Cancel As Integer) Debug.Print "BeforeUpdate" Private Sub Form_Current() Debug.Print "Current" Private Sub Form_Delete(Cancel As Integer) Debug.Print "Delete" Wenn Sie nun einen Datensatz im Unterformular markieren und diesen mit der Entf-Taste oder dem entsprechenden Ribbon-Eintrag löschen, erscheint die Meldung aus Bild 2. Diese Meldung besagt, dass das Löschen dieses Datensatzes das Löschen verknüpfter Datensätze (in diesem Fall aus der Tabelle tblbestelldetails) nach sich ziehen würde. Wenn Sie einen neuen Datensatz in diesem Formular anlegen, der noch keine verknüpften Datensätze in der Tabelle tblbestelldetails aufweist und diesen löschen, erscheint eine andere Meldung nämlich die aus Bild 3. Zu diesem Zeitpunkt werfen wir auch einen Blick in den Direktbereich des VBA-Editors. Dieser sieht nun wie in Bild 4 aus. Wir merken uns den aktuellen Zustand und bestätigen die Löschmeldung mit einem Klick auf die Schaltfläche Ja. Im Direktbereich sieht es dann wie folgt aus, wobei wir den Zeitpunkt der Anzeige der Löschmeldung hier durch einen entsprechenden Kommentar eingefügt haben: Delete Current BeforeDelConfirm 'Meldung AfterDelConfirm Beim Löschen werden also vier Ereignisse ausgelöst: Bild 3: Meldung beim Löschen eines nicht mit anderen Datensätzen in Beziehung stehenden Datensatzes Seite 3

6 FORMULARE UND STEUERELEMENTE LÖSCHEN IN FORMULAREN: EREIGNISSE Beim Löschen Beim Anzeigen Vor Löschbestätigung Vor Löschbestätigung Bild 4: Der Direktbereich mitten in einem Löschvorgang Wenn wir beim Erscheinen des Meldungsfensters nicht auf Ja, sondern auf Nein klicken, löst dies noch ein Ereignis mehr aus: Delete Current BeforeDelConfirm Current AfterDelConfirm Das Current-Ereignis (Beim Anzeigen) wird hier ausgelöst, weil der Datensatzzeiger wieder auf den zu löschenden Datensatz eingestellt wird. Löschbestätigung deaktivieren Aber gab es da nicht die Möglichkeit, die Löschbestätigung zu deaktivieren und Datensätze direkt zu löschen, wenn man auf die Entf-Taste klickt? Ja, die gibt es. Dazu öffnen Sie den Optionen-Dialog von Access und wechseln dort zum Bereich Clienteinstellungen. Hier finden Sie unter Bestätigen drei Optionen: Datensatzänderungen, Löschen von Dokumenten und Aktionsabfragen (siehe Bild 5). In unserem Fall benötigen wir die Option Datensatzänderungen, die wir deaktivieren. Was geschieht nun im Direktbereich, wenn wir einen Datensatz löschen? Es werden nur noch die folgenden beiden Ereignisse ausgelöst: Delete Current Wann immer wir die Ereignisse mit Prozeduren versehen möchten, müssen wir also bei Vor Löschbestätigung und Nach Löschbestätigung berücksichtigen, dass diese bei deaktivierter Option Datensatzänderungen auf dem Client-Rechner nicht ausgeführt werden. Warnungen per VBA ausschalten Wenn Sie die Warnmeldungen unabhängig von den Einstellungen auf dem Clientrechner abschalten wollen, etwa um keine Meldung anzuzeigen, können Sie die Meldungen auch per VBA deaktivieren. Dazu rufen Sie einfach die Methode Set- Warnings des DoCmd-Objekts mit dem Parameter False auf: Bild 5: Meldung beim Löschen eines Datensatzes DoCmd.SetWarnings False Seite 4

7 FORMULARE UND STEUERELEMENTE LÖSCHEN IN FORMULAREN: EREIGNISSE Danach können Sie beispielsweise per VBA den Datensatz löschen und anschließend die Warnmeldungen wieder aktivieren: DoCmd.SetWarnings True Diese Einstellung hat keinen direkten Einfluss auf die Option Datensatzänderung; das heißt, sie ändert nicht den Wert dieser Option. Sie sorgt vielmehr dafür, dass Access zum Beispiel beim Durchführen von Änderungen wie auch beim Löschen von Daten so tut, als ob der Benutzer für alle angezeigten Warnmeldungen einfach die Eingabetaste betätigt. Es wird also jeweils die standardmäßige Aktion für die Warnmeldung ausgeführt. Beim Löschen eines Datensatzes handelt es sich also beispielsweise die als Standard markierte Schaltfläche Ja betätigt. Dies wirkt sich übrigens auf alle Aktionen aus, die mit einer Meldung reagieren, die lediglich Schaltflächen als Reaktionsmöglichkeit anbieten. Wenn Sie also etwa eine Parameterabfrage nutzen wollen, werden ihre Inputboxen auch bei abgeschalteten Meldungen angezeigt. Option per VBA ändern Sie können die Option Datensatzänderungen auch per VBA ändern. Und noch besser: Sie können diese sogar abfragen! Dies schauen wir uns zuerst an. Die Methode zum Abfragen vieler der Einstellungen aus dem Optionen- Dialog lautet GetOption. Sie erwartet den Namen der englischen Option als Parameter. Für die Option Datensatzänderungen heißt der Parameter beispielsweise Confirm Record Changes. Sie können den Wert mit der Debug.Print-Methode im Direktbereich des VBA-Editors ausgeben: Debug.Print GetOption ("Confirm Record Changes") -1 Dies liefert bei aktivierter Option beispielsweise den Wert -1, also True. Nun wollen wir die Option deaktivieren, Bild 6: Erfolgreich per VBA geänderte Option zunächst über die Benutzeroberfläche. Dazu entfernen Sie den Haken von der Option Datensatzänderungen und schließen den Optionen-Dialog wieder. Anschließend fragen wir den Wert der Option erneut über den Direktbereich ab: Debug.Print GetOption ("Confirm Record Changes") 0 Es gelingt wir konnten die Änderung erfolgreich per VBA abfragen. Nun wollen wir die Option per VBA wieder aktivieren. Dazu nutzen wir die Anweisung SetOption, wieder mit dem Namen der Option als ersten Parameter. Als zweiten Parameter geben wir den festzulegenden Wert an, in diesem Fall True: SetOption "Confirm Record Changes", True Wenn wir nun erneut den Optionen-Dialog öffnen, erhalten wir das Ergebnis aus Bild 6 die Option wurde erfolgreich eingestellt. Zum Deaktivieren der Option nutzen wir wieder die folgende Anweisung: SetOption "Confirm Record Changes", False Wenn der Benutzer nun für seine eigenen Anwendungen die Option Datensatzänderungen auf True eingestellt haben, Sie aber möchten, dass diese Option für einen besseren ergonomischen Ablauf bei der Benutzung Ihrer eigenen Anwendung deaktiviert wird, können Sie wie folgt vorgehen: Seite 5

8 FORMULARE UND STEUERELEMENTE LÖSCHEN IN FORMULAREN: EREIGNISSE Legen Sie für die Ereignisprozedur Beim Laden eine Prozedur an, welche die Option Confirm Record Changes prüft, den Wert in einer Variablen speichert und die Option dann gegebenenfalls auf den Wert False einstellt. Der Benutzer kann dann Datensatzänderungen durchführen, ohne dass die lästigen Meldungen angezeigt werden. Beim Schließen des Formulars wird das Ereignis Beim Entladen ausgelöst. Für dieses legen Sie eine weitere Prozedur an, welche den Wert der Option Confirm Record Changes wieder auf den beim Öffnen vorgefundenen Wert einstellt. Als Erstes benötigen wir dazu eine Boolean-Variable, in der wir den Zustand der Option Datensatzänderungen beim Öffnen des Formulars speichern. Diese deklarieren wir oben im allgemeinen Teil des Klassenmoduls: Dim bolconfirmrecordchanges As Boolean Die Ereignisprozedur, die durch das Ereignis Beim Laden ausgelöst wird, sieht wie folgt aus: Private Sub Form_Load() bolconfirmrecordchanges = _ GetOption("Confirm Record Changes") If bolconfirmrecordchanges = True Then SetOption "Confirm Record Changes", False Sie speichert zunächst den Wert der Option Confirm Record Changes in der Variablen bolconfirmrecordchanges. Hat diese dann den Wert True, stellt sie die Option mit der SetOption-Anweisung auf den Wert False ein. In der Prozedur, die durch das Ereignis Beim Entladen ausgelöst wird, stellen wir die Option dann einfach wieder auf den Wert der Variablen bolconfirmrecordchanges ein, den wir beim Öffnen ermittelt haben, und stellen so den Ausgangszustand wieder her: Private Sub Form_Unload(Cancel As Integer) SetOption "Confirm Record Changes", _ bolconfirmrecordchanges Standardmeldung durch eigene Meldung ersetzen Wenn Sie die Anzeige einer Warnmeldung vor dem Löschen von Datensätzen beibehalten wollen, aber die standardmäßig angezeigte Meldung ersetzen möchten, können Sie dies auch tun. Dazu hinterlegen wir eine Prozedur für das Ereignis Vor Löschbestätigung, die wie in Bild 7 aussieht. Die Prozedur fragt den Benutzer mit einer eigenen Meldung, ob dieser den Datensatz wirklich löschen will. Lautet das Ergebnis nein (vbno), stellt die Prozedur den Wert des Parameters Cancel auf den Wert True ein. Dadurch wird der Lösch-Vorgang abgebrochen. Damit die Private Sub Form_BeforeDelConfirm(Cancel As Integer, Response As Integer) Dim intresult As VbMsgBoxResult intresult = MsgBox("Möchten Sie den Datensatz wirklich löschen?", vbcritical + vbyesno, "Datensatz löschen") If intresult = vbno Then Cancel = True Response = acdataerrcontinue Listing 1: Anzeigen einer alternativen Meldung beim Löschen eines Datensatzes Seite 6

9 FORMULARE UND STEUERELEMENTE LÖSCHEN IN FORMULAREN: EREIGNISSE eingebaute Standardmeldung unterdrückt wird, legen wir außerdem für den Parameter Response den Wert acdata- ErrContinue fest. Das Ergebnis sieht nun wie in Bild 7 aus. Leider hat Access den Datensatz zu diesem Zeitpunkt bereits aus dem Formular entfernt und der Benutzer kann nicht prüfen, ob er wirklich den richtigen Datensatz gelöscht hat. Wie können wir dieses Problem lösen? Wir könnten beispielsweise versuchen, den Wert des Feldes Artikelname zu ermitteln und diesen in die Meldung einzuarbeiten, damit der Benutzer sieht, welchen Artikel er gerade löscht. Bild 7: Eigene Meldung beim Löschen eines Datensatzes Dim strdeleted As String Die Ereignisprozedur für das Ereignis Beim Löschen passen wir wie folgt an: Dummerweise können wir beim Auslösen der Ereignisprozedur Vor Löschbestätigung nicht mehr über den Bezug Me!Artikelname auf den Artikelnamen zugreifen, da der Datensatz zu diesem Zeitpunkt schon nicht mehr Bild 8: Um die Bezeichnung des zu löschenden Artikels erweiterte Löschbestätigung im Formular angezeigt wird. Der Datensatzzeiger zeigt hier auf den leeren, neuen Datensatz und Me!Artikelname liefert den Wert Null. Aber es gibt ja noch die Ereignisprozedur Beim Löschen. Wird diese ausgelöst, ist der Datensatz noch sichtbar. Wir können also hier beispielsweise den Namen des Artikels in eine Variable schreiben und ihren Inhalt dann in der Ereignisprozedur für Vor Löschbestätigung auslesen und nutzen. Private Sub Form_Delete(Cancel As Integer) strdeleted = Me!Artikelname Die MsgBox-Anweisung in der Prozedur Form_ BeforeDelConfirm erweitern wir dann wie folgt um den Namen des zu löschenden Artikels: Private Sub Form_BeforeDelConfirm(Cancel As Integer, _ Response As Integer) Dim intresult As VbMsgBoxResult intresult = MsgBox("Möchten Sie den Datensatz " µ... & vbcrlf & vbcrlf & "'" & strdeleted & "'" µ & vbcrlf & vbcrlf & "wirklich löschen?", µ vbcritical + vbyesno, "Datensatz löschen") Die Variable deklarieren wir wie folgt im allgemeinen Teil des Klassenmoduls zum Unterformular: Das Ergebnis ist die Meldung aus Bild 8, der die Bezeichnung des Artikels in einer eigenen Zeile hervorhebt. Seite 7

10 FORMULARE UND STEUERELEMENTE LÖSCHEN IN FORMULAREN: EREIGNISSE Private Sub Form_Delete(Cancel As Integer) Dim intresult As VbMsgBoxResult intresult = MsgBox("Möchten Sie den markierten Datensatz wirklich löschen?", vbcritical + vbyesno, _ "Datensatz löschen") If intresult = vbno Then Cancel = True Listing 2: Anzeigen einer alternativen Meldung beim Löschen eines Datensatzes Löschbestätigung bei angezeigtem Datensatz Wie so oft gibt es aber in Access verschiedene Methoden, um eine Aufgabe zu lösen. Wie wäre es beispielsweise, wenn der Datensatz beim Auftauchen der Meldung zur Löschbestätigung noch vorhanden und markiert ist? Dann könnte der Benutzer noch einmal genau auf den Datensatz schauen und sich dann überlegen, ob er diesen löschen möchte. Auch das können wir realisieren, denn es gibt ja auch noch die Ereignisprozedur Beim Löschen. Bild 9: Löschbestätigung bei noch sichtbarem Datensatz Diese gestalten wir in der Kombination aus dem Hauptformular frmartikelloeschen und dem Unterformular sfmartikelloeschen wie in Listing 2. Da das Ereignis Beim Löschen direkt nach dem Betätigen der Entf-Taste beziehungsweise des entsprechenden Ribbon-Befehls, aber vor dem Entfernen des Datensatzes ausgeführt wird, ist der zu löschende Datensatz zu diesem Zeitpunkt noch sichtbar. Außerdem ist der zu löschende Datensatz auch noch markiert, was den Bedienkomfort maximiert siehe auch Bild 9. Um das Beispiel abzurunden, müssen wir nur noch dafür sorgen, dass die Meldung, die Access standardmäßig anzeigt, unterbunden wird. Dazu nutzen wir wiederum die Prozedur, die durch das Ereignis Vor Löschbestätigung ausgeführt wird. Hier stellen wir einfach nur den Parameter Response auf den Wert acdataerrcontinue ein: Private Sub Form_BeforeDelConfirm(Cancel As Integer, _ Response As Integer) Response = acdataerrcontinue Standard-Löschbestätigung unterbinden, kurze Version Daraus können wir dann auch noch eine Methode zum Unterbinden der Standard-Löschbestätigungen ableiten, ohne dass wir die Option Datensatzänderungen beziehungsweise Confirm Record Changes anpassen müssen. Dazu brauchen Sie einfach nur den Parameter Response Seite 8

11 FORMULARE UND STEUERELEMENTE LÖSCHEN IN FORMULAREN: EREIGNISSE in der Ereignisprozedur Form_BeforeDelConfirm auf den Wert acdataerrcontinue einstellen: Private Sub Form_BeforeDelConfirm(Cancel As Integer, _ Response As Integer) Response = acdataerrcontinue Dieses Beispiel finden Sie in den beiden Formularen frmartikelloeschen_ii und sfmartikel- Loeschen_II. Löschen mehrerer Datensätze gleichzeitig Die Vorgehensweise mit dem Verbleib der Anzeige der zu löschenden Datensätze gelingt sogar beim Löschen mehrerer Datensätze (siehe Bild 10). Bild 10: Löschbestätigung beim Löschen mehrerer Datensätze sondern die Begrenzung der Anzeige der Meldung auf ein einziges Mal. Dazu haben wir das Klassenmodul des Unterformulars frmartikelloeschen_ii mit dem Code aus Listing 3 gefüllt. Allerdings stimmt genau genommen hier der Text der Meldung nicht mehr, denn diese bezieht sich ja nur auf einen einzigen Datensatz. Bekommen wir es der Ordnung halber hier noch hin, den Text auch für mehrere Datensätze passend zu machen? Klar: Man könnte einfach einen Text wie Möchten Sie den/die markierten Datensätze löschen? einsetzen. Wir benötigen drei Variablen: intloeschengesamt nimmt die Anzahl der gesamten zu löschenden Datensätze auf. intloeschenaktuell nimmt die Anzahl der Datensätze auf, die aktuell noch zu löschen sind. Aber das ist uns etwas zu einfach. Außerdem zeigt sich bei näherer Betrachtung, dass die Meldungsbox für jeden zu löschenden Datensatz angezeigt wird. Die Ereignisprozedur Form_Delete wird also für jeden zu löschenden Datensatz einmal aufgerufen. Also suchen wir einen Weg, wie Sie vor dem Löschen ermitteln können, wie viele Datensätze der Benutzer im Datenblatt markiert hat und wie Sie dafür sorgen, dass die Meldung nur einmal angezeigt wird. Das Hauptproblem dabei ist nicht, die Meldung abhängig von der Anzahl im Singular oder Plural zu definieren, bolnichtloeschen speichert, ob der Benutzer in der Meldung auf Ja (löschen) oder Nein (nicht löschen) geklickt hat. Die Form_Delete-Ereignisprozedur prüft, ob die Variable intloeschenaktuell den Wert 0 enthält, was der Fall ist, wenn sie bei einem Löschvorgang von einem oder mehreren Datensätzen zum ersten Mal aufgerufen wird. In diesem Fall ermittelt sie die Anzahl der aktuell markierten Datensätze über die Eigenschaft SelHeight des Formulars. Hier lauert die erste Gefahr: Wenn der Benutzer nur einen Teil der Datensätze markiert hat, liefert Sel- Seite 9

12 FORMULARE UND STEUERELEMENTE LÖSCHEN IN FORMULAREN: EREIGNISSE Dim intloeschengesamt As Integer Dim intloeschenaktuell As Integer Dim bolnichtloeschen As Boolean Private Sub Form_BeforeDelConfirm(Cancel As Integer, Response As Integer) Response = acdataerrcontinue Private Sub Form_Delete(Cancel As Integer) Dim intresult As VbMsgBoxResult Dim strmessage As String If intloeschenaktuell = 0 Then intloeschengesamt = Me.SelHeight If intloeschengesamt > Me.Recordset.RecordCount Then intloeschengesamt = Me.Recordset.RecordCount intloeschenaktuell = intloeschengesamt If intloeschengesamt = 1 Then strmessage = "Möchten Sie den markierten Datensatz wirklich löschen?" Else strmessage = "Möchten Sie die " & intloeschengesamt & " markierten Datensätze wirklich löschen?" intresult = MsgBox(strMessage, vbcritical + vbyesno, "Datensatz löschen") If intresult = vbno Then bolnichtloeschen = True If intloeschenaktuell > 0 Then If bolnichtloeschen = True Then Cancel = True intloeschenaktuell = intloeschenaktuell - 1 If intloeschenaktuell = 0 Then bolnichtloeschen = False intloeschengesamt = 0 Listing 3: Löschen eines oder mehrerer Datensätze Height das von uns gewünschte Ergebnis. Wenn der Benutzer jedoch alle Datensätze markiert hat, beispielsweise über die Tastenkombination Strg + A, dann markiert Access auch den neuen, leeren Datensatz am Ende der Auswahl. Liegen 77 Datensätze in der Datenherkunft vor, liefert Me.SelHeight dann also den Wert 78. Dies müssen wir zunächst ausschließen. Also vergleicht die Prozedur den Wert von Me.SelHeight mit der Anzahl der Datensätze, die wir mit Me.Recordset.RecordCount ermitteln. Ist Me.SelHeight größer als Me.Recordset. RecordCount, hat der Benutzer offensichtlich alle Datensätze einschließlich des neuen, leeren Datensatzes Seite 10

13 FORMULARE UND STEUERELEMENTE LÖSCHEN IN FORMULAREN: EREIGNISSE markiert. In diesem Fall speichern wir in der Variablen intloeschengesamt die Anzahl der mit Me.Recordset. RecordCount ermittelten Datensätze. Anschließend stellen wir auch den Wert der Variablen int- LoeschenAktuell auf den Inhalt von intloeschengesamt ein, denn noch wurde ja kein Datensatz gelöscht. intloeschengesamt und intloeschenaktuell weisen also nach dem ersten Aufruf von Form_Delete den gleichen Wert auf, der der Anzahl der markierten Datensätze entspricht. Anschließend prüfen wir, ob ein oder mehrere Datensätze gelöscht werden sollen und speichern in der Variablen strmessage die in der Löschbestätigung anzuzeigende Meldung. Im Falle des Wertes 1 in der Variablen intloeschengesamt verwenden wir den Singular (Möchten Sie den markierten Datensatz wirklich löschen?), sonst den Plural (Möchten Sie die x markierten Datensätze wirklich löschen?). Dabei bauen wir die Anzahl der zu löschenden Datensätze über die Variable intloeschengesamt in den Ausdruck ein. Damit füllen wir dann die MsgBox-Anweisung, deren Ergebnis (vbyes oder vbno) wir in der Variablen intresult speichern. Sollte intresult danach den Wert vbno enthalten, stellen wir die Variable bolnichtloeschen auf True ein. Das Einstellen auf False ist nicht nötig, da die Variable mit dem Wert False vorbelegt wird und zum Abschluss des Löschvorgangs auch wieder auf diesen Wert eingestellt wird. Damit ist der Teil der Prozedur abgeschlossen, der nur beim ersten Aufruf durchgeführt wird. Der folgende Teil hingegen wird bei jedem Aufruf der Ereignisprozedur Form_Delete ausgeführt. Er beginnt mit einer If...Then-Bedingung, die prüft, ob intloeschen- Aktuell einen Wert größer als 0 aufweist. Dies ist beim ersten Aufruf immer der Fall, da intloeschenaktuell dann mindestens den Wert 1 aufweist oder einen größeren, falls der Benutzer mehr als einen Datensatz zum Löschen markiert hat. Innerhalb dieser Bedingung wird der Wert der Variablen intloeschenaktuell für jeden Aufruf um 1 vermindert, sodass irgendwann intloeschenaktuell gleich 0 ist. Ist intloeschenaktuell jedoch größer als 0, prüft die Prozedur innerhalb der If...Then-Bedingung, ob bolnicht- Loeschen den Wert True hat. Falls ja, wird der Parameter Cancel auf True eingestellt, was dazu führt, dass der Datensatz, für den die Ereignisprozedur gerade ausgelöst wird, nicht gelöscht wird. Dies gilt dann übrigens für jeden Aufruf, da die Variable bolnichtloeschen ja nur beim ersten Aufruf eingestellt wird. Hier wird nun intloeschenaktuell um eins vermindert. Hat intloeschenaktuell danach den Wert 0, war dies der letzte zu behandelnde Datensatz und wir können auch die Variablen bolnichtloeschen auf den Wert False und die Variable intloeschengesamt auf den Wert 0 zurücksetzen. Aktion auf jeden Fall nach dem Löschen ausführen Interessant wird es, wenn Sie eine Aktion per VBA- Code ausführen möchten, die auf jeden Fall nach dem Löschen des Datensatzes ausgeführt wird. Das ist kein Problem, solange der Benutzer die Option Bestätigen Datensatzänderungen aktiviert hat. In diesem Fall wird nicht nur die Ereignisprozedur Form_ Delete ausgelöst, sondern auch die beiden Prozeduren Form_BeforeDelConfirm und Form_AfterDelConfirm. Wenn der Benutzer jedoch Bestätigen Datensatzänderungen deaktiviert hat, werden diese beiden Ereignisse schlicht nicht mehr ausgelöst! Sollten wir also aufwendigen Code in die beiden Prozedu- Seite 11

14 FORMULARE UND STEUERELEMENTE LÖSCHEN IN FORMULAREN: EREIGNISSE ren geschrieben haben, der vor und nach der Löschbestätigung dafür sorgt, dass mit dem Löschen in Zusammenhang stehende Aktionen durchgeführt werden, schauen wir in die Röhre. Wie also gehen wir damit um? Gibt es eine alternative Möglichkeit, um diesen Code auszuführen beispielsweise schon in Form_Delete? Und was dann, wenn der Benutzer Bestätigen Datensatzänderungen doch aktiviert hat? Dann gehen wir einfach wie weiter oben beschrieben vor. Dort haben wir beim Laden des Formulars den aktuellen Wert der Option Confirm Record Changes erfasst und diese auf True eingestellt, falls diese den Wert False hatte. In diesem Fall werden die beiden Ereignisse Vor Löschbestätigung und Nach Löschbestätigung auf jeden Fall ausgelöst und wir können in Nach Löschbestätigung den Code einfügen, der nach dem Löschen ausgeführt werden soll. Dies haben wir in den beiden Formularen frmartikel- Loeschen_III und sfmartikelloeschen_iii abgebildet. Zusammengefasst sieht dies dann wie folgt aus: Dim bolconfirmrecordchanges As Boolean Private Sub Form_Load() bolconfirmrecordchanges = _ GetOption("Confirm Record Changes") If bolconfirmrecordchanges = False Then SetOption "Confirm Record Changes", True Private Sub Form_AfterDelConfirm(Status As Integer) Debug.Print "AfterDelConfirm" 'Hier Aktionen, die nach dem Löschen ausgeführt werden 'sollen Private Sub Form_BeforeDelConfirm(Cancel As Integer, _ Response As Integer) Debug.Print "BeforeDelConfirm" Private Sub Form_Delete(Cancel As Integer) Debug.Print "Delete" Private Sub Form_Unload(Cancel As Integer) If bolconfirmrecordchanges = False Then SetOption "Confirm Record Changes, False" Schließlich müssen wir in der Prozedur, die durch das Ereignis Vor Löschbestätigung ausgelöst wird, prüfen, ob der Benutzer die Löschbestätigung angezeigt haben möchte und diese gegebenenfalls unterdrücken: Private Sub Form_BeforeDelConfirm(Cancel As Integer, _ Response As Integer) If bolconfirmrecordchanges = False Then Response = acdataerrcontinue Zusammenfassung und Ausblick Dieser Beitrag hat gezeigt, dass ein simpler Tastendruck, wie er für das Löschen eines markierten Datensatzes benötigt wird, eine ganze Reihe von Ereignissen auslöst. Mit diesen können Sie den Löschvorgang auf individuelle Weise anpassen, indem Sie etwa eine eigene Meldung generieren, die statt der Standardmeldung von Access angezeigt werden. Dabei haben wir noch nicht einmal alle Möglichkeiten abgedeckt. So werden vom System ja manchmal auch Meldungen angezeigt, die darauf hinweisen, dass ein Löschen etwa wegen vorhandener verknüpfter Datensätze nicht möglich ist. Hier können Sie interessanterweise nicht mit den Techniken aus diesem Beitrag eingreifen. Welche Möglichkeiten Sie hier haben, zeigt der Beitrag Löschen von in Beziehung stehenden Daten ( in der kommenden Ausgabe. Seite 12

15 FORMULARE UND STEUER ELEMENTE REIHENFOLGE EINFACH FESTLEGEN Reihenfolge einfach festlegen Wenn Sie Daten mit individueller Reihenfolge anlegen wollen, können Sie das mit sehr wenig Aufwand anstellen, indem Sie dafür einfach ein Zahlenfeld anlegen. Für dieses kann der Benutzer dann einen eigenen Wert festlegen, der zwischen dem zweier vorhandener Felder liegt und die Reihenfolge-Werte dann per Mausklick aktualisieren. Wie das genau aussieht und wie einfach die Programmierung hierfür ist, lesen Sie im vorliegenden Beitrag. Warum die Reihenfolge festlegen? Gründe, um die Reihenfolge von Datensätzen in einer Tabelle festzulegen, gibt es genügend. In unserem Beispiel der Tabelle tblartikel gehen wir zum Beispiel davon aus, dass der Benutzer die Artikel in einer bestimmten Reihenfolge für einen Katalog ausgeben möchte. Feld zum Speichern der Reihenfolge hinzufügen Um die Reihenfolge für Datensätze festzulegen, benötigen wir natürlich ein entsprechendes Feld. Dieses nennen wir Position und fügen es einfach der betroffenen Tabelle, in diesem Fall tblartikel, als Feld mit dem Datentyp Zahl hinzu (siehe Bild 1). Hier passen wir allerdings noch die Feldgröße auf Single an, damit wir auch Dezimalzahlen eingeben können. Bild 1: Feld für das Einstellen der Reihenfolge Haupt- und Unterformular Die Daten wollen wie in einem Unterformular in der Datenblattansicht anzeigen. Dazu erstellen wir zunächst ein neues Unterformular und fügen diesem eine Abfrage als Datenherkunft hinzu, welche alle Felder der Tabelle tblartikel liefert, und zwar sortiert nach dem Feld Position (siehe Bild 2). Danach können wir dem Unterformular die Felder der Datenherkunft zuweisen, allerdings in Bild 2: Datenherkunft für das Unterformular Seite 13

16 FORMULARE UND STEUERELEMENTE REIHENFOLGE EINFACH FESTLEGEN einer ganz bestimmten Reihenfolge: Das Feld Position greifen wir zuerst aus der Feldliste heraus und fügen es oben im Formularentwurf ein. Erst danach ziehen wir die übrigen Felder aus der Feldliste unterhalb des Feldes Position in das Zielformular (siehe Bild 3). Dadurch werden die Felder in der Datenblattansicht gleich in der gewünschten Reihenfolge angezeigt, nämlich mit dem Feld Position an erster Stelle. Bild 3: Hinzufügen der Felder zum Unterformular Legen Sie nun noch die Eigenschaft Standardansicht auf den Wert Datenblatt fest und speichern Sie das Formular unter dem Namen sfmartikelmitreihenfolge. Schließen Sie das Formular anschließend. Danach erstellen Sie das Hauptformular und speichern es unter dem Namen frmartikelmitreihenfolge. Ziehen Sie dann das Unterformular aus dem Navigationsbereich von Access in den Entwurf des neu angelegten Hauptformulars. Richten Sie das Unterformular so aus, dass das Ergebnis wie in Bild 4 aussieht. Nun können Sie noch die Eigenschaften Horizontaler Anker und Vertikaler Anker des Unterformular-Steuerelements auf die Werte Beide einstellen, damit dieses seine Größe gemeinsam mit dem Hauptformular ändert. Bild 4: Unterformular zum Hauptformular hinzufügen Reihenfolge ohne Reihenfolge Interessant ist, was geschieht, wenn Sie das Formular nun in der Formularansicht öffnen, ohne zuvor die Werte des Feldes Position zu füllen. Die Datensätze werden willkür- Seite 14

17 FORMULARE UND STEUER ELEMENTE REIHENFOLGE EINFACH FESTLEGEN Schaltfläche zum Aktualisieren der Reihenfolge Nun fügen wir dem Hauptformular eine Schaltfläche hinzu, mit der wir die Reihenfolge der Datensätze aktualisieren können. Diese Schaltfläche nennen wir cmdreihenfolgeaktualisieren. Für die das Ereignis Beim Klicken der Schaltfläche hinterlegen wir die Prozedur aus Listing 1. Diese Prozedur erstellt zunächst ein Recordset auf Basis der Tabelle tblartikel, wobei diese die Datensätze aufsteigend nach den Feldern Position und ArtikelID sortiert. Danach durchläuft sie alle Datensätze der Tabelle in einer Do While-Schleife. Bild 5: Ohne Werte liefert das Feld Position eine willkürliche Sortierung. lich sortiert und zwar noch nicht einmal nach dem Feld mit dem Primärindex (ArtikelID). Das Ergebnis sieht dann zunächst wie in Bild 5 aus. Hier versetzt sie den aktuellen Datensatz zunächst mit der Edit-Methode in den Bearbeitungszustand, stellt dann das Feld Position auf den Wert der Eigenschaft AbsolutePosition des Datensatzzeigers plus ein ein (AbsolutePosition ist 0-basiert) und speichert den Datensatz mit der Update-Methode. Danach werden die übrigen Datensätze in der Do While-Schleife auf die gleiche Weise bearbeitet. Die letzte Anweisung der Proze- Private Sub cmdreihenfolgeaktualisieren_click() Dim db As DAO.Database Dim rst As DAO.Recordset Set db = CurrentDb Set rst = db.openrecordset("select * FROM tblartikel ORDER BY Position, ArtikelID", dbopendynaset) Do While Not rst.eof rst.edit rst!position = rst.absoluteposition + 1 rst.update rst.movenext Loop rst.close Set rst = Nothing Set db = Nothing Me!sfmArtikelMitReihenfolge.Form.Requery Listing 1: Initiales Einstellen der Reihenfolge Seite 15

18 FORMULARE UND STEUERELEMENTE REIHENFOLGE EINFACH FESTLEGEN dur aktualisiert die im Unterformular angezeigten Daten, die dann in der soeben festgelegten Sortierung erscheinen. Da das Feld Position zuvor noch keine Werte aufwies, erfolgt die Sortierung in diesem Fall ausschließlich nach den Werten des Primärschlüsselfeldes ArtikelID. Benutzer gibt Reihenfolge vor Nun soll der Benutzer eine einfache Möglichkeit erhalten, für einen neuen Datensatz direkt über das Feld Position an der gewünschten Stelle einzufügen. Nun ist es unter Access ja nicht möglich, neue Datensätze an beliebiger Stelle einzufügen, denn der neue, leere Datensatz ist immer der letzte ganz unten. Bild 6: Der neue Datensatz mit Position 1,5... Durch unsere Vorbereitung kann der Benutzer jedoch nun einen neuen Datensatz eingeben und für diesen durch einen geeigneten Wert für das Feld Position nach dem Speichern an der richtigen Stelle einsortieren. Im Beispiel aus Bild 6 geben wir beispielsweise den Wert 1,5 für das Feld Position ein, weil wir den Datensatz zwischen dem ersten und zweiten vorhandenen Datensatz einsortieren wollen. Nach dem Speichern des Datensatzes und dem anschließenden Betätigen der Schaltfläche Reihenfolge aktualisieren landet der Datensatz tatsächlich an der gewünschten Stelle (siehe Bild 7). Reihenfolge ohne Schaltfläche Nun wollen wir diese Lösung noch so verfeinern, dass der Benutzer den Datensatz nur noch speichern muss und der Datensatz dann automatisch an die gewünschte Stelle verschoben wird. Dazu fügen wir die Anweisungen, die sich bisher in der Prozedur cmdreihenfolgeaktualisieren_click befanden, in der Ereignisprozedur ein, die Bild 7:... landet zwischen dem vorherigen ersten und zweiten Datensatz. durch das Ereignis Nach Aktualisierung des Unterformulars ausgelöst wird. Die Ereignisprozedur sieht dann wie in Listing 2 aus. Sie führt grundsätzlich die gleichen Schritte durch wie die zuvor beschriebene Ereignisprozedur mit dem Unterschied, dass sie gleich nach der Eingabe des neuen Datensatzes ausgeführt wird. Und es gibt noch einen weiteren Einsatzzweck: Sie können nämlich auch die Position vorhandener Datensätze durch Einstellen des Wertes für das Feld Position ändern. Wenn Sie etwa den Datensatz an der zweiten Position auf Seite 16

19 FORMULARE UND STEUER ELEMENTE REIHENFOLGE EINFACH FESTLEGEN Private Sub Form_AfterUpdate() Dim db As DAO.Database Dim rst As DAO.Recordset Dim lngartikelid As Long Set db = CurrentDb Set rst = db.openrecordset("select * FROM tblartikel ORDER BY Position, ArtikelID", dbopendynaset) Do While Not rst.eof rst.edit rst!position = rst.absoluteposition + 1 rst.update rst.movenext Loop rst.close Set rst = Nothing Set db = Nothing Listing 2: Einstellen der Reihenfolge nach dem Update eines Datensatzes die dritte Position verschieben wollen, sodass der dritte Datensatz auf die zweite Position rutscht, geben Sie für das Feld Position des zweiten Datensatzes einfach einen Wert ein, der zwischen den Werten des Feldes Position für die Datensätze der dritten und der vierten Position liegt (siehe Bild 8). Position nach dem Löschen ändern Nun kann es natürlich auch einmal vorkommen, dass Sie einen Datensatz aus der Liste entfernen möchten. Bild 8: Verschieben des Datensatzes von der zweiten Position auf die dritte Position Datensätze ebenfalls die erneute Aktualisierung der Werte im Feld Position ausgelöst wird. Dazu benötigen wir ein paar Ereignisprozeduren im Unterformular. In diesem Fall sollen die Positionen natürlich direkt nach dem Löschen angepasst werden. Dazu benötigen wir entsprechende Ereignisprozeduren, die nach dem Löschen von Datensätzen ausgelöst werden. Im Beitrag Löschen in Formularen: Ereignissej (www. access-im-unternehmen.de/1128) schauen wir uns im Detail an, welche Ereignisprozeduren am Löschen von Daten aus Formularen beteiligt sind und wie Sie diese für eigene Zwecke nutzen können. Hier wollen wir dafür sorgen, dass nach dem Löschen eines oder mehrerer Wir wollen in jedem Fall berücksichtigen, dass der Benutzer auf dem Rechner die Einstellung der Access-Option Bestätigen Datensatzänderungen beeinflussen kann. Ist diese aktiviert, soll die Meldung auch angezeigt werden, falls nicht, soll diese nicht erscheinen. Wenn die Datensatzänderungen ohne Meldung angezeigt werden sollen, wird allerdings auch nicht das Ereignis Nach Löschbestätigung ausgelöst, in der wir genau die Anweisungen schreiben möchten, die nach dem Löschen den Inhalt des Feldes Status für alle verbleibenden Seite 17

20 FORMULARE UND STEUERELEMENTE REIHENFOLGE EINFACH FESTLEGEN Datensätze aktualisieren wollen. Das prüfen wir eingangs und schreiben das Ergebnis in die Variable bolconfirmrecordchanges: Dim bolconfirmrecordchanges As Boolean Beim Laden des Unterformulars prüfen wir den Wert der Option Bestätigen Datensatzänderungen und tragen diesen in bolconfirmrecordchanges ein. Außerdem aktivieren wir die Option, wenn diese nicht aktiviert ist, denn wir wollen ja, dass das Ereignis Nach Löschbestätigung feuert: Private Sub Form_Load() bolconfirmrecordchanges = µ GetOption("Confirm Record Changes") If bolconfirmrecordchanges = True Then SetOption "Confirm Record Changes", True Bevor wir das vergessen, legen wir die Prozedur für das Ereignis Beim Entladen an und stellen dort die Option Bestätigen Datensatzänderungen wieder auf den alten Wert aus der Variablen bolconfirmrecordchanges ein: Private Sub Form_Unload(Cancel As Integer) SetOption "Confirm Record Changes", µ bolconfirmrecordchanges Dann kümmern wir uns um die Anzeige eventueller Meldungen, die durch Datensatzänderungen und -löschungen hervorgerufen werden. Wenn der Benutzer Bestätigen Datensatzänderungen auf seinem Rechner aktiviert hat, sollen die Meldungen erscheinen, sonst nicht. Wir haben an dieser Stelle jedenfalls in der Ereignisprozedur Beim Laden die Anzeige der Meldungen aktiviert. Damit diese nicht erscheint, wenn der Benutzer die Meldungen nicht sehen will, füllen wir die Prozedur, die durch das Ereignis Vor Löschbestätigung ausgelöst wird, wie folgt: Private Sub Form_BeforeDelConfirm(Cancel As Integer, µ If bolconfirmrecordchanges = False Then Response As Integer) Private Sub PositionenAktualisieren() Dim db As DAO.Database Dim rst As DAO.Recordset Dim lngartikelid As Long Set db = CurrentDb Set rst = db.openrecordset("select * FROM tblartikel ORDER BY Position, ArtikelID", dbopendynaset) Do While Not rst.eof rst.edit rst!position = rst.absoluteposition + 1 rst.update rst.movenext Loop rst.close Set rst = Nothing Set db = Nothing Me.Requery Listing 3: Positionen aktualisieren in einer eigenen Prozedur Seite 18

21 FORMULARE UND STEUER ELEMENTE REIHENFOLGE EINFACH FESTLEGEN Response = acdataerrcontinue Die Prozedur prüft anhand von bolconfirmrecordchanges, ob der Benutzer die Bestätigung der Datensatzänderungen aktiviert hat. Mehrere Datensätze verschieben Fehlt noch die Möglichkeit, gleich mehrere Datensätze gleichzeitig an die gewünschte Stelle zu verschieben. Hier ergibt sich das Problem, dass wir zwar mehrere Datensätze markieren können, aber wenn wir dann auf das Feld Position eines der Datensätze klicken, hebt Access die Markierung wieder auf. Falls nicht, sorgen wir mit Response = acdataerrcontinue dafür, dass diese auch nicht erscheint, obwohl wir die Anzeige der Meldungen eigentlich aktiviert haben. Nun kommt der interessante Teil die Aktualisierung der Werte des Feldes Position. Da wir diesen Vorgang nun nicht mehr nur beim Hinzufügen und Aktualisieren eines Datensatzes, sondern auch beim Löschen auslösen wollen, nehmen wir die notwendigen Anweisungen zunächst aus der Ereignisprozedur Form_AfterUpdate heraus und tragen diese in eine eigene Prozedur ein (siehe Listing 3). Den ersten Aufruf dieser Prozedur müssen wir nun natürlich in die Ereignisprozedur Form_AfterUpdate eintragen, der wir die entsprechenden Zeilen entnommen haben: Private Sub Form_AfterUpdate() PositionenAktualisieren Außerdem fügen wir den Aufruf der Prozedur Positionen- Aktualisieren noch in die Ereignisprozedur Form_After- DelConfirm ein: Private Sub Form_AfterDelConfirm(Status As Integer) PositionenAktualisieren Damit haben wir nun ein rundes Paket, welches die Reihenfolge nicht nur beim Hinzufügen eines Datensatzes oder beim Ändern des Feldes Position anpasst, sondern auch noch beim Löschen eines Datensatzes. Also fügen wir dem Hauptformular eine Schaltfläche namens cmdverschieben hinzu, welche eine InputBox anzeigt, über die der Benutzer die Zielposition eingeben kann. Anschließend soll die Ereignisprozedur, die durch die Schaltfläche aufgerufen wurde, die markierten Datensätze an der gewünschten Stelle verschieben. Das Unterfangen ist allerdings nicht ganz so einfach. Wir können mit den beiden Eigenschaften SelTop und Sel- Height ermitteln, welche Zeilen im Datenblatt der Benutzer markiert hat. Wenn wir dies allerdings in der Ereignisprozedur erledigen wollen, die durch die Schaltfläche cmdverschieben ausgelöst wird, haben wir ein Problem: Sobald wir auf die Schaltfläche klicken, wird der Fokus nämlich vom Unterformular auf die Schaltfläche im Hauptformular verschoben und damit hebt Access auch die Markierung im Unterformular auf. Wir müssen also ein wenig tricksen. Dazu legen wir zwei Variablen im Kopf des Klassenmoduls des Hauptformulars, also in Form_frmArtikelMitReihenfolge, an: Dim lngseltop As Long Dim lngselheight As Long Diese füllen wir, wenn das Ereignis Beim Verlassen des Unterformulars ausgelöst wird. Zu diesem Zeitpunkt ist die Markierung noch vorhanden, wir können diese also wie folgt auslesen und in die beiden Variablen schreiben: Private Sub sfmartikelmitreihenfolge_exit(cancel As Integer) Seite 19

22 FORMULARE UND STEUERELEMENTE REIHENFOLGE EINFACH FESTLEGEN Private Sub cmdverschieben_click() Dim db As DAO.Database Dim rst As DAO.Recordset Dim sfm As Form Dim l As Long Dim sngposition As Single Dim strposition As String Set db = CurrentDb strposition = InputBox("An welche Position sollen die markierten Datensätze verschoben werden?", _ "Datensätze verschieben") If IsNumeric(Replace(strPosition, ".", ",")) Then sngposition = CSng(strPosition) Else If Len(strPosition) > 0 Then MsgBox "'" & strposition & "' ist keine gültige Zahl." Exit Sub Set sfm = Me!sfmArtikelMitReihenfolge.Form Set rst = db.openrecordset("select * FROM tblartikel ORDER BY Position, ArtikelID", dbopendynaset) For l = lngseltop To lngseltop + lngselheight - 1 rst.absoluteposition = l - 1 rst.edit rst!position = sngposition + (l - lngseltop + 1) / 1000 rst.update Next l rst.movefirst rst.sort = "Position, ArtikelID" Set rst = rst.openrecordset Do While Not rst.eof rst.edit rst!position = rst.absoluteposition + 1 rst.update rst.movenext Loop rst.close Set rst = Nothing Set db = Nothing Me!sfmArtikelMitReihenfolge.Form.Requery Listing 4: Einstellen der Reihenfolge für mehrere Datensätze gleichzeitig lngseltop = Me!sfmArtikelMitReihenfolge.Form.SelTop lngselheight = µ Me!sfmArtikelMitReihenfolge.Form.SelHeight Damit kommen wir zu der Prozedur aus Listing 4. Hier nutzen wir einige DAO-Techniken, um unser Vorhaben umzusetzen. Dabei ermitteln wir zunächst die Zielposition per InputBox und speichern diese in sngposition. Dann Seite 20

23 FORMULARE UND STEUER ELEMENTE REIHENFOLGE EINFACH FESTLEGEN öffnen wir ein Recordset auf Basis der Tabelle tblartikel. Diese durchlaufen wir nicht per Do While- Schleife, sondern nur die zu verschiebenden Datensätze in einer For...Next-Schleife über alle Werte vom Index des ersten markierten Artikels bis zum letzten. Dabei stellen wir den Wert des Feldes Position auf einen Wert ein, der dem Wert aus sngposition plus einem Hundertstel der aktuellen Zählervariablen l geteilt durch 1000 erhält. Wenn der Benutzer also beispielsweise als Position 1,1 eingibt, erhalten zwei markierte Datensätze die Positionswerte 1,101 und 1,102. Bild 9: Verschieben mehrerer Datensätze gleichzeitig die Markierung der beiden Datensätze bereits aufgehoben, wenn die InputBox erscheint. Dies ist nur der erste Teil. Danach nutzen wir eine relativ selten verwendete Möglichkeit von DAO, nämlich das Festlegen der Sortierkriterien für ein DAO-Recordset mit der Sort-Methode und der Angabe des Sortierausdrucks (hier Position, ArtikelID) und weisen das bestehende Recordset aus der Variablen rst erneut der Variablen rst zu (im Gegensatz zum herkömmlichen Öffnen eines Recordsets ohne Angabe der Datenherkunft und Parametern wie dbopendynaset). Anschließend durchlaufen wir wie in den vorherigen Beispielen das komplette Recordset, das nach dem Inhalt des Feldes Position sortiert ist und beispielsweise Werte wie 1, 1,101, 1,102, 2, 3,... enthält. Dabei stellen wir den Inhalt des Feldes Position wieder auf ganze Zahlen ein, in diesem Fall also 1, 2, 3, 4, 5... Dieser Teil des Beispiels sieht dann in Aktion wie in Bild 9 aus. Hier sehen Sie eine Bildmontage, denn natürlich ist Zusammenfassung und Ausblick Die hier vorgestellte Methode zum Sortieren von Daten nach individuellen, nicht durch die Feldinhalte abgebildeten Kriterien ist nicht unbedingt die ergonomischste. Da gibt es wesentlich benutzerfreundlichere Methoden wie beispielsweise das Markieren des zu verschiebenden Datensatzes und das Verschieben mit entsprechenden Schaltflächen. Oder, als beste Variante, das Verschieben per Drag and Drop. Das Verschieben mit Schaltflächen wie Ganz nach oben, Nach oben, Nach unten oder Ganz nach unten ist jedoch auch mühselig, da man dann durchaus einige Mausklicks benötigt, um ans Ziel zu gelangen. Das Drag and Drop wird in der Datenblattansicht von Access nicht unterstützt. Dafür müsste man etwa das ListView- Steuerelement heranziehen, was aber seinerseits nicht die Möglichkeiten der Datenblattansicht zum Bearbeiten der Daten liefert. Seite 21

24 FORMULARE UND STEUERELEMENTE LÖSCHEN IM LISTENFELD PER TASTATUR Löschen im Listenfeld per Tastatur Wenn Sie Daten im Listenfeld anzeigen, die durch den Benutzer etwa per Doppelklick bearbeitet oder per Schaltfläche gelöscht oder erweitert werden sollen, lässt sich dies leicht erledigen. Eher selten trifft man auf Listenfelder, deren Einträge man einfach durch Markieren und Betätigen der Entf-Taste löschen kann. Wie Sie dies programmieren, schauen wir uns im vorliegenden Beitrag an. Einfach Daten im Listenfeld markieren und mit der Entf- Taste löschen das wäre in vielen Fällen eine praktische Erweiterung der sonst üblichen Löschen-Schaltfläche. Wenn man mehr als einen Datensatz aus der Liste löschen möchte, muss man sonst nämlich ordentlich mit der Maus arbeiten markieren, auf Löschen klicken, markieren, auf Löschen klicken... Das können wir auch einfacher programmieren und dem Benutzer so die Anwendung vereinfachen. Einträge aus ungebundenem Listenfeld löschen Als Erstes schauen wir uns an, wie wir Einträge aus einem ungebundenen Listenfeld löschen können. Dieses legen wir unter dem Namen lstungebunden in einem neuen Formular an. Das Formular soll beim Laden einige Einträge zum Listenfeld hinzufügen, was wir mit folgender Ereignisprozedur erledigen: Private Sub Form_Load() Dim i As Integer Me!lstUngebunden.RowSourceType = "Value List" For i = 1 To 10 Next i Me!lstUngebunden.AddItem "Eintrag " & i Die Prozedur stellt die Eigenschaft Herkunftsart des Listenfeldes auf Wertliste ein und fügt innerhalb einer For...Next-Schleife zehn durchnummerierte Einträge zum Listenfeld hinzu. Dies liefert das Listenfeld mit den Einträgen aus Bild 1. Wenn wir nun einen der Einträge per Tastatur mit der Entf-Taste löschen wollen, müssen wir die Tastenereignisse des Listenfeldes hinzuziehen. In diesem Fall wollen wir den Eintrag löschen, sobald der Benutzer die Entf-Taste herunterdrückt. Dazu hinterlegen wir die Ereignisprozedur aus Listing 1 für das Ereignis Bei Taste ab des Listenfeldes. Die Prozedur prüft, ob der Wert des Parameters KeyCode, der beim Herunterdrücken der Taste geliefert wird, der Zahl 46 entspricht. In diesem Fall ermittelt die Prozedur den Index des aktuell markierten Eintrags im Listenfeld und schreibt diesen in die Variable lngitem. Bild 1: Listenfeld mit Beispieldaten Diese Variable prüft die folgende If...Then- Bedingung auf den Wert -1. Ist der Wert nicht gleich -1, ruft die Prozedur die RemoveItem- Methode des Listenfeldes auf und übergibt mit lngitem den Index der zu löschenden Zeile. Damit wird der aktuell markierte Eintrag Seite 22

25 FORMULARE UND STEUERELEMENTE LÖSCHEN IM LISTENFELD PER TASTATUR Private Sub lstungebunden_keydown(keycode As Integer, Shift As Integer) Dim lngitem As Long Select Case KeyCode Case 46 lngitem = Me!lstUngebunden.ListIndex If Not lngitem = -1 Then Me!lstUngebunden.RemoveItem lngitem If Not Me!lstUngebunden.ListCount = 0 Then If lngitem < Me!lstUngebunden.ListCount Then Me!lstUngebunden.ListIndex = lngitem Else Me!lstUngebunden.ListIndex = Me!lstUngebunden.ListCount - 1 End Select Damit kann der Benutzer nun durch mehrmaliges Betätigen der Entf-Taste mehrere untereinander liegende Einträge hintereinander löschen, ohne zwischendurch einen neuen Datensatz markieren zu müssen. Soll ein Datensatz nicht gelöscht werden, kann der Benutzer den nächsten Datensatz mit den Nach oben- und Nach unten-tasten ansteuern. Listing 1: Löschen einzelner Einträge aus einem ungebundenen Listenfeld entfernt. Das Listenfeld behält den Fokus, aber danach ist kein Datensatz markiert (siehe Bild 2). Sollten wir die Markierung nach dem Löschen auf einen anderen Datensatz verschieben? Denkbar wäre, dass der Benutzer beispielsweise von oben nach unten einige Datensätze löschen möchte. Also markieren wir nach dem Löschen den Datensatz, der sich direkt unterhalb des gelöschten Datensatzes befunden hat. Dazu haben wir hinter der Zeile mit der RemoveItem- Methode zunächst die folgende Zeile eingefügt: Me!lstUngebunden.ListIndex = lngitem Allerdings war dies etwas zu kurz gedacht, denn die Prozedur lieferte einen Fehler, wenn wir den letzten vorhandenen Eintrag gelöscht haben und auch das Löschen des letzten Eintrags der Liste lieferte regelmäßig einen Fehler. Also haben wir noch ein paar verschachtelte Bedingungen hinzugefügt. Die erste fragt ab, ob das Listenfeld überhaupt noch Einträge enthält. Falls nicht, wird auch kein Eintrag mehr markiert. Falls doch, prüfen wir, ob lngitem, also der Index des gelöschten und des nun zu markierenden Datensatzes, der letzte Eintrag im Listenfeld ist. Falls nein, wird einfach der Eintrag mit dem Index des gelösch- Bild 2: Listenfeld nach dem Löschen eines Datensatzes Bild 3: Löschen einer Mehrfachauswahl Seite 23

26 FORMULARE UND STEUERELEMENTE LÖSCHEN IM LISTENFELD PER TASTATUR ten Eintrags markiert, also der erste Eintrag hinter dem gelöschten Eintrag. Sollten wir uns schon an der letzten Position befinden, markiert die Prozedur den letzten Eintrag des Listenfeldes. Auf diese Weise können wir auch den hinteren Eintrag markieren und von dort aus alle Einträge von hinten nach vorne löschen, ohne dass wir zwischendurch einen Eintrag von Hand markieren müssen. Mehrfachauswahl löschen i = i + 1 Nun wollen wir uns ansehen, wie wir Einträge aus Next varitem einem Listenfeld löschen Next l können, für das die Mehrfachauswahl aktiviert ist. End Select Dazu fügen wir dem Beispielformular ein weiteres Listenfeld namens lstungebundenmehrfach hinzu, dessen Eigenschaft Mehrfachauswahl wir auf Einfach oder Erweitert einstellen. Damit auch dieses Listenfeld mit einigen Beispieleinträgen gefüllt wird, fügen wir der For...Next-Schleife der Prozedur Form_Load die folgende Zeile hinzu: Private Sub lstungebundenmehrfach_keydown(keycode As Integer, Shift As Integer) Dim varitem As Variant Select Case KeyCode Case 46 End Select For Each varitem In Me!lstUngebundenMehrfach.ItemsSelected Me!lstUngebundenMehrfach.RemoveItem varitem Next varitem Listing 2: Erster Versuch, Einträge aus der Mehrfachauswahl eines Listenfeldes zu löschen Private Sub lstungebundenmehrfach_keydown(keycode As Integer, Shift As Integer) Dim varitem As Variant Dim arritems() As Long Dim l As Long Dim i As Integer Select Case KeyCode Case 46 For Each varitem In Me!lstUngebundenMehrfach.ItemsSelected ReDim Preserve arritems(i) arritems(i) = varitem For l = i - 1 To 0 Step -1 Me!lstUngebundenMehrfach.RemoveItem arritems(l) Listing 3: Zweiter Versuch, Einträge aus der Mehrfachauswahl eines Listenfeldes zu löschen Wenn wir dann die Einträge wie im Bild oben markieren und auf die Entf-Taste klicken, wird nur der erste Eintrag gelöscht. Warum das? Weil durch das Löschen des ersten Eintrags und damit der Aktualisierung des Listenfeldes auch die vorhandenen Markierungen entfernt werden. Me!lstUngebundenMehrfach.AddItem "Eintrag " & i In diesem Fall können wir nicht nur einen, sondern mehrere Einträge markieren (siehe Bild 3). Wir fügen der Prozedur, die durch das Ereignis Bei Taste ab des Listenfeldes lstungebundenmehrfach ausgelöst wird, die Zeilen aus Listing 2 hinzu. Also müssen wir dies etwas anders angehen und die Indizes der zu löschenden Einträge erst in einem Array zwischenspeichern. Die programmieren wir in der neuen Version der Prozedur, die Sie in Listing 3 finden. Hier verwenden wir gleich drei neue Variablen, nämlich i und l mit dem Datentyp Long Seite 24

27 FORMULARE UND STEUERELEMENTE LÖSCHEN IM LISTENFELD PER TASTATUR und ein Array namens arritems ebenfalls mit dem Datentyp Long. Wenn der Benutzer die Entf-Taste gedrückt hat, durchlaufen wir wie zuvor eine For Each- Schleife über alle markierten Elemente des Listenfeldes und redimensionieren das Array jeweils auf die Anzahl der erfolgten Durchläufe. Um diese zu zählen, erhöhen wir jeweils die Variable i um den Wert 1. Der Position des Arrays mit dem Indexwert aus i weisen wir dabei jeweils den Index beziehungsweise die Position des soeben bearbeiteten Listeneintrags zu in diesem Beispiel erhalten also etwa die Zeilen 0, 1 und 2 des Arrays die Werte 0, 2 und 4. Nach dem Durchlaufen wissen wir über den Wert von i, wieviele Zeilen gelöscht werden sollen. In einer For... Next-Schleife mit der Schrittweite -1 durchlaufen wir dann die Werte von i-1 bis 0. Warum rückwärts? Wenn wir vorwärts arbeiten, würden wir nicht die Einträge mit dem Index 0, 2 und 4 löschen, sondern die mit dem Index 0, 3 und 6. Warum? Wenn wir den Eintrag mit dem Index 0 löschen, rückt der Eintrag mit dem Index 2, den wir auch löschen wollen, auf den Index 1. Wir löschen dann den Eintrag, der zuvor den Index 3 hatte. Beim Löschen aus Auflistungen also immer rückwärts in der Schleife arbeiten! In der Schleife entfernen wir dann die jeweiligen Einträge wieder mit der RemoveItem-Methode, wobei wir den Index diesmal dem Array arritems entnehmen. Nach dem Durchlaufen der Schleife sind dann alle gewünschten Einträge entfernt. Wir sparen uns es an dieser Stelle auch, wie im ersten Beispiel gleich den ersten Eintrag nach dem gelöschten Eintrag zu markieren. Bild 4: Einstellen der Datensatzherkunft der beiden Listenfelder Löschen aus gebundenen Listenfeldern Nun schauen wir uns die beiden Beispiele von oben noch für gebundene Listenfelder an. Dazu erstellen wir eine kleine Beispieltabelle namens frmwerte mit den Feldern ID (Primärschlüsselfeld) und Wert (Textfeld). Das Formular frmlistenfeldgebunden ist genauso aufgebaut wie das vorherige Beispielformular. Der Unterschied ist, dass wir die Eigenschaft Herkunftsart bei Tabelle/Abfrage belassen. Außerdem stellen wir die Eigenschaft Datensatzherkunft auf die folgende Abfrage ein (siehe Bild 4): SELECT tblwerte.id, tblwerte.wert FROM tblwerte; Damit die Listenfelder das erste Feld der Datenquelle als gebundene Spalte nutzen, die aber nicht angezeigt wird und nur das Feld Wert anzeigen, stellen wir die Eigenschaft Spaltenanzahl auf 2 und die Eigenschaft Spaltenbreiten auf 0cm ein. Seite 25

28 FORMULARE UND STEUERELEMENTE LÖSCHEN IM LISTENFELD PER TASTATUR Private Sub lstgebunden_keydown(keycode As Integer, Shift As Integer) Dim db As DAO.Database Dim lngitem As Long Select Case KeyCode Case 46 Set db = CurrentDb lngitem = Me!lstGebunden.ListIndex If Not lngitem = -1 Then db.execute "DELETE FROM tblwerte WHERE ID = " & Me!lstGebunden.ItemData(lngItem) Me!lstGebunden.Requery If Not Me!lstGebunden.ListCount = 0 Then If lngitem < Me!lstGebunden.ListCount Then Me!lstGebunden.ListIndex = lngitem Else Me!lstGebunden.ListIndex = Me!lstGebunden.ListCount - 1 End Select Listing 4: Gebundenes Listenfeld: Einzelne Einträge löschen Einzelne Einträge löschen Die Prozedur zum Löschen einzelner Einträge aus dem gebundenen Listenfeld finden Sie in Listing 4. Sie prüft in der Select Case-Bedingung, ob der Benutzer die Taste mit dem KeyCode-Wert 46, also die Entf-Taste, gedrückt hat. In diesem Fall erstellt sie einen Verweis auf die aktuelle Datenbank und speichert diese in der Variablen db. Die Prozedur prüft dann, ob überhaupt ein Eintrag ausgewählt ist. Falls ja, löscht sie den Eintrag aus der Tabelle tblwerte, dessen Feld ID den Wert hat, den die Eigenschaft ItemData für den Eintrag mit dem Index aus lngitem liefert. lngitem ist der Index des markierten Eintrags, ItemData liefert den Wert der gebundenen Spalte für den Eintrag mit dem entsprechenden Index. Nach dem Aktualisieren der Datensatzherkunft des Listenfeldes mit der Requery-Methode folgt das Markieren des nächsten Datensatzes also das Markieren des folgenden Datensatzes, sofern einer vorhanden ist, oder das Markieren des vorherigen Datensatzes, wenn der Benutzer zuvor den letzten Datensatz in der Liste gelöscht hat. Dazu prüft die Prozedur, ob das Listenfeld überhaupt noch einen Eintrag anzeigt. Falls nicht, wird entsprechend kein Eintrag markiert. Falls doch, prüft die Prozedur, ob der gelöschte Eintrag nicht der letzte Eintrag war. In diesem Fall stellt sie den Index auf den gleichen Index-Wert ein, den der zuvor gelöschte Eintrag besaß. Bleibt noch die Möglichkeit, dass der Benutzer den letzten Eintrag der Liste gelöscht hat. In diesem Fall soll der nun letzte Eintrag im Listenfeld markiert werden. Löschen einer Mehrfachauswahl aus dem gebundenen Listenfeld Damit kommen wir zum Löschen mehrerer Datensätze aus dem gebundenen Listenfeld. Hier ist die Eigenschaft Mehrfachauswahl des Listenfeldes lstgebundenmehrfach beispielsweise auf Erweitert eingestellt. Damit kann der Benutzer die Einträge wie etwa die Dateien im Windows Explorer auswählen. Seite 26

29 FORMULARE UND STEUERELEMENTE LÖSCHEN IM LISTENFELD PER TASTATUR Private Sub lstgebundenmehrfach_keydown(keycode As Integer, Shift As Integer) Dim db As DAO.Database Dim varitem As Variant Dim arritems() As Long Dim lngloeschen As Long Dim l As Long Dim i As Integer Select Case KeyCode Case 46 Set db = CurrentDb For Each varitem In Me!lstGebundenMehrfach.ItemsSelected ReDim Preserve arritems(i) arritems(i) = varitem i = i + 1 Next varitem For l = i - 1 To 0 Step -1 lngloeschen = Me!lstGebundenMehrfach.ItemData(arrItems(l)) db.execute "DELETE FROM tblwerte WHERE ID = " & lngloeschen Next l Me!lstGebundenMehrfach.Requery End Select Listing 5: Löschen mehrerer Einträge im gebundenen Listenfeld Nach der Auswahl und dem Betätigen der Entf-Schaltfläche prüft die Prozedur, die durch das Ereignis Bei Taste ab ausgelöst wird und die wie in Listing 5 aussieht, wieder den Wert des Parameters KeyCode auf den Wert 46. In diesem Fall wird die Variable db über die Funktion CurrentDb mit einem Verweis auf die aktuelle Datenbank gefüllt. Dann durchläuft die Prozedur in einer For Each- Schleife alle markierten Einträge des Listenfeldes, auf die wir mit der Auflistung ItemsSelected zugreifen. Die von dieser Auflistung gelieferten Index-Werte landen jeweils in der Variant-Variablen varitem. Wir erweitern das Array arritems dann jeweils auf die Anzahl, die dem Wert der Zählervariablen i entspricht und fügen ihm den aktuell durchlaufenen Wert aus der Variablen varitem zu. Dann erhöhen wir i um 1 und bearbeiten den nächsten Eintrag der Auflistung. Damit ausgestattet durchlaufen wir in einer For...Next- Schleife rückwärts alle Elemente des Arrays von i - 1 bis 0. In der Variablen lngloeschen nehmen wir den Wert der gebundenen Spalte des Listenfeldes für den jeweils angegebenen Index-Wert aus dem Array arritems auf, den wir mit der ItemData-Eigenschaft ermitteln. Diesen Wert nutzen wir dann als Vergleichswert für das Kriterium der DELETE-Aktionsabfrage, welche den aktuellen Datensatz aus der Tabelle tblwerte löscht. Schließlich aktualisieren wir nach dem Durchlaufen aller Elemente des Arrays die Datenherkunft des Listenfeldes mit der Requery-Methode. Zusammenfassung und Ausblick Das Löschen von Listenfeld-Einträgen erfolgt meist nach vorherigem Markieren der zu löschenden Daten und anschließendem Löschen mit einer dafür vorgesehenen Schaltfläche. Dieser Beitrag zeigt einmal, wie Sie auch aus dem Listenfeld ganz einfach Datensätze durch Betätigen der Entf- Schaltfläche löschen können, und zwar für ungebundene und gebundene Listenfelder. Seite 27

30 FORMULARE UND STEUERELEMENTE ZEITRÄUME PER LISTENFELD UND INPUTBOX Zeiträume per Listenfeld und InputBox Die Beispieldatenbank Vereinsverwaltung erwartet an einer Stelle die Eingabe der Vereinszugehörigkeit. Je Mitglied kann es auch mehrere Vereinszugehörigkeiten geben. Diese wollen wir diesmal in einem Listenfeld darstellen und ohne Verwendung eines eigenen Detailformulars per InputBox verwalten. Dass dies gar nicht unbedingt einfacher oder schneller geht, zeigt der vorliegende Beitrag. Der größte Aufwand bei der Verwaltung von Zeiträumen entsteht durch die Überprüfung der eingegebenen Werte: Das Startdatum darf nicht hinter dem Enddatum liegen, die Zeiträume der verschiedenen Datensätze dürfen sich nicht überlappen. In diesem Beitrag zeigen wir, wie Sie die Zeiträume zu einem bestimmten Thema, hier die Vereinszugehörigkeiten von Mitgliedern zu einem Verein, in einem Listenfeld anzeigen und per InputBox verwalten wollen. Diese Zeiträume könnten aber auch etwa die Beschäftigungszeiten in einem Unternehmen sein und Sie finden sicher noch weitere Beispiele. In unserem Beispiel kümmern wir uns um die Einträge der Tabelle tblvereinszugehoerigkeiten, die wie in Bild 1 mit der Tabelle tblmitglieder verknüpft ist. Das Listenfeld lstvereinszugehoerigkeiten sowie die beiden Schaltflächen cmdneuevereinszugehoerigkeit und cmdvereinszugehoerigkeitloeschen füllen wir gleich mit Code. Ein Klick auf die Schaltfläche cmdneuevereinszugehoerigkeit soll beispielsweise eine InputBox wie in Bild 2 anzeigen. Bild 1: Mitglieder und Vereinszugehörigkeiten Zuvor wollen wir jedoch noch die Daten der Tabelle tblvereinszugehoerigkeiten im Listenfeld anzeigen. Wie Sie diese Steuer elemente anlegen, erläutern wir im Beitrag Bild 2: Zeiträume per InputBox eingeben Seite 28

31 FORMULARE UND STEUERELEMENTE ZEITRÄUME PER LISTENFELD UND INPUTBOX Vereinsverwaltung: Formulare ( Datensatzherkunft für das Listenfeld Das Listenfeld soll alle Datensätze der Tabelle tblvereinszugehoerigkeiten anzeigen, die dem aktuell im Formular frmmitglieddetails angezeigten Mitglied zugeordnet sind. Außerdem sollen die Daten aufsteigend nach dem Eintrittsdatum sortiert werden. Dies erledigen wir mit der Abfrage aus Bild 3. Diese verwendet alle Felder der Tabelle tblvereinszugehoerigkeiten, das Fremdschlüsselfeld MitgliedID wird jedoch nur als Kriterium verwendet. Damit nur die Vereinszugehörigkeiten zum aktuellen Mitglied erscheinen, stellen wir das Kriterium dieses Feldes auf [Forms]![fr mmitglieddetails]![mitgliedid] ein. Dies führt dazu, dass die Datensätze automatisch gefiltert werden. Damit das Listenfeld nur die für die Bearbeitung wichtigen Spalten anzeigt, stellen wir die Eigenschaft Spaltenanzahl auf 3 und die Eigenschaft Spaltenbreiten auf 0cm;3cm;3cm ein. Damit erhalten wir dann, sofern schon einige Datensätze vorliegen, in der Formularansicht Daten wie etwa in Bild 4. Wenn wir dann den Datensatz im Formular wechseln, aktualisiert dies allerdings noch nicht die im Listenfeld angezeigten Daten. Dazu müssen wir noch die Ereignisprozedur Beim Anzeigen zum Formular hinzufügen und diese wie folgt ergänzen: Private Sub Form_Current() Me!lstVereinszugehoerigkeiten.Requery Bild 3: Datensatzherkunft des Listenfeldes lstvereinszugehoerigkeiten Bild 4: Zwei Datensätze im Listenfeld Danach gelingt die Aktualisierung des Listenfeldes beim Datensatzwechsel reibungslos. Hinzufügen eines neuen Zeitraums Die durch die Schaltfläche cmdneuevereinszugehoerigkeit ausgelöst Ereignisprozedur soll dem Benutzer die Möglichkeit geben, einen neuen Zeitraum zum aktuellen Mitglied hinzuzufügen (siehe Listing 1). Die Prozedur ermittelt zunächst mit einer InputBox-Anweisung das Eintrittsdatum für die neue Vereinszugehörigkeit. Dann prüft sie, ob die Variable, welche das Ergebnis der Funktion aufgenommen hat, eine Zeichenkette mit einer Länge größer 0 aufweist. Dies ist unter folgenden Bedingungen nicht der Fall: Wenn der Benutzer das Feld leer lässt und auf OK klickt oder wenn er direkt auf Abbrechen klickt dann wird unabhängig vom aktuell in die InputBox eingetragenen Wert eine leere Zeichenfolge zurück- Seite 29

32 FORMULARE UND STEUERELEMENTE ZEITRÄUME PER LISTENFELD UND INPUTBOX geliefert. Sollte streintritt nach dieser Zeile also eine Zeichenkette mit mindestens einem Zeichen enthalten, ist die folgende If...Then-Bedingung erfüllt. Eine weitere If... Then-Bedingung prüft dann, ob der Benutzer ein gültiges Datum eingegeben hat. Falls nicht, wird der Else-Teil ausgeführt, welcher den Benutzer darauf hinweist, dass er Private Sub cmdneuevereinszugehoerigkeit_click() Dim db As DAO.Database Dim dateintritt As Date Dim dataustritt As Date Dim streintritt As String Dim straustritt As String streintritt = InputBox("Geben Sie das Eintrittsdatum an.", "Startdatum") If Len(strEintritt) > 0 Then Set db = CurrentDb If IsDate(strEintritt) Then dateintritt = CDate(strEintritt) straustritt = InputBox("Geben Sie das Austrittsdatum an.", "Austrittsdatum", "Noch aktiv.") If Len(strAustritt) > 0 And Not straustritt = "Noch Aktiv." Then If IsDate(strAustritt) Then dataustritt = CDate(strAustritt) If dateintritt > dataustritt Then MsgBox ("Das Eintrittsdatum muss vor dem Austrittsdatum liegen. Die Eingabe wird abgebrochen.") Exit Sub Else db.execute "INSERT INTO tblvereinszugehoerigkeiten(mitgliedid, Eintrittsdatum, " _ & "Austrittsdatum) VALUES(" & Me!MitgliedID & ", " & SQLDatum(datEintritt) & ", " _ & SQLDatum(datAustritt) & ")", dbfailonerror Me!lstVereinszugehoerigkeiten.Requery Else MsgBox ("'" & straustritt & "' ist kein gültiges Austrittsdatum.") Exit Sub Else db.execute "INSERT INTO tblvereinszugehoerigkeiten(mitgliedid, Eintrittsdatum) VALUES(" _ & Me!MitgliedID & ", " & SQLDatum(datEintritt) & ")", dbfailonerror Me!lstVereinszugehoerigkeiten.Requery Else MsgBox ("'" & streintritt & "' ist kein gültiges Eintrittsdatum.") Exit Sub Else Exit Sub Listing 1: Anlegen eines neuen Zeitraums Seite 30

33 FORMULARE UND STEUERELEMENTE ZEITRÄUME PER LISTENFELD UND INPUTBOX kein gültiges Eintrittsdatum eingegeben hat. Ist das Datum jedoch gültig, speichern wir die in den Datentyp Date konvertierte Zeichenkette in der Variablen dateintritt. Danach können wir mit der Abfrage des Austrittsdatums fortfahren. Normalerweise gehen wir davon aus, dass eine neue Vereinszugehörigkeit nur ein Eintrittsdatum enthält, daher füllen wir den Standardtext dieser InputBox mit dem Text Noch aktiv., was wie in Bild 5 aussieht. Nach der Eingabe des Benutzers prüft die folgende If... Then-Bedingung, ob sowohl die zurückgelieferte Zeichenkette eine Länge von mindestens einem Zeichen aufweist und der Text nicht mit dem Standardwert Noch aktiv. übereinstimmt. Falls nicht, wird im Else-Teil der Bedingung über die Execute-Methode des Database-Objekts db eine INSERT INTO-Anweisung ausgeführt, welche einen neuen Datensatz in der Tabelle tblvereinszugehoerigkeiten anlegt und dabei die Felder MitgliedID und Eintrittsdatum mit dem Primärschlüsselwert des aktuellen Datensatzes im Formular und dem Wert aus dateintritt füllt. Dabei nutzen wir die Hilfsfunktion SQLDatum aus dem Modul mdltools, um das Datum SQL-konform abzubilden (als yyyy-mm-dd). Danach aktualisieren wir die Anzeige im Listenfeld mit der Requery-Methode des Listenfeldes. Hat der Benutzer jedoch ein gültiges Austrittsdatum angegeben, weil er etwa aus statistischen Gründen frühere Vereinszugehörigkeiten nachtragen möchte, prüft die Prozedur, ob es sich bei straustritt um ein gültiges Datum handelt. Auch hier erscheint im Else-Teil eine entsprechende Meldung, falls straustritt kein gültiges Datum enthält. Ist straustritt ein Datum, wird es mit CDate in die Date-Variable dataustritt überführt. Da nun beide Datumsangaben vorhanden sind, können wir noch prüfen, ob das Eintrittsdatum gegebenenfalls hinter dem Austrittsdatum liegt und den Benutzer darauf hinweisen. Er muss den Vorgang dann erneut starten. Sind die Daten hingegen Bild 5: Vorbelegung für das Austrittsdatum korrekt, fügt die Prozedur den Datensatz mit einer INSERT INTO-Aktionsabfrage zur Tabelle tblvereinszugehoerigkeiten hinzu. Diesmal berücksichtigt sie dabei nicht nur die Felder MitgliedID und Eintrittsdatum, sondern auch noch das Feld Austrittsdatum. Ändern eines bestehenden Zeitraums Wenn der Benutzer doppelt auf einen der vorhandenen Einträge klickt, löst dies die Ereignisprozedur lstvereinszugehoerigkeiten_dblclick aus, die Sie in Listing 2 finden. Die Prozedur ermittelt zunächst über die Eigenschaft Column(0) des Listenfeld-Steuerelements den Wert des in der ersten Spalte angezeigten Feldes. Wobei»angezeigt«der falsche Begriff ist, denn wir haben die Breite dieser Spalte ja auf 0cm eingestellt, wodurch die Spalte faktisch ausgeblendet wird. Der darin enthaltene Wert des Primärschlüsselfeldes der Tabelle tblvereinszugehoerigkeiten für den aktuell markierten Datensatz landet jedenfalls in der Variablen lngvereinszugehoerigkeitid. Gegebenenfalls hat der Benutzer auf eine leere Stelle im Listenfeld geklickt. In diesem Fall liefert Column(0) den Wert Null zurück, den wir durch die Funktion Nz durch den Wert 0 ersetzen. Hat lngvereinszugehoerigkeitid danach den Wert 0, zeigt die Prozedur eine Meldung an, die den Benutzer darauf hinweist, dass er den Doppelklick für einen bestehenden Eintrag ausführen muss. Nun folgt ein vorbereitender Schritt für die Bearbeitung der bestehenden Daten: Wir speichern die in der zweiten und dritten Spalte des markierten Eintrags im Listenfeld befindlichen Werte in den Variablen streintritt und straustritt. Dann Seite 31

34 FORMULARE UND STEUERELEMENTE ZEITRÄUME PER LISTENFELD UND INPUTBOX Private Sub lstvereinszugehoerigkeiten_dblclick(cancel As Integer) Dim db As DAO.Database Dim dateintritt As Date, dataustritt As Date Dim streintritt As String, straustritt As String Dim lngvereinszugehoerigkeitid As Long lngvereinszugehoerigkeitid = Nz(Me!lstVereinszugehoerigkeiten.Column(0), 0) If lngvereinszugehoerigkeitid = 0 Then MsgBox "Sie müssen einen Eintrag auswählen.", vbokonly, "Kein Eintrag ausgewählt." Exit Sub streintritt = Nz(Me!lstVereinszugehoerigkeiten.Column(1), "") straustritt = Nz(Me!lstVereinszugehoerigkeiten.Column(2), "") streintritt = InputBox("Geben Sie das Eintrittsdatum an.", "Startdatum", streintritt) Set db = CurrentDb If Len(strEintritt) > 0 Then If IsDate(strEintritt) Then dateintritt = CDate(strEintritt) straustritt = InputBox("Geben Sie das Austrittsdatum an.", "Austrittsdatum", straustritt) If Len(strAustritt) > 0 Then If IsDate(strAustritt) Then dataustritt = CDate(strAustritt) If dateintritt > dataustritt Then MsgBox ("Das Eintrittsdatum muss vor dem Austrittsdatum liegen. Die Eingabe wird abgebrochen.") Exit Sub Else db.execute "UPDATE tblvereinszugehoerigkeiten SET Eintrittsdatum = " & SQLDatum(datEintritt) _ & ", Austrittsdatum = " & SQLDatum(datAustritt) & " WHERE VereinszugehoerigkeitID = " _ & Me!lstVereinszugehoerigkeiten, dbfailonerror Me!lstVereinszugehoerigkeiten.Requery Else MsgBox ("'" & straustritt & "' ist kein gültiges Austrittsdatum.") Exit Sub Else db.execute "UPDATE tblvereinszugehoerigkeiten SET Eintrittsdatum = " & SQLDatum(datEintritt) _ & ", Austrittsdatum = NULL WHERE VereinszugehoerigkeitID = " _ & lngvereinszugehoerigkeitid, dbfailonerror Me!lstVereinszugehoerigkeiten.Requery Else MsgBox ("'" & streintritt & "' ist kein gültiges Eintrittsdatum.") Exit Sub 8 Else MsgBox ("Das Eintrittsdatum kann nicht geleert werden.") Exit Sub Listing 2: Bearbeiten eines vorhandenen Zeitraums Seite 32

35 FORMULARE UND STEUERELEMENTE ZEITRÄUME PER LISTENFELD UND INPUTBOX zeigen wir eine InputBox an, die das Eintrittsdatum abfragen soll und den Wert aus streintritt als Standardwert nutzt (siehe Bild 6). Das Ergebnis landet schließlich in der Variablen streintritt und wird auf seine Eigenschaft als Datum geprüft. Ist es kein Datum, erscheint wieder die im Else-Teil aufgerufene Meldung mit einem entsprechenden Hinweis, anderenfalls landet streintritt in dateintritt. Die nächste InputBox nimmt das Austrittsdatum auf und speichert es in der Variablen straustritt. Genau diese liefert auch den in der InputBox angezeigten Standardwert. Auch hier erfolgt wieder die Prüfung auf ein gültiges Datum mit den Konsequenzen bei Fehleingabe. Ein gültiges Datum vorausgesetzt, prüft die Prozedur dann, ob dateintritt größer als dataustritt ist und gibt gegebenenfalls eine Meldung aus und bricht die Prozedur ab. Anderenfalls schreibt eine UPDATE-Aktionsabfrage die geänderten Werte für die Felder Eintrittsdatum und Austrittsdatum für den in lngvereinszugehoerigkeitid angegebenen Datensatz in die Tabelle tblvereinszugehoerigkeiten ein. Bild 6: Vorbelegung für das Eintrittsdatum beim Bearbeiten eines Datensatzes Die Prozedur verwendet wieder die String-Variablen streintritt und straustritt zum Speichern der Ein- und Austrittsdaten. Sie prüft, ob der Benutzer vor dem Betätigen der Schaltfläche überhaupt einen Eintrag im Listenfeld markiert hat. Falls nicht, erscheint eine entsprechende Meldung. Ist ein Eintrag markiert, landen die Inhalte der zweiten und dritten Spalte des Listenfeldes in den beiden Variablen streintritt und straustritt. Nun treffen wir Vorbereitungen, um eine Rückfrage beim Benutzer bezüglich der zu löschenden Daten zusammenzustellen. Wenn straustritt ein Datum enthält, fügen wir nämlich vorn noch die Zeichenkette bis an: straustritt = " bis " & straustritt Hat der Benutzer kein Austrittsdatum angegeben, kommt noch der Else-Teil der dritten verschachtelten If...Then- Bedingung zum Tragen. Hier aktualisieren wir den bearbeiteten Datensatz ebenfalls mit einer UPDATE-Aktionsabfrage, aber wir leeren das Feld Austrittsdatum, indem wir es auf den Wert NULL einstellen. Löschen eines bestehenden Zeitraums Nun fehlt noch das Löschen eines bestehenden Zeitraums. Dies erledigen wir mit der Schaltfläche cmdvereinszugehoerigkeitenloeschen, welche die Ereignisprozedur aus Listing 3 auslöst. Dies wirkt sich dann auf die Meldung in der folgenden MsgBox-Anweisung aus. Ist straustritt nämlich leer, wird schlicht kein Austrittsdatum bei der Rückfrage angegeben, anderenfalls erscheint das Austrittsdatum mit führendem bis (siehe Bild 7). Bestätigt der Benutzer diese mit OK, löscht eine DELETE-Aktionsabfrage den betroffenen Datensatz aus der Tabelle und aktualisiert die Anzeige im Listenfeld. Sich überschneidende Zeiträume Fehlt noch die Möglichkeit, dass der Benutzer zwei Zeiträume eingibt, die sich überschneiden. In diesem Fall Seite 33

36 FORMULARE UND STEUERELEMENTE ZEITRÄUME PER LISTENFELD UND INPUTBOX Private Sub cmdvereinszugehoerigkeitloeschen_click() Dim streintritt As String Dim straustritt As String Dim db As DAO.Database If Not IsNull(Me!lstVereinszugehoerigkeiten) Then Else streintritt = Nz(Me!lstVereinszugehoerigkeiten.Column(1), "") straustritt = Nz(Me!lstVereinszugehoerigkeiten.Column(2), "") If Len(strAustritt) > 0 Then straustritt = " bis " & straustritt If MsgBox("Vereinszugehörigkeit vom " & streintritt & straustritt & " wirklich löschen?", vbokcancel, _ "Löschbestätigung") = vbok Then Set db = CurrentDb db.execute "DELETE FROM tblvereinszugehoerigkeiten WHERE VereinszugehoerigkeitID = " _ & Me!lstVereinszugehoerigkeiten.Column(0), dbfailonerror Me!lstVereinszugehoerigkeiten.Requery MsgBox "Kein Listeneintrag markiert.", vbokonly, "Fehlende Auswahl" Listing 3: Löschen eines Zeitraums wollen wir kurz vor dem Speichern eines geänderten Zeitraums oder eines neu angelegten Zeitraums in der Tabelle prüfen, ob dieser sich gegebenenfalls mit einem bereits vorhandenen Zeitraum für ein Mitglied überschneidet. Für dieses scheinbar einfache Unterfangen ist eine relativ umfangreiche Prozedur erforderlich, die wie in Listing 4 aussieht. Die Prozedur erwartet vier Parameter, von denen einer Optional ist. Die ersten beiden sind das Eintrittsdatum und das Austrittsdatum, das der Benutzer soeben eingegeben hat. Der dritte ist die ID des Mitglieds, für das die neue Vereinszugehörigkeit gespeichert werden soll. Der vierte und optionale Parameter ist der Primärschlüsselwert eines bestehenden Eintrags in der Tabelle tblvereinszugehoerigkeiten, der natürlich nur dann übergeben wird, wenn der Benutzer einen vorhandenen Datensatz bearbeitet. Die Bild 7: Löschen eines Zeitraums Prozedur prüft zunächst, ob für den vierten, optionalen Parameter lngvereinszugehoerigkeitid ein Wert übergeben wurde oder ob dieser den initialen Wert 0 enthält. Ist dieser Wert 0, wurde der Parameter nicht übergeben und die Prozedur stellt eine SELECT-Abfrage zusammen, welche alle Datensätze liefert, die zu dem aktuellen Mitglied gehören. Hat lngvereinszugehoerigkeitid nicht den Seite 34

37 FORMULARE UND STEUERELEMENTE ZEITRÄUME PER LISTENFELD UND INPUTBOX Wert 0, was der Bearbeitung eines vorhandenen Datensatzes der Tabelle tblvereinszugehoerigkeiten entspricht, wird die SELECT-Abfrage um ein zusätzliches Kriterium erweitert, welches den in Bearbeitung befindlichen Zeitraum der Vereinszugehörigkeit ausschließt. Wir wollen den Public Function AufUeberschneidungenPruefen(datEintritt As Date, dataustritt As Date, lngmitgliedid As Long, _ Optional lngvereinszugehoerigkeitid As Long) Dim db As DAO.Database, rst As DAO.Recordset Dim strsql As String, bolueberschneidung As Boolean, straustritt As String Set db = CurrentDb If lngvereinszugehoerigkeitid = 0 Then strsql = "SELECT * FROM tblvereinszugehoerigkeiten WHERE MitgliedID = " & lngmitgliedid Else strsql = "Select * FROM tblvereinszugehoerigkeiten WHERE MitgliedID = " & lngmitgliedid _ & " AND NOT VereinszugehoerigkeitID = " & lngvereinszugehoerigkeitid Set rst = db.openrecordset(strsql, dbopendynaset) Do While Not rst.eof If dateintritt <= rst!eintrittsdatum Then If dataustritt <= rst!eintrittsdatum Then bolueberschneidung = False Else bolueberschneidung = True Exit Do ElseIf dateintritt >= rst!austrittsdatum Then bolueberschneidung = False Else bolueberschneidung = True Exit Do rst.movenext Loop If bolueberschneidung = True Then If dataustritt = 0 Then straustritt = "-(Ende offen)" Else straustritt = "-" & dataustritt MsgBox "Der Zeitraum " & dateintritt & straustritt & " überschneidet sich mit dem Zeitraum " _ & rst!eintrittsdatum & "-" & rst!austrittsdatum & "." AufUeberschneidungenPruefen = False Else AufUeberschneidungenPruefen = True End Function Listing 4: Prüfen auf Überschneidungen von Mitgliedszeiträumen Seite 35

38 FORMULARE UND STEUERELEMENTE ZEITRÄUME PER LISTENFELD UND INPUTBOX bearbeiteten Zeitraum schließlich nicht mit dem Zeitraum im Originalzustand vergleichen. Nachdem wir die SQL-Anweisung zusammengestellt haben, öffnen wir mit der OpenRecordset-Methode ein Recordset auf Basis dieser Anweisung. Danach durchlaufen wir alle Datensätze in einer Do While-Schleife. Hier beginnt nun der Abgleich der mit dateintritt und dataustritt gelieferten Zeitraums mit den bereits gespeicherten Zeiträumen dieses Mitglieds. Die erste If... Then-Bedingung prüft, ob dateintritt kleiner oder gleich dem gespeicherten Eintrittsdatum des aktuellen Datensatzes ist.falls ja, untersuchen wir, ob dataustritt ebenfalls kleiner als rst!eintrittsdatum ist. Falls beide kleiner sind, erhält die Variable bolueberschneidung den Wert False, falls nicht, den Wert True in diesem Fall ist zwar dat- Eintritt kleiner als rst!eintrittsdatum, aber dataustritt ist größer der bearbeitete Zeitraum überschneidet sich zumindest teilweise mit dem gespeicherten Zeitraum. Ist im ElseIf-Teil dateintritt größer oder gleich rst!austrittsdatum, liegt der Zeitraum auf jeden Fall hinter dem gespeicherten Zeitraum. Der Else-Teil untersucht dann die übrigen Fälle, die nur lauten können: dateintritt ist größer als rst!eintrittsdatum und kleiner als rst!austrittsdatum, was bedeutet, dass sich die Zeiträume überschneiden. Auf diese Weise untersuchen wir alle gespeicherten Datensätze in der Tabelle zum aktuell angezeigten Mitglied und verlassen die Do While-Schleife sofort, wenn bolueberschneidung einmal den Wert True erhält. Diesen Wert prüfen wir im Anschluss. Hat bolueberschneidung den Wert True, prüfen wir noch, ob der Benutzer das Austrittsdatum festgelegt hat oder nicht. Falls nicht, schreiben wir die Zeichenkette -(Ende offen) in die Variable straustritt, sonst das Minuszeichen und das Austrittsdatum. Schließlich geben wir eine entsprechende Meldung aus, die angibt, welcher Zeitraum sich mit dem bearbeiteten Zeitraum überschneidet. Die Funktion gibt dann mit Auf- UeberschneidungenPruefen den entsprechenden Wert zurück. Prüfung auf Überschneidungen aufrufen Der Aufruf dieser Funktion tragen Sie in der Prozedur cmdneuevereinszugehoerigkeit_click an den Stellen ein, wo die INSERT INTO-Abfrage ausgeführt wird (an zwei Stellen):... If AufUeberschneidungenPruefen(datEintritt, dataustritt, µ... Me!MitgliedID) = True Then db.execute "INSERT INTO...", dbfailonerror Me!lstVereinszugehoerigkeiten.Requery In der Prozedur lstvereinszugehoerigkeiten_dblclick benötigen wir nur einen Aufruf dieser Funktion: If AufUeberschneidungenPruefen(datEintritt, dataustritt, µ Me.MitgliedID, lngvereinszugehoerigkeitid) = True Then db.execute "UPDATE...", dbfailonerror Me!lstVereinszugehoerigkeiten.Requery Somit werden die Änderungen an der Tabelle tblvereinszugehoerigkeiten nur durchgeführt, wenn der neue oder bearbeitete Zeitraum auch zu den vorhandenen Zeiträumen passt. Zusammenfassung und Ausblick Dieser Beitrag zeigt, wie Sie Zeiträume in einem Listenfeld anzeigen und per Doppelklick auf einen der Einträge oder auf eine Neu-Schaltfläche bearbeiten oder anlegen können. Auch eine Löschen-Schaltfläche steht zur Verfügung. Die Änderungen haben wir statt in einem Detailformular für die Vereinszugehörigkeiten diesmal mit einfachen InputBox-Anweisungen realisiert. Seite 36

39 INTERAKTIV SVG ALS GRAFIKKOMPONENTE SVG als Grafikkomponente In zwei Beiträgen rund um das Thema 'HTML5 als Grafikkomponente' erfuhren Sie, wie Sie mithilfe des Webbrowser-Steuerelements und HTML5 programmgesteuert Grafiken erzeugen. Über eine andere Schnittstelle, nämlich SVG, lässt sich dasselbe erreichen, wobei hier nicht Pixel-, sondern Vektorgrafiken erzeugt werden, die deutlich mehr Interaktivität zulassen und nachträglich bearbeitbar sind. Referenz Die Ausführungen dieses Beitrags setzen jene aus dem Artikel HTML5 als Grafikkomponente in der Ausgabe 01/2018 fort. Dort sind die Grundlagen zum Einsatz des Webbrowser-Steuerelements im Verein mit HTML5 beschrieben, auf die hier deshalb nicht noch einmal eingegangen wird. SVG als Alternative zu HTML5 Über HTML5 können Sie unter Access im Webbrowser- Steuerelement Bilder anlegen, indem Sie dessen zugrundeliegendes HTML-Objektmodell steuern. Als Ergebnis erhalten Sie eine Pixelgrafik als Summe der Grafikoperationen, deren Schritte sich nicht mehr rückgängig machen oder bearbeiten lassen. Lediglich das Abspeichern als Bilddatei oder im BLOB- Feld einer Tabelle steht Ihnen dann noch offen. Ganz anders bei SVG, wo die Grafik im Vektorformat entsteht, auf deren einzelne Elemente jederzeit zugegriffen werden kann. Solche Grafiken rendert der Browser dann zur Laufzeit. Es verwundert eigentlich, dass diese flexible Lösung noch von niemand aufgegriffen und umgesetzt wurde dachte ich! Tatsächlich findet, während ich dies zu Papier bringe, in Wien die zweite Access-DevCon statt, eine internationale Entwicklerkonferenz, welche unter anderem Vorträge von Experten zu verschiedenen Themenstellungen rund um Access anbietet. Und der litauische Entwickler Alexandru Tofan, ein bisher weitgehend unbeschriebenes Blatt, demonstriert dort ebenfalls seine Lösungen zum Einsatz von SVG als Grafik-Engine. Ich bin gespannt, welche Ähnlichkeit seine Ausführungen zu den hier ganz unabhängig vorgestellten haben werden, falls sie veröffentlicht werden... Der Hauptgrund dafür, dass eine Implementierung von SVG im Webbrowser Control noch nicht stattfand, ist wohl darin zu suchen, dass Microsoft dem Internet Explorer erst spät den Umgang mit diesem Format beibrachte. Und da das Webbrowser-Steuerelement im Rohzustand nur die Version 7 des Browsers emuliert, welche noch kein SVG kannte, laufen die sehr wohl im HTML-Objektmodell enthaltenen zugehörigen Methodenaufrufe ins Leere. Erst die Modifikation des Webbrowsers über die VBA-Routine PrepareBrowser, die, wie im oben angeführtem Beitrag, schon mehrmals erläutert wurde, lässt ihn im Gewand des IE 11 erscheinen. Danach akzeptiert er anstandslos auch alle SVG-Methoden. SVG als Format SVG (Scalable Vector Graphics) ist an sich ein auf XML basierendes Dateiformat, welches seit dem Jahr 2001 existiert und kontinuierlich weiterentwickelt wurde. Dateien mit der Endung.svg können etwa mit dem Open Source-Programm Inkscape angezeigt oder erstellt werden, welches sich fast schon zum Standard gemausert hat. Aber auch andere Vektorzeichenprogramme beherrschen meist den Export in dieses Format. Inzwischen hat es sich aber vor allem im Web als wichtigste Schnittstelle zur Anzeige von Vektorgrafiken etabliert. Der Vorteil von Vektorgrafiken gegenüber Pixelbildern ist ja die verlustfreie Skalierbarkeit, denn solche Grafiken werden grundsätzlich erst zur Laufzeit beim Seite 37

40 INTERAKTIV SVG ALS GRAFIKKOMPONENTE Rendern in Pixel übersetzt. Bei technischen Zeichnungen, Organigrammen, Charts oder Karten ist es deshalb das Format der Wahl. Einst ging es in erster Linie um die Anzeige solcher SVG- Grafikdateien, für die bis dahin nur ein spezielles in die Website eingebettetes Adobe-ActiveX-Steuerelement infrage kam. Doch seit der Implementierung direkt im Browser kam auch die dynamische Steuerung der einzelnen Elemente der Grafik hinzu. So lassen sich SVG-Grafiken nun auch ohne jeglichen Bezug zu einer Datei etwa über Javascript anlegen und beeinflussen. Und glücklicherweise hat Microsoft im HTML-Objektmodell des Internet Explorers alle Elemente von Javascript nach COM übersetzt, womit auch unter VBA Zugriff auf die entsprechenden Klassen und Methoden besteht, ohne dass Sie sich mit einer anderen Skriptsprache beschäftigen müssen. Die SVG-Grafiken stellen nun einfach eine Folge von Grafikanweisungen dar, die im XML-Format abgelegt sind. Der Browser interpretiert diese und rendert sie anhand der übergebenen Parameter. Dafür gibt es das neue Tag svg, welches allerdings nur dann korrekt interpretiert wird, wenn ein bestimmtes Namespace-Attribut mit xmlns hinzugefügt wird: <svg xmlns=" (Inhalt) <svg> Innerhalb dieses Tags befinden sich dann die Anweisungen. Das können Linien sein, Rechtecke, Kreise, Ellipsen, Polygone, Bezier-Kurven, Text, Füllungen, Pixeldaten oder Verweise auf Bilddateien. All diese Elemente können über Attribute mit diversen Parametern versehen werden, die deren Gestalt bestimmen. Der Clou aber sind mehrere Grafikfilter, die Sie auf einfache Weise anlegen und nach Belieben auf diese Elemente anwenden können. So kann etwa gefüllter Kreis mit einem Blur-Filter verwaschen werden, dessen Parameter Sie ebenfalls steuern können. Eine einfache Line erzeugen Sie etwa über dieses Konstrukt: <svg xmlns=" height="300" width="500"> <line x1="0" y1="0" x2="200" y2="200" </svg> style="stroke:black;stroke-width:2"/> Hier sind dem svg-tag gleich die Attribute width und height zugeordnet, die die Abmessungen der Grafikfläche (500x300 Pixel) festlegen. Die Linie erzeugen Sie über das line-tag. Die Koordinatenpunkte der Linie sind über die Werte der Attributparameter x1, y1, x2, y2 definiert. Das Aussehen der Linie bestimmt nun das style-attribut, welches seinerseits eine Menge weiterer Definitionen aufnehmen kann, die sich aus dem Fundus der HTML-Styles ableiten, die Sie eventuell auch abseits von SVG kennen. So gibt stroke etwa an, welche Farbe die Line haben soll, was hier auf Schwarz (black) festgelegt ist. Natürlich sind hier auch beliebige andere über Hexadezimalwerte definierte Farben möglich, wie #A0A0A0, oder über die rgb-funktion ermittelte: stroke:rgb(40,20,105) Die Breite der Linie steht schließlich im Parameter stroke-width (2 Pixel). Übrigens können Sie innerhalb des svg-tags wiederum ein oder mehrere svg-tags einbetten, was zu verschachtelten Zeichenflächen führt. SVG dynamisch unter VBA erzeugen Der Witz am HTML-Objektmodell ist aber ja, dass Sie den HTML-Text nicht unbedingt per String-Verkettung generieren müssen, sondern sich der passenden HTML-Klassen bedienen, deren Eigenschaften Sie dann einstellen. So gibt es zur Linie etwa das Element SVG- LineElement, welches Sie so erzeugen: Seite 38

41 INTERAKTIV SVG ALS GRAFIKKOMPONENTE Dim odoc As HTMLDocument Dim osvgline As SVGLineElement Set odoc = WebCtl.Object.Document Set osvgline = odoc.createelementns( _ " "line") Das HTML-Dokument odoc erhalten Sie hier über die Document-Eigenschaft des Webbrowser-Steuerelements WebCtl. Normalerweise reicht die Methode createelement aus, um auf der Website ein neues HTML-Element zu erzeugen. Hier allerdings wird die Alternative createelementns benötigt, weil auf den SVG- Namespace Bezug genommen werden muss, damit der Webbrowser erkennt, um welches line-element es sich genau handelt. So erzeugt stellen Sie nun die Eigenschaften des SVG- LineElements ein: Bild 1: Eine im Webbrowser-Steuerelement per VBA generierte SVG-Grafik als Grafikgenerator zum Zuge kommen. Bild 1 zeigt es zur Laufzeit, nachdem auf die Schaltfläche SVG erzeugen geklickt wurde. osvgline.x1 = 0: osvgline.y1 = 0 osvgline.x2 = 200: osvgline.y2 = 200 Leider sind das auch schon alle Eigenschaften, die diese Klasse bei Microsoft direkt anbietet. Alle anderen müssen Sie über die Methode setattribute belegen: osvgline.setattribute "stroke", "black" osvgline.setattribute "stroke-width", "2" Das Formular enthält lediglich ein Webbrowser-Steuerelement mit dem Namen ctlie, dessen Steuerelementinhalt im Entwurf auf "=about:blank" festgelegt ist und einen Button, der die Anlage der SVG-Grafik auslöst. Damit das Webbrowser-Control mit SVG umgehen kann, muss zuvor die Routine PrepareBrowser des Moduls mdlwebcontrol aufgerufen worden sein, welche Einstellungen der Registry modifiziert, wie dies bereits erschöpfend in den Beiträgen zu HTML5 erläutert wurde. Die Methode setattribute finden Sie im Objektkatalog nicht unter der Klasse SVGLineElement, und trotzdem funktioniert sie. Das liegt daran, dass sich ein SVG- LineElement versteckt vom allgemeineren IHTMLElement ableitet, welches diese Methode aufweist. SVG-Grafik-Beispiel Das Formular frmsvgtest der Demodatenbank vereint die wesentlichen Techniken, die für den Einsatz von SVG Der Klick auf den Button cmdcreate lässt die Ereignisprozedur in Listing 1 ablaufen. Hier kommt es zunächst zum Aufruf der Hilfsroutine NavigateBlank, die weiter unten abgebildet ist. Sie stellt per Navigate2-Methode sicher, dass im Webbrowser ctlie ein leeres Dokument angelegt ist. Nachdem dieses erfolgreich fertiggestellt ist, was die Do-Loop-Schleife mit Abfrage der ReadyState-Eigenschaft kontrolliert, kann die modulweit deklarierte Objektvariable odoc auf dieses Dokument gesetzt Seite 39

42 INTERAKTIV SVG ALS GRAFIKKOMPONENTE werden. Das WithEvents in der Deklaration ermöglicht, dass auf Ereignisse der gesamten Website, wie Mausklicks, reagiert werden kann. Private WithEvents odoc As HTMLDocument Private WithEvents osvg As SVGSVGElement Private oevent As IHTMLEventObj Private Sub cmdcreate_click() Dim osvgelement As SVGCircleElement Dim oelement As IHTMLElement Im Header des Dokuments muss anschließend eine meta-eigenschaft hinterlegt werden, die X-UA- Compatible lautet, damit der Browser die SVG-Anweisungen auch umsetzt. Dies geschieht durch Beschreiben des Tags über die Methode innerhtml des head-objekts. Und nun kann bereits die SVG-Zeichenfläche im Dokument mit createelementns angelegt werden, wobei hier das Element- Tag svg als String übergeben wird. Das Element landet in der Objektvariablen osvg vom Typ SVGS- VGElement, die ebenfalls im Kopf des Moduls mit WithEvents deklariert ist, damit auf Ereignisse speziell der Zeichenfläche reagiert werden kann. NavigateBlank Die Zeichenfläche ist damit zwar erstellt, hat bislang jedoch noch keine definierten Abmessungen. Dies holt der folgende With- Block auf die Objektvariable nach. Um die Breite des svg-elements einzustellen, können Sie zwei Methoden odoc.head.innerhtml = "<head><meta http-equiv=""x-ua-compatible""/></head>" Set osvg = odoc.createelementns(" "svg") With osvg.width.baseval.value = 300.Height.baseVal.Value = 250.Style.backgroundColor = "#e0e0e0" End With Set osvgelement = odoc.createelementns(" "circle") With osvgelement.setattribute "id", "Kreis".setAttribute "cx", "50".setAttribute "cy", "50".setAttribute "r", "40".setAttribute "stroke", "darkblue".setattribute "stroke-width", "4".setAttribute "fill", "red" End With osvg.appendchild osvgelement odoc.body.appendchild osvg Debug.Print odoc.all(0).outerhtml Sub NavigateBlank() ctlie.object.navigate2 "about:blank" Do DoEvents Loop Until ctlie.object.readystate = 4 Set odoc = ctlie.object.document Listing 1: Der wesentliche Code-Teil des Formulars frmsvgtest erzeugt im Click-Ereignis die Grafik einsetzen. Die eine ist das Setzen des width-attributs über die Methode setattribute, die andere die Nutzung der eingebauten Eigenschaft Width der Objektklasse. Seite 40

43 INTERAKTIV SVG ALS GRAFIKKOMPONENTE Letztes geschieht hier. Allerdings handelt es sich bei Width nicht etwa um einen Integer-Wert, sondern um einen speziellen zusammengesetzten SVG-Datentyp, dessen Eigenschaft baseval.value verwendet werden muss. Sie bekommt den Wert 300 Pixel zugewiesen. Ähnliches erfolgt für die Höhe der Fläche und Height. Und dann erhält die Zeichenfläche noch eine Hintergrundfarbe, die über das Style-Attribut backgroundcolor mit einem hexadezimalen Grau-Wert versehen wird. Das SVG-Objekt wird erst in das Dokument geschrieben und damit sichtbar, wenn es am Ende der Routine über appendchild an den Body odoc.body angehängt wird. Zuvor muss jedoch noch der SVG-Kreis erzeugt und an das SVG-Objekt selbst angehängt sein. Deshalb generiert createelementns nun ein Element circle und weist es als Objekt der Variablen osvgelement vom MSHTML-Typ SVGCircleElement zu. Sie verweist nun auf ein abstraktes Kreis-Objekt, dessen Eigenschaften erst noch eingestellt werden müssen. Der folgende With-Block mit den setattribute-anweisungen übernimmt dies. Die Mittelpunktkoordinate wird mit cx und cy festgelegt, der Radius in r ohne dezidierte Angabe von Einheiten geht der Browser dabei von Pixeln aus. Dem Element wird ein eindeutiger Bezeichner Kreis über die id spendiert, damit es später identifizierbar und damit sogar auch dynamisch bearbeitbar ist. Die Farbe des Kreises steht in stroke, die Linienbreite in strokewidth und die Füllfarbe im Attribut fill. Das Kreiselement wird schließlich mit appendchild an das SVG-Flächenelement osvg angehängt, welches selbst dann in den Body geschrieben wird. Ab nun ist die Grafik tatsächlich sichtbar. Obwohl wir nur mit MSHTML-Objekten hantieren und keinerlei String-Bearbeitung einsetzen, übersetzt sich das Ganze automatisch in gültigen HTML-Text. Die Debug.Print-Anweisung verdeutlicht, was dabei herauskommt und gibt im VBA-Direktfenster diesen String aus, der hier der Übersicht halber mit Einrückungen formatiert wurde: <html> <head><meta http-equiv="x-ua-compatible""/></head> <body> </html> <svg xmlns=" style="background-color: rgb(224, 224, 224);" width="300" height="250"> </svg> </body> <circle id="kreis" fill="red" stroke="darkblue" stroke-width="4" cx="50" cy="50" r="40" /> SVG Knowledgebase Es liegt weit außerhalb des Radius dieses Beitrags, alle SVG-Elemente mit ihren Parametern darzustellen. Empfehlung: Schauen Sie sich etwa auf der deutschen Seite SELFHTML um, die unter der Rubrik SVG mit vielen Beispielen erschöpfend Auskunft gibt. Dort sind die möglichen Elemente und ihre Attribute gut erläutert. Das Vorgehen zur Übersetzung der HTML-Tags nach VBA ist dann immer dasselbe. Sie erzeugen ein Element über die Funktion createelementns unter Angabe des Element-Tags, weisen es einer Objektvariablen zu, die den generellen Typ SVGElement haben kann oder auch schlicht Object, und setzen dessen Eigenschaften am besten über setattribute-anweisungen innerhalb eines With-Blocks. SVG-Filter Das Formular frmsvgtest enthält noch eine weitere Schaltfläche Komplexes SVG, deren Betätigung zur Grafik in Bild 2 führt. Hier sind genau zwei SVG-Elemente angelegt, nämlich ein gelber Kreis mit grüner Umrandung und das Bild einer Tulpe aus einer JPG-Datei. Auf das Bild wird aber ein Weichzeichnenfilter angewandt, auf den Kreis ein Turbulenzfilter. Hier kommen neue Techniken ins Spiel. Das Prinzip ist allerdings das gleiche, wie im vorigen Beispiel. Ein SVG-Element wird angelegt und an den Seite 41

44 INTERAKTIV SVG ALS GRAFIKKOMPONENTE Bild 2: Einsatz von Filtern für die komplexere SVG-Grafik per VBA Body des über about:blank erzeugten Dokuments gehängt. Das Vorgehen, um die Bilddatei in die Grafik zu bekommen, ist hier nicht trivial. Zwar könnte man das Attribut href des image-elements mit dem absoluten Pfad zur Datei belegen, doch beim Verschieben der Datenbank und der Bilddatei tulpe.jpg in ein anderes... Verzeichnis wäre der Bezug verloren. Deshalb wird ReDim bin(lof(1) - 1) aus der Bilddatei ein sogenanntes binäres data-uri Close #1 Get #1,, bin erzeugt und dessen Inhalt dem Bildelement unmittelbar übergeben. Hinter- With osvgelement grund dafür ist zusätzlich, dass sich so auch Dateien, die etwa in OLE-Feldern von in Tabellen abgespeichert sind, ebenfalls zur End With Ansicht bringen lassen. Das data-uri kommt... dabei als base64-kodierter String in das Dokument. Diese Funktionalität kam bereits im früheren Beitrag zum Zeichnen und Malen mit HTML5 zum Einsatz. Einen Teil der Ereignisprozedur cmdcreate- Complex_Click zeigt Listing 2. Die Bilddatei wird zuerst mit der Open-Anweisung in das Byte-Array bin eingelesen. Dann wird das leere SVG-Bildelement über das Tag image erzeugt. Es bekommt die id dataimage, die Breite und Höhe 200px (width, height), und die Bilddaten über href aus einem Hexadezimal-String, der mittels der Hilfsfunktion EncodeBase64 im Modul mdlhelper aus dem Array erzeugt wird. Der Internet Explorer zeigt das Bild dann allerdings aus Sicherheitsgründen erst an, wenn zusätzlich die Eigenschaft crossorigin auf anonymous gesetzt wird, weil sonst die Herkunft der Binärdaten für ihn ungeklärt ist. Das Weichzeichnen kommt schließlich zustande, indem Sie der filter-eigenschaft den Ausdruck url(#blur) Open CurrentProject.Path & "\tulpe.jpg" For Binary Access Read As #1 Set osvgelement = odoc.createelementns(" "image").setattribute "id", "dataimage".setattribute "width", "200".setAttribute "height", "200".setAttribute "href", "data:image/jpeg;base64," & EncodeBase64(bin).setAttribute "crossorigin", "anonymous".setattribute "filter", "url(#blur)" osvg.appendchild osvgelement Listing 2: Einlesen der Bilddatei in ein Byte-Array und Übergeben desselben an das SVG-image-Element Seite 42

45 INTERAKTIV SVG ALS GRAFIKKOMPONENTE übergeben. Darin verweist der Bezeichner blur auf einen SVG-Filter, der allerdings schon eigenständig angelegt sein muss. Auch dies lässt sich über SVG-Objekte bewerkstelligen. Im ersten Schritt wird das Filter-Objekt erzeugt: Set osvgfilter = odoc.createelementns( _ " "filter") osvgfilter.setattribute "id", "blur" Er bekommt als Eigenschaft lediglich die id blur. Denn das Filter-Objekt bewirkt selbst noch keinen Filtereffekt und stellt nur einen Container für einen oder mehrere Effekte dar. Ein SVG-Element kann nur über diesen etwas umständlichen Weg mit einem Bildeffekt versehen werden. Dem Element muss im Attribut filter der Name des Filters übergeben werden, wobei die Syntax url(#<id>) verwendet wird. Das Filter-Element wiederum benötigt als Kindelement ein Filtereffekt-Element. Das geschieht in der Prozedur so: Set osvgeffect = odoc.createelementns( _ " "fegaussianblur") osvgeffect.setattribute "in", "SourceGraphic" osvgeffect.setattribute "stddeviation", "10" osvgfilter.appendchild osvgeffect osvg.appendchild osvgfilter Das SVG-Element ist hier vom Typ fegaussianblur und erzeugt einen Gaussschen Weichzeichner. Seine Eigenschaft stddeviation bestimmt den Radius des Weichzeichnens je höher, desto verwischter. Das Attribut in gibt an, woher die Ausgangsgrafik kommt. Mit dem Wert sourcegraphic bestimmen Sie, dass das SVG-Element selbst dafür herangezogen wird. Das Effektobjekt hängen Sie schließlich an das Filterobjekt an und dieses selbst an das SVG-Objekt, also die generelle SVG-Fläche. Dabei spielt es keine Rolle, in welcher Reihenfolge Sie vorgehen. Sie können erst das Filterobjekt erzeugen und dann das SVG-Zeichenelement, oder umgekehrt. MSHT- ML analysiert die Sache selbst und generiert aus den Objekten die funktionierenden HTML-Anweisungen. Mit dem Turbulenzfilter für das Kreiselement wird es nochmals komplizierter. Hier müssen nämlich zwei Effekte miteinander kombiniert werden. Der eine SVG- Effekt erzeugt einen Turbulenzgenerator: Set osvgfilter = odoc.createelementns( _ " "filter") osvgfilter.setattribute "id", "turb" Set osvgeffect = odoc.createelementns( _ " "feturbulence") osvgeffect.setattribute "type", "fractalnoise" osvgeffect.setattribute "basefrequency", "0.03" osvgeffect.setattribute "numoctaves", "3" osvgeffect.setattribute "stitchtiles", "stitch" osvgfilter.appendchild osvgeffect Der neue Filter bekommt die willkürliche id turb. Ihm wird ein Effekt des Typs feturbulence spendiert, auf dessen Parameter wir nicht weiter eingehen. Sie sind andernorts ausführlich dargestellt. Nun handelt es sich bei diesem Effekt lediglich um eine Art abstrakten Generator, der ein zweidimensionales Rauschen hervorbringt. Auf ein SVG-Element angewandt wüsste der Browser noch nicht, was er mit dem Rauschen anfangen soll. Denn der Generator besitzt keinen Eingang in! Darum muss an den Filter noch ein weiterer Effekt angehängt werden, wobei wir hier einen Verschiebeeffekt nutzen, der die Pixel des Ausgangselements um den Wert eines Rauschens verschiebt: Set osvgeffect = odoc.createelementns( _ " "fedisplacementmap") osvgeffect.setattribute "in", "SourceGraphic" osvgeffect.setattribute "in2", "turbulence" osvgeffect.setattribute "scale", "50" osvgfilter.appendchild osvgeffect Seite 43

46 INTERAKTIV SVG ALS GRAFIKKOMPONENTE Der SVG-Effekt trägt den Namen fedisplacementmap. Er besitzt zwei Eingänge, mit in die Herkunft der Grafik, auf die der Effekt angewandt werden soll, und mit in2 den Turbulenzeffekt. Zusätzlich kann mit scale noch angegeben werden, wie stark sich der Effekt aus in2 ausdehnt, also skaliert. Auch dieser Effekt wird dem Filterelement angehängt. Erst jetzt kann der Filter dem Kreis übergeben werden: Set osvgelement = odoc.createelementns( _ " "circle") With osvgelement.setattribute "id", "displacedcircle".setattribute "cx", "50".setAttribute "cy", "50".setAttribute "r", "40".setAttribute "stroke", "green".setattribute "stroke-width", "4".setAttribute "fill", "yellow".setattribute "filter", "url(#turb)" End With osvg.appendchild osvgelement Im letzten Attribut wird der Filter turb angewiesen, und schon verzerren sich der Kreis und dessen Füllung entsprechend dem Turbulenzgenerator. Dies sind nur zwei Beispiele für SVG-Filter, die die grundsätzliche Vorgehensweise verdeutlichen sollen. Insgesamt kennt SVG aktuell 16 solcher Filtereffekte, wobei bei diesen weniger an Bildbearbeitung gedacht wurde, als an die für Zeichnungen und Texte nützliche. Deshalb handelt es sich bei vielen um solche für Farbkombinationen oder -verschiebungen oder Elementkombinationen. Die entsprechende SELFHTML-Seite zu SVG/Filter gibt eine gute Übersicht. Um es nochmals deutlich zu machen: Sie können die Filter auf jedes beliebige SVG-Grundelement anwenden! Also nicht nur auf Kreise, Rechtecke, Linien, Polygonzüge oder Kurven, sondern etwa auch auf Text. Allerdings muss es sich dann bei Letzterem um SVG-Textelemente handeln. Ein Textelement legen Sie dergestalt an: Set osvgelement = odoc.createelementns( _ " "text") With osvgelement.setattribute "x", 20.setAttribute "y", 30.setAttribute "style", _ "font-family:calibri;font-size:12px;fill:#404040;".innertext = "Ein SVG-Textelement" End With Das Element text platziert sich auf der SVG-Fläche an der Koordinate 20/30 (linke obere Ecke des Textblocks) und hat den über style definierten Schriftstil Calibri mit der Höhe 12 Pixel. Die Schriftfarbe steht in fill mit einem hexadezimalen Dunkelgrau. Den eigentlichen Text schreiben Sie dann in die Eigenschaft innertext. SVG-Ereignisse Imgrunde kann jedes HTML-Element Events, wie Mausklicks, auslösen, die Sie in Klassenmodulen abfangen können, wenn Sie eine modulweit per WithEvents deklarierte Objektvariable auf sie setzen. Das verhält sich bei SVG-Elementen ebenso. Allerdings leiten sich die dabei verfügbaren Ereignisse praktisch alle von der allgemeinen Klasse HTMLElement ab, die allen SVG- Elementen untergeschoben ist. Aber gerade hier haben SVG-Grafiken einen enormen Vorteil gegenüber HTML5- Grafiken. Da die Elemente alle zur Laufzeit gerendert werden, kann jederzeit auf sie Bezug genommen werden. Ein Element können Sie etwa zur Laufzeit löschen oder über seine Attributparameter verändern. Nun ist es etwas mühsam allen SVG-Elementen bei komplexeren Grafiken jeweils eine Objektvariable zuzuweisen, um auf deren Ereignisse zu reagieren. Da verliert man schon mal die Übersicht. Das ist indessen auch gar nicht nötig, weil es eine kompaktere Lösung gibt. Dabei wird etwa für einen Mausklick lediglich das entspre- Seite 44

47 INTERAKTIV SVG ALS GRAFIKKOMPONENTE chende Event des HTML-Dokuments selbst abgefangen und erst innerhalb dieser Ereignisprozedur das Element abgefragt, welches das Ereignis auslöste. Im Formular frmsvgtest ist auch dies implementiert. Der Ausgangspunkt ist dabei das HTML-Dokument, welches der Variablen odoc zugewiesen wird: Private WithEvents odoc As HTMLDocument Der Klick auf irgendeine Stelle im fertigen Dokument löst dann ein Click-Ereignis aus, das von einer Ereignisprozedur verarbeitet werden kann: Private Function odoc_onclick() As Boolean Set oevent = odoc.parentwindow.event If oevent.button = 0 Then End Function Select Case oevent.srcelement.id Case "Kreis" MsgBox "Kreis angeklickt", vbexclamation End Select Der Trick ist hier, dass eine weitere Objektvariable oevent auf die Event-Eigenschaft des Browser-Fensters gesetzt wird. Sie ist so deklariert: Private oevent As IHTMLEventObj Und dieser Objekttyp kennt unter anderen die Methode Button, die Auskunft über die Maustaste gibt, welche gedrückt wurde. Eine 0 bedeutet dabei linke Maustaste. Die Bedingung in der Prozedur wertet dies aus und entscheidet dann über ein Select-Case-Statement auf die ID des auslösenden Elements (srcelement), wie weiter verfahren wird. Handelt es sich um den SVG-Kreis, der zuvor generiert wurde und dessen id Kreis lautet, dann erscheint eine Meldung. In den Select-Case-Block können Sie nun beliebig viele Elemente abfragen und individuell auf sie reagieren. Voraussetzung ist natürlich, dass alle mit einem eindeutigen id-attribut ausgestattet wurden. Möchten Sie dennoch auf ein Element direkt zugreifen, dann geschieht das so, wie beim Tulpenbild im Formular: Private WithEvents osvgimage As SVGImageElement... Private Function osvgimage_onclick() As Boolean MsgBox "SVG Bild angeklickt" End Function Natürlich muss dazu die Objektvariable auf das angelegte SVG-Bildelement verweisen, was sinnvollerweise bereits beim Erzeugen per createelementns geschieht. Ein etwas speziellerer Fall ist das Ereignis bei der rechten Maustaste. Das fängt man eher nicht mit dem onclick-ereignis ab, sondern mit dem dafür vorgesehenen oncontextmenu, weil die Aktion in der Regel ein Kontextmenü hervorruft: Private Function osvgimage_oncontextmenu() As Boolean MsgBox "Statt svg image Kontextmenü" End Function Wenn Sie diese Ereignisprozedur einsetzen, so erscheint also eine Meldung, nicht aber das Kontextmenü. Grund hierfür ist der Umstand, dass es sich um eine Funktion handelt, der Sie einen Boolean-Wert zuweisen können. Ohne weitere Programmierung ist der Rückgabewert deshalb False. Tatsächlich können Sie dies aber ändern, indem Sie statt der Msgbox diese Zeile einfügen: osvgimage_oncontextmenu = True Nun erscheint beim Rechtsklick auf das Tulpenbild tatsächlich das Kontextmenü. Einfacher aber ist es wieder, sich das globale Kontextmenü des Dokuments zunutze zu machen: Seite 45

48 INTERAKTIV SVG ALS GRAFIKKOMPONENTE Private Function odoc_oncontextmenu() As Boolean Set oevent = odoc.parentwindow.event Select Case oevent.srcelement.id Case "displacedcircle" odoc_oncontextmenu = True Case "Kreis" MsgBox "Kreis rechtsgeklickt", vbinformation End Select End Function Auch hier wird wieder das Event-Objekt befragt und anhand der id des auslösenden Elements entschieden, welche Aktion ausgelöst wird. Beim einfachen Kreis kommt es zu einer Meldung ohne Kontextmenü. Bei Turbulenzkreis des zweiten Beispiels, der die id displacedcircle trägt, wird stattdessen das Kontextmenü des Dokuments aktiviert. Das Kontextmenü des Dokuments lässt sich allerdings nur mit großem Aufwand modifizieren oder mit eigenen Einträgen bestücken. Die MSHTML-Bibliothek reicht dafür nicht aus, und die in früheren Beiträgen eingesetzte Bibliothek oleexp.tlb müsste her, weil es hier um den Umgang mit speziellen Interfaces geht. Das lassen wir außen vor. Es ist dann aber fraglich, ob das Standardkontextmenü in einer SVG-Grafik etwas zu suchen hat und den Anwender eher verwirrt. Schalten Sie es einfach ab, indem Sie die obige Ereignisfunktion einsetzen, aber keinerlei Code in sie schreiben. Diagramme mit SVG Im Beitrag zu HTML5 lernten Sie die Anlage von Balken- und Tortendiagrammen auf Basis der Daten eines Recordsets kennen. In der Beispieldatenbank zum aktuellen Beitrag bilden wir exakt dieselbe Funktionalität Bild 3: Die Balkendiagramme des Formulars frmsvgdiagram präsentieren sich wie ein Ebenbild der HTML5-Version (frmhtml5diagram) Seite 46

49 INTERAKTIV SVG ALS GRAFIKKOMPONENTE ab und versuchen dabei auch möglichst die gleichen Prozedurnamen zu verwenden. Eingesetzt wird nun jedoch nicht HTML5, sondern SVG. Rufen Sie dazu das Formular frmsvgdiagram auf klicken auf Erzeuge Diagramm. Es entsteht ein Balkendiagramm, welches genau der Version unter HTML5 entspricht (siehe Bild 3), egal, ob Sie das Diagramm für Monatsumsätze oder Umsatz nach Ländern auswählen. Nur das Tortendiagramm weicht in seiner Gestalt etwa ab, weil hier auf die Texte zusätzlich ein zusammengesetzter SVG-Effekt angewendet wird, der zu Demonstration einen DropShadow-Schatten nachbildet (siehe Bild 4). Tatsächlich entspricht der Entwurf des Formulars genau der HTML5-Version. Und auch der Code zeigt nur wenige Abweichungen. Statt der für das Generieren des Balkendiagramms verantwortlichen Klasse clshtml5diagram kommt nun das neue Klassenmodul clssvgdiagram zum Zug, für die Tortengrafik das Klassenmodul clssvgpiechart. Deren Prozeduren haben deshalb auch die gleichen Namen, so dass die Versionen schnell austauschbar wären. Die neuen Klassen instanziieren sich ebenfalls im Load- Ereignis des Formulars: Private CDgm As clssvgdiagram Private CDgmPie As clssvgpiechart Private Sub Form_Load() Set CDgm = New clssvgdiagram Set CDgmPie = New clssvgpiechart Bild 4: Auch die SVG-Tortengrafiken entsprechen den HTML5-Versionen, zeigen bei den Texten jedoch zusätzlich einen bläulichen Schatten Seite 47

50 INTERAKTIV SVG ALS GRAFIKKOMPONENTE Zu besprechen ist folglich nicht das neue Formular selbst, sondern die neuen Klassenmodule. Dabei wird jedoch nur auf das eingegangen, was die Module von den HTML5-Versionen unterscheidet und sich zuvörderst in der Prozedur PaintData niederschlägt. Function CreateDiagramDoc(Browser As WebBrowser) Ende: Fehler: Dim odiv As HTMLDivElement Dim oelmt As IHTMLElement On Error GoTo Fehler Set odoc = Browser.Document odoc.body.setattribute "bgcolor", ColorToStr(vbWhite) odoc.head.innerhtml = _ "<meta http-equiv=""x-ua-compatible""/>" Set odiv = odoc.createelement("div1") odiv.id = "div1" Set osvg = CreateSVGElement("svg") osvg.setattribute "id", "svgimage" osvg.setattribute "width", CStr(m_Width) osvg.setattribute "height", CStr(m_Height) odiv.appendchild osvg odoc.body.appendchild odiv PaintData CreateDiagramDoc = True Exit Function MsgBox Err.Description, vbcritical Resume Ende End Function Listing 3: Die Routine legt ein Dokument samt SVG im Browser an. Das Diagramm wird im Webbrowser-Steuerelement über die Prozedur CreateDiagramDoc erzeugt, der Sie lediglich über den Objektparameter einen Verweis auf den Browser übergeben. Der Rest erfolgt automatisch. Natürlich übernimmt diese Prozedur nun nicht mehr die Anlage eines HTML5-Canvas im Dokument, sondern die eines SVG-Elements, wie Listing 3 verdeutlicht. Es ist wieder in einen DIV-Bereich eingebettet, der es im Zweifel einfacher gestattet, die Grafik im Dokument zu verschieben, ohne die eigentliche SVG-Grafik anzufassen. Der Teil zum Erzeugen der Diagrammelemente ist in die Prozedur PaintData ausgegliedert, die hier abschließend aufgerufen wird. Ihre 120 Zeilen können wir hier nicht in einem Listing abbilden und beschränken uns auf die für SVG-Elemente relevanten Teile aus ihr. Der Diagrammrahmen wird über ein Rechteckelement erzeugt: Set osvgelement = CreateSVGElement("rect") Weil die wiederholte Übergabe des Namespace-Arguments an die MSHTML-Methode createelementns etwas ermüdend ist, gibt es die Hilfefunktion CreateS- VGElement, der Sie nur noch das Element-Tag übergeben. Die Hilfsfunktion fügt den Namespace hinzu: Private Function CreateSVGElement( _ ByVal tag As String) As SVGElement Set CreateSVGElement = odoc.createelementns( _ End Function " tag) Diese Funktion wird anschließend auch für alle weiteren SVG-Elemente verwendet. Die Eigenschaften des Elementobjekts erfahren ebenfalls eine Spezialbehandlung. Teilweise werden sie nicht über die Methode setattribute direkt gesetzt, sondern über eine Hilfsfunktion BaseValueLen. Das gilt für alle Eigenschaften, die Maße betreffen, weil die bei SVG-Objekten, wie schon früher erwähnt, den zusammengesetzten Typ SVGAnimated- Length aufweisen: With osvgelement BaseValueLen.x, xoffset: BaseValueLen.y, 20 BaseValueLen.Width, m_width xoffset BaseValueLen.Height, m_height - 50 BaseValueLen.rx, 6: BaseValueLen.ry, 6.setAttribute "style", "fill:" & _ Seite 48

51 INTERAKTIV SVG ALS GRAFIKKOMPONENTE End With ColorToStr(m_BackColor) & _ ";fill-opacity:0.5; stroke:darkblue;" & _ "stroke-width:2;opacity:0.7" Die Hilfsfunktion belegt lediglich die Eigenschaft baseval des in L übergebenen Maßes mit dem Wert aus val: Private Sub BaseValueLen(L As SVGAnimatedLength, val As Variant) L.baseVal.Value = val Das erspart zwar etwas Schreibarbeit, hat jedoch noch einen anderen Grund. Die Werte für die Koordinaten etwa stammen ja aus Integer-Variablen, während die Methode setattribute als Argument einen String erwartet. Folglich müssten alle Integer- oder Fließkommawerte über die VBA-Funktionen Str oder CStr erst in Text umgewandelt werden. Einfacher ist da die Zuweisung über einen Variant-Parameter, den baseval.value ohnehin erwartet. Der obige Code-Block ist übrigens ein Beispiel für ein gerundetes SVG-Rechteck, denn die Attribute rx und ry spezifizieren das Maß der Rundung mit dem Wert 3 Pixel. Tatsächlich weicht die Grafik in diesem Punkt von der HTML5-Version ab. Die folgenden Schleifen der Prozedur haben alle den gleichen Aufbau, wie die der HTML5-Version. Statt der HTML5-Zeichenoperationen mit der Übergabe von Parametern erfolgt hier die Anlage von SVG-Elementen, deren Attribute anschließend gesetzt werden. Das macht den Code etwas länger, dafür aber übersichtlicher. Greifen wir uns einmal den gekürzten Teil zum Generieren der Datenbeschriftungen heraus (Listing 4). Die Schleife durchläuft alle Datensätze des im Property DataRecordset der Klasse übergebenen Recordsets m_rs. Für jeden Datensatz wird ein text-element Do While Not m_rs.eof Loop Set osvgelement = CreateSVGElement("text") With osvgelement.setattribute "x", x.setattribute "y", _ m_height - IIf((i Mod 2) = 0, 16, 2).setAttribute "style", _ "font-family:calibri;font-size:12px;" & _ "fill:#404040;opacity:0.9".innertext = CStr(m_RS(0).Value) End With osvg.appendchild osvgelement x = x + w1 i = i + 1 m_rs.movenext Listing 4: In dieser Schleife werden die SVG-Datentexte angelegt. erzeugt und an die SVG-Fläche osvg angehängt. x und y sind berechnete Werte, die die Positionen der Texte über den Datenbalken bestimmen. Im style-attribut ist der Schriftstil hartkodiert. opacity definiert dabei eine leichte Transparenz der Schriftzüge, damit die Rasterlinien durchscheinen. In einem erweiterten Klassenmodul würde man hierfür eventuell flexible Schrifteigenschaften als Properties und Member-Variablen vorsehen, damit das Diagramm besser angepasst werden könnte. Die Attribute werden dabei direkt gesetzt, also nicht die Hilfsfunktion BaseValLen herangezogen. Das funktioniert deshalb, weil es sich bei den Koordinaten um reine positive Integerwerte handelt, die automatisch korrekt in einen String umgewandelt werden. Anders bei der Beschriftung über innertext selbst: Hier muss erst CStr() auf den Fließkommawert angesetzt werden, damit ein analoger String aus ihm wird. Das Dezimaltrennzeichen ist dann ein Punkt. Mit Str() würde es ein Komma. Nach dem Durchlaufen der Routine PaintData zeigt sich nun das fertige Diagramm im Webbrowser-Steuerelement. Seite 49

52 INTERAKTIV SVG ALS GRAFIKKOMPONENTE Im Gegensatz zur HTML5-Version weist es noch den Umstand auf, dass es auf Mausklicks reagiert. Zum einen erscheint bei Rechtsklick das übliche Kontextmenü. Unter produktiven Bedingungen würde man es eher abschalten. Hier wurde es jedoch aktiviert, weil es einige interessante Funktionen bereithält. So führt der Eintrag Bild speichern unter... zu einem Dateiauswahldialog, der die Grafik nicht als Bitmap, sondern als Vektorgrafikdatei blank.svg abspeichern möchte, was sich auch problemlos machen lässt. Die so erstellte SVG-Datei kann aber etwa Inkscape nicht einlesen. Bei Inspektion unter einem Texteditor zeigt sich, dass der Internet Explorer nicht nur den SVG- Teil des Dokuments exportiert hat, sondern auch das DIV-Tag. Entfernt man dieses aus der Datei, so, dass sie mit <svg..> beginnt, dann kann das Diagramm auch unter Inkscape angezeigt werden. Dort macht sich dann auch schon der Vorteil von SVG-Vektorgrafiken bemerkbar: Das Diagramm kann beliebig skaliert werden, ohne dass es zu unscharfen Linien oder Texten kommt. Ein linker Mausklick auf das Diagramm führt zu einer Messagebox, die einen Zahlenwert ausgibt. Für die Datenbeschriftungen ergibt das einen Sinn, für die anderen Elemente weniger. Die zugehörige Ereignisprozedur dient nämlich nur zur Demonstration einer neuen Technik und sieht so aus: Private Sub osvg_onmouseup() Dim oelement As SVGElement Set oevent = odoc.parentwindow.event If oevent.button = 1 Then Set oelement = odoc.elementfrompoint( _ oevent.offsetx, oevent.offsety) Select Case oelement.tagname Case "rect": MsgBox oelement.x.animval.value Case "text": MsgBox oelement.innertext End Select Da keiner der SVG-Elemente im Dokument mit einer id versehen wurde das sollten Sie jedoch immer tun!, lässt sich ein Element, welches das Ereignis auslöste, auch nicht so einfach identifizieren. Trotzdem kann einiges über es erfahren werden. Das Event-Objekt hat etwa die Eigenschaften offsetx und offsety, die Auskunft über die Koordinate geben, an die geklickt wurde. Aus dieser Koordinate kann die Funktion elementfrompoint des Dokuments das Element-Objekt ermitteln und an die Variable oelement zuweisen. Handelt es sich bei ihm um ein Rechteck (tagname = rect) oder einen Text (tagname = text), so greifen die entsprechenden Bedingungen des Select-Case-Blocks, und als Meldung wird entweder der Text selbst ausgegeben, oder bei Rechtecken der x-wert der Koordinate. Das bringt uns, wie gesagt, wenig, zeigt jedoch, wie Sie auch ohne Ids an die auslösenden Elemente gelangen. Abspeichern als JPG-Bild Während das Ausdrucken der Diagramme wenig Probleme bereitet und im Formular über die Methode exec- Command des Dokuments im Verein mit dem Parameter print ausgelöst werden kann, bereitet der Export in eine Bilddatei mehr Kopfzerbrechen, als in der HTML5-Version. Dort nämlich besteht die Grafik ohnehin aus einem Bitmap, so dass Sie über den data-uri des image- Objekts an die Pixeldaten gelangen. Die SVG-Grafik aber wird ja erst zur Laufzeit gerendert! Der Browser veröffentlicht indessen keinerlei Methoden, um an die gerenderten Daten zu gelangen. Hier bleibt nur der indirekte Zugriff auf das Browser- Fenster und eine Art Screenshot über API-Funktionen. Das war mein erster Ansatz, der jedoch obsolet wurde, nachdem ich auf die raffinierte API-Funktion OLEDraw stieß. Dieser kann man ein OLE-Objekt übergeben, dessen visuellen Inhalt sie dann in einen in einen beliebigen Windows Device Context (DC) rendert. Das funktioniert mit jedem visuellen OLE-Objekt und damit auch mit einem HTMLDocument. Der Clou aber ist, dass man ihr zusätzlich mitteilen kann, wie groß die gerenderte Grafik Seite 50

53 INTERAKTIV SVG ALS GRAFIKKOMPONENTE werden soll. Das ermöglicht ein Abspeichern in eine Bilddatei, deren Bildabmessungen viel größer sind, als das im Webbrowser präsentierte Diagramm. Die API-Funktion interagiert dazu mit der OLE-Anwendung und sagt ihr, welche Größe sie haben möchte. Der Browser übergibt ihr dann das gerenderte Bild. Das heißt im Klartext, dass ein Diagramm, welches im Browser die Ausdehnung 800x800 Pixeln besitzt, auch als Bild in der Größe von 4000x4000 Pixeln ausgegeben werden kann, ohne dass Elemente verschwimmen. Aus einer Line der Breite 2px wird dann einfach eine messerscharfe mit 10px. Tatsächlich kann die Funktion aber nicht nur eine Bilddatei erstellen, sondern das Bild auch in die Zwischenablage legen. Das geschieht dann, wenn Sie den letzten Parameter ToClipboard auf True setzen. Eine Bilddatei wird in diesem Fall nicht angelegt. Die Funktion ruft wiederum jeweils die Prozedur SaveAsImage der Klassenmodule clssvgdiagram und clssvgpiechart auf (Listing 5). Der Prozedur übergeben Sie den gewünschten Dateinamen samt Pfad oder einen Leer-String. In diesem Fall tritt ein Speichern-unter-Dialog auf den Plan, über den Natürlich kommt man bei dieser API-Funktion auch nicht ohne eine umfangreichere API-Routine herum. Das Modul mdlgdipspecial der Beispieldatenbank übernimmt alles Nötige. Es basiert auf dem GDIPlus-API und lässt den Export eines OLE-Objekts als Bilddatei im JPG-Format zu. Sie brauchen nur die Methode SaveToImageFile aufzurufen, die die folgende Syntax aufweist: Function SaveOLEToImageFile( _ ByVal OleObj As stdole.iunknown, _ ByVal W As Long, _ ByVal H As Long, _ ByVal FileName As String, _ Optional ByVal Quality As Byte = 90, _ Optional ByVal ToClipboard As Boolean) As Boolean Der erste zu übergebende Parameter ist das OLE- Objekt, in unserem Fall also ein Verweis auf das HTML- Dokument-Objekt des Webbrowser-Steuerelements. Mit W und H geben Sie an, welche Breite und Höhe das erzeugte Bild haben soll. Daran schließt sich der volle Pfad zur zu exportierenden Datei an, gefolgt vom optionalen Quality. Ohne Angabe wird der Qualitätsfaktor 90 verwendet, was für die meisten Zeichnungen ausreichend sein dürfte. Geringere Werte lassen meist kleine Artefakte erscheinen, während Sie mit 100 in jedem Fall auf der sicheren Seite sind. Sub SaveAsImage(ByVal FileName As String, _ Optional ByVal dscale As Double = 1, _ Optional Preview As Boolean) Dim W As Long, H As Long Dim ret As Boolean Dim sfile As String sfile = FileName If Not odoc Is Nothing Then If Len(sFile) = 0 Then sfile = GetSaveFileDialog(, "SVGDiagramm.jpg") If Len(sFile) = 0 Then Exit Sub With odoc.getelementbyid("svgimage") W = dscale *.Width.baseVal.Value H = dscale *.Height.baseVal.Value.focus End With ret = SaveOLEToImageFile(oDoc, W, H, sfile, 85) If ret Then If Preview Then ShellExecute 0&, "open", sfile, _ vbnullstring, vbnullstring, 5& Listing 5: Abspeichern des Diagramms als Bild im Klassenmodul Seite 51

54 INTERAKTIV SVG ALS GRAFIKKOMPONENTE Sie das Verzeichnis und den Dateinamen bestimmen können. Der Parameter dscale ist ein Fließkommawert, der standardmäßig auf 1 steht und damit das Bild in den Abmessungen exportiert, wie es im Browser zu sehen ist. Ein Wert von 0.5 verkleinert das Bild, ein Wert von 3 verdreifacht seine Abmessungen. Setzen Sie schließlich den Parameter Preview auf True, so öffnet Windows die exportierte Bilddatei über die Shell automatisch, was je nach Dateizuordnung in der Regel das Programm Windows-Bildvorschau sein dürfte. Da die API-Funktion OLEDraw zwingend die Angabe von Breite und Höhe des zu rendernden Bilds erwartet, muss diese zuvor errechnet worden sein. In der Routine wird dazu das SVG-Flächenelement svgimage im Dokument herausgesucht (getelementbyid) und dessen Breite etwa über seine Eigenschaft Width.basVal.Value erhalten. Diese Funktion der Klasse ruft nun das Ereignis Click der Als Bild speichern...-schaltfläche des Diagrammformulars auf: Tortendiagramm Das Klassenmodul für Tortendiagramme (clssvgpiechart) ist exakt gleich aufgebaut, wie das für die Balkendiagramme. Nur die Prozedur PaintData hat natürlich einen komplett anderen Inhalt. Aus Platzgründen gehen wir auf deren Gestaltung nicht ausführlich ein, da die wesentlichen Infos bereits ausreichend beim Balkendiagramm dargestellt wurden. Interessant ist aber vielleicht das Verfahren zum Zeichnen der Tortenstücke. Am einfachsten wäre es, einen Kreis und für jedes Tortenstück jeweils eine Line vom Mittelpunkt zum Radius zu zeichnen. Das war auch die Methode, die beim HTML5-Diagramm zum Einsatz kam. Die so erzeugte Figur konnte dort problemlos mit einer Zufallsfarbe gefüllt werden. Anders bei SVG. Da hier keine Pixelgrafik vorliegt, kann eine aus mehreren SVG- Elementen gezeichnete Figur nicht so einfach gefüllt werden, schon gar nicht, wenn es sich um Linienelemente handelt. Nur ein einzelnes SVG-Element kann im style-attribut über fill eine bestimmte Füllfarbe erhalten. Also benötigen wir hier ein Element, das sich sowohl aus Linien, wie Kurven zusammensetzt. Die Wahl fällt darum auf ein SVG-path-Element. Private Sub cmdsaveimg_click() If optgrp2.value = 1 Then Else CDgm.SaveAsImage "", 2, True CDgmPie.SaveAsImage "", 2, True Je nachdem, ob das Generieren eines Balken- oder Tortendiagramms in der Optionsgruppe optgrp2 ausgewählt ist, kommt entweder die Klassenvariable CDgm oder CDgmPie zum Zug und ruft die Methode SaveAsImage auf. Als Skalierungsfaktor ist fest 2 eingesetzt, so dass die exportierte Bilddatei die doppelte Größe aufweisen wird, wie das im Browser angezeigte Diagramm. Ein Path-Element kann nämlich mehrere verschiedenartige Segmente beherbergen. Das können gerade Linien sein, Kreisteile und Bezier-Kurven. Selbst das nichtvisuelle Verschieben des Stifts zu einer Koordinate, bei der er aufsetzt, ist möglich. Wenn Sie sich im Objektkatalog die Methoden der Klasse ISVGPathElement anschauen, wird das deutlich. createsvgpathsegmovetoabs setzt den Zeichenstift quasi an eine bestimmte Koordinate. createsvgpathseglinetoabs zeichnet von dort aus eine Linie zur übergebenen Koordinate, createsvgpath- SegArcAbs würde von dort aus einen Kreisabschnitt zeichnen. Genau diese drei Segmentarten kommen für die Tortenstücke zum Einsatz, während weitere, wie createsvgpathsegcurvetocubicabs zum Zeichnen einer Bezier-Kurve, hier nicht benötigt werden. Diese Segmente werden als Objekte an das Path-Element über dessen Elementliste pathseglist angefügt, wobei wie dafür die Methode appenditem verwenden. Damit die Figur gefüllt werden kann, muss sie komplett Seite 52

55 INTERAKTIV SVG ALS GRAFIKKOMPONENTE geschlossen sein, was die Methode createsvgpath- SegClosePath übernimmt. Die Figur muss nun nur noch über das style-attribut mit Werten für die Linienbreite, -farbe, Transparenz und Füllung versehen werden. Der Dropshadow-Effekt für die Textschatten schließlich erfordert einen etwas komplexeren SVG-Filter. Einen einzelnen SVG-Effekt gibt es dafür nicht, weshalb dieser Filter sich aus mehreren miteinander kombinierten bildet. Im Einzelnen sind dies fegaussianblur zum Weichzeichnen des Texts, feoffset zum Verschieben des weichgezeichneten Texts als Hintergrund, femerge und femergenode zum Kombinieren dieser beiden Effekte, und schließlich feblend zum transparenten Einblenden des Schattens unter den Text. Die Beschreibung der genauen Funktionsweise würde an dieser Stelle den Rahmen sprengen. Im Klassenmodul ist der entsprechende Abschnitt der Routine PaintData in den Kommentarblock DropShadow-Filter eingebunden. Die Technik ist Ergebnis einer Recherche nach den Suchbegriffen SVG Dropshadow im Netz, die etliche Beispiele HTML-Beispiele und JavaScripte zutage fördert. Diese wurden lediglich nach VBA und MSHTML konvertiert. SVG-Animationen Ein besonderes Feature von SVG ist die Möglichkeit, die SVG-Elemente in fast allen Parametern zu animieren. Der Radius eines Kreises etwa kann zur Laufzeit über dessen Attribut r geändert werden. Dazu ist es noch gar nicht einmal notwendig, das Attribut etwa in einer Schleife fortwährend neu zu setzen. Vielmehr gibt es ein spezielles SVG-Element animate, das Sie als Kindelement einem SVG-Grundelement hinzufügen. Dem animate-element sagen Sie dann über Attribute, welche Parameter des Elternobjekts zu animieren sind. Sie können etwa den Radius bestimmen, sagen, innerhalb welcher Grenzen sich dieser verändern soll, und festlegen, in welcher Zeit das erfolgen soll. Der Browser nimmt diese Parameteränderung dann selbständig vor, ohne dass Sie auch nur eine zusätzliche VBA-Code-Zeile dafür benötigen. Allerdings hat Microsoft die SVG-animate-Elemente und -Methoden (noch?) nicht alle direkt in das Objektmodell übersetzt. Um den Rahmen dieses Beitrags nicht zu sprengen, enthält die Demo-Datenbank auch keine Beispiele für diese Techniken. Mehr erfahren Sie etwa im Netz unter SELFHTML unter der Rubrik SVG/animate. Zu den SVG-Diagrammen ist nun alles Wesentliche gesagt. Um mehr Interaktivität mit den SVG-Elementen geht es dann im gesonderten Beitrag zum Malen und Zeichnen mit SVG. Ein letztes Beispiel zum Umgang mit SVG integrieren wir hier aber noch. Interaktive SVG-Karte Sie müssen SVG-Zeichnungen nicht von Grund auf per VBA neu erstellen. Sie können ebenso eine existierende SVG-Datei laden und weiterverarbeiten. Die könnte über den Export aus einem Vektorzeichenprogramm stammen wie Inkscape. Im vorliegenden Beispiel des Formulars frmsvgworld lädt das Webbrowser-Steuerelement eine SVG-Europakarte, die auf Klicks reagiert (siehe Bild 5). Die Betätigung des Buttons SVG-Grafik laden des Formulars weist den Browser oweb lediglich an, direkt zur SVG-Datei zu navigieren: Private Sub cmdloadsvg_click() oweb.navigate2 CurrentProject.Path & "\europa.svg" Die Datei europa.svg muss sich dazu im Datenbankverzeichnis befinden. Der Vorgang ist abgeschlossen, wenn das Dokument fertig gerendert ist, was das Ereignis NavigateComplete2 hervorruft. Dort wird die WithEvents deklarierte Objektvariable odoc auf das Dokument im Browser gesetzt: Private Sub oweb_navigatecomplete2(byval pdisp As µ Object, URL As Variant) Set odoc = oweb.document Seite 53

56 INTERAKTIV SVG ALS GRAFIKKOMPONENTE Bild 5: Das Formular frmsvgworld lädt im Webbrowser das Bild einer SVG-Datei und erzeugt bei Mausklick auf einen Bereich eine Meldung Ein Mausklick auf das Dokument löst diese Ereignisprozedur aus: Private Function odoc_onclick() As Boolean Dim oevent As Object Set oevent = odoc.parentwindow.event If TypeName(oEvent.srcElement) = "SVGPathElement" Then End Function MsgBox oevent.srcelement.id Bei allen gefüllten Länderfiguren der Karte handelt es sich um SVG-path-Elemente. Die filtert die Routine heraus (srcelement = SVGPathElement) und zeigt in einer Meldung dann deren id an. Das gb steht für Großbritannien, und das ist die id, die dem Path-Element in der Datei spendiert ist. Genauso gut könnte man weitere Eigenschaften und Attribute des Elements ausgeben lassen, oder gar über VBA das style-attribut neu belegen und etwa die Hintergrundfarbe des Lands ändern. Seite 54

57 VBA UND PROGRAMMIERTECHNIK OBJEKT- UND FELDNAMEN REFAKTORIEREN Objekt- und Feldnamen refaktorieren Es kommt vor, dass man als Access-Entwickler mit der Weiterbearbeitung von Datenbanken betraut wird. Oft geschieht es dann, dass Objekt- und Feldnamen nicht den gängigen Konventionen entsprechen. Tabellennamen kommen ohne Präfix, Objekt- und Feldnamen enthalten Umlaute, Leerzeichen et cetera. Um dadurch entstehende Probleme zu vermeiden, können wir die Benennungen anpassen. Wie dies gelingt, zeigt der vorliegende Beitrag. Die Tabelle und die Feldnamen des Beispiels aus Bild 1 hat jeder schon einmal gesehen. Die Tabelle wurde ohne Präfix benannt, hier würden wir uns ein führendes tbl wünschen. Das Primärschlüsselfeld hätten wir lieber ohne Unterstrich (wobei das Geschmackssache ist). Definitiv sollten wir aber Straße in Strasse ändern und im Feld Telefon geschäftlich wollen wir nicht nur den Umlaut durch die entsprechenden Vokale ersetzen und das Leerzeichen weglassen. Außerdem verwenden wir in der Regel Camel Case und schreiben das zweite und die folgenden Worte groß, hier also TelefonGeschaeftlich. Das Ergebnis soll in unserem Fall also wie in Bild 2 aussehen. Bild 1: Tabellenname und Feldnamen sollten refaktoriert werden. Notwendige Änderungen finden In unserem Beispiel ist es leicht, die zu ändernden Elemente zu finden. Bei einer Tabelle mit wenigen Feldern erledigt man den Job auch am einfachsten von Hand. Das bezieht sich allerdings nur auf die reine Änderung der Objekt- und Feldbezeichnungen es gibt ja auch Referenzen auf diese Bezeichnungen in anderen Tabellen, Abfragen, Formularen, Berichten, Makros und auch im VBA-Code. Und wenn Sie aber eine große Datenbankanwendung mit vielen Tabellen anpassen wollen, Bild 2: Tabelle mit Präfix im Namen und mit angepassten Feldnamen haben Sie noch mehr zu tun. Es ist also eine Frage der Datenbankgröße, wann Sie es aufgeben, die Änderungen manuell zu erledigen und der Wunsch auftaucht, die Änderungen automatisch auszuführen. Wie das gelingt, soll Seite 55

58 VBA UND PROGRAMMIERTECHNIK OBJEKT- UND FELDNAMEN REFAKTORIEREN der vorliegende Beitrag zeigen. Vorher schauen wir uns aber noch an, welche Änderungen im Detail anfallen und wo diese sich überall auswirken. Referenzen auf Objektnamen und Feldnamen Die Namen von Tabellen und Feldnamen können bereits in anderen Tabellen auftauchen nämlich dort, wo Sie Nachschlagefelder eingerichtet haben. Wenn wir unserer Anwendung also eine Tabelle namens Anreden m/w mit den Feldern Anrede-ID und Anrede finden, kann es sein, dass ein Fremdschlüsselfeld namens Anrede-ID in der Kunden-Tabelle vorliegt, welches die Anreden-Tabelle referenziert das sieht dann im Entwurf wie in Bild 3 aus. Wenn wir nun auch die Bezeichnungen der verknüpften Tabelle ändern, dann müssen wir darauf achten, dass auch die Verweise angepasst werden. Zum Glück unterstützt uns Access hier wie, schauen wir uns weiter unten an. Aber auch in Abfragen, Formularen und Berichten sowie in Makros finden wir unter Umständen Verweise auf die Bild 3: Verweis auf andere Tabelle im Tabellenentwurf Namen von Objekten und Feldern. Ob diese automatisch angepasst werden, schauen wir uns ebenfalls an. Auf keinen Fall erfolgt eine automatische Anpassung im VBA-Code. Hier müssen wir selbst Hand anlegen. Damit wir hier keine Änderung auslassen, werden wir später ein wenig Code produzieren, der diese Aufgabe für uns übernimmt. Die Objektnamen-Autokorrektur Access hat schon viele Versionen eine Funktion namens Objektnamen-Autokorrektur. Diese sorgt dafür, dass Änderungen von Objekt- und Feldnamen direkt an Stellen übernommen werden, welche die betroffenen Objekte und Felder referenzieren. Bild 4: Einstellungen für die Objektnamen-Autokorrektur Die Einstellungen für die Objektnamen-Autokorrektur nehmen Sie in den Access- Optionen vor. Dort finden Sie diese unter Aktuelle Datenbank im Bereich Optionen für die Objektnamen-Autokorrektur (siehe Bild 4). Wir aktivieren hier zusätzlich noch die Seite 56

59 VBA UND PROGRAMMIERTECHNIK OBJEKT- UND FELDNAMEN REFAKTORIEREN Option Änderungen für Objektnamenautokorrektur protokollieren. Zu Testzwecken enthält die Beispieldatenbank einige Objekte, die auf die Tabelle und die enthaltenen Felder verweisen. Auf diese Weise können wir nach der Änderung prüfen, ob die Änderungen auch auf die referenzierenden Objekte übertragen wurden. Wir verwenden dazu ein Formular namens frmkunden, das nur Daten der Tabelle Kunden anzeigt, ein Formular namens frmkundenmitanreden, welches eine Abfrage mit den Daten aus den beiden Tabellen als Datenherkunft verwendet und ein Formular mit Unterformular, welches die Anreden im Hauptformular und die zugeordneten Kunden im Unterformular anzeigt. Eine Abfrage namens qrykundennachanrede fasst die Daten der beiden Tabellen Kunden und Anreden zusammen. Auf Makros wollen wir an dieser Stelle verzichten. Außerdem schreiben wir ein paar VBA-Prozeduren, welche auf die Daten der Tabellen Kunden und Anreden zugreifen. Bevor wir die Tabellen- und Feldnamen angepasst haben, haben wir die vorherige Version der Datenbank gesichert, und zwar unter dem Namen FeldnamenRefaktorieren_Backup.accdb. Feld Kunde-ID wird KundeID Feld Anrede-ID wird AnredeID Feld Straße wird Strasse Feld Telefon geschäftlich wird TelefonGeschaeftlich Nachdem die Änderungen an den Tabellen Kunden und Anreden durchgeführt wurden, sehen diese wie in Bild 5 aus. Durch die Änderungen an der Tabelle Anreden, die nun tblanreden heißt, müssten auch Anpassungen am Fremdschlüsselfeld AnredeID der Tabelle tblkunden vorgenommen worden sein. Nach der Änderung schauen wir uns den Entwurf der Tabelle tblkunden an und wechseln dort für das Feld AnredeID zur Registerseite Nachschlagen der Feldeigenschaften. Hier finden wir die korrekt geänderte Abfrage vor (siehe Bild 6). Komplexere Ausdrücke Wenn wir nun statt einer einfachen SELECT-Anweisung einen komplizierteren, von Hand geänderten Ausdruck für die Abfrage in der Eigenschaft Datensatzherkunft nutzen, funktioniert die Objektnamen-Autokorrektur in unseren Tests immer noch. Wenn Sie zum Beispiel einfach zwei Felder verknüpfen wie im folgenden Beispiel, wird die Abfrage ebenfalls ordnungsgemäß angepasst: Danach nehmen wir folgende Änderungen vor: Name der Tabelle Anreden auf tblanreden Feld Anrede-ID wird AnredeID Feld Anrede m/w wird Bezeichnung Tabelle Kunden wird tblkunden Bild 5: Die Tabellen nach der Änderung Seite 57

60 VBA UND PROGRAMMIERTECHNIK OBJEKT- UND FELDNAMEN REFAKTORIEREN SELECT [Anreden].[Anrede-ID], [Anreden]. [Anrede m/w] & Anreden.[Anrede-ID] FROM Anreden; Bild 6: Fremdschlüsselfeld nach der Änderung der verknüpften Tabelle Bild 8: Änderungen durch die Objektnamen-Autokorrektur Die Tabelle Objektnamen- Autokorrekturprotokoll Da wir die Option Änderungen für Objektnamenautokorrektur protokollieren aktiviert haben, erstellt Access nun eine neue Tabelle namens Objektnamen-Autokorrekturprotokoll, in der es die einzelnen durch die Änderung verursachten Änderungen einträgt. Genaugenommen muss man es etwas anders formulieren: Die automatischen Änderungen werden nämlich nicht schon in die Tabelle eingetragen, sobald der Programmierer die auslösenden Änderungen vornimmt, sondern erst, wenn die von den automatischen Änderungen betroffenen Objekte erstmals nach der Änderung geöffnet werden. Bild 7: Die Abfrage qrykundenmitanrede vor den Änderungen Access prüft also wohl erst beim Öffnen der Objekte, ob es Änderungen in den zugrunde liegenden Objekten gibt und nimmt dann die Autokorrektur vor. Dies ist auch Seite 58

61 VBA UND PROGRAMMIERTECHNIK OBJEKT- UND FELDNAMEN REFAKTORIEREN die korrigierten Tabellen- und Feldnamen vor (siehe Bild 9). Formulare Auch die drei vorbereiten Formulare wollen wir uns anschauen. Beim ersten Formular namens frmkunden hat dies gut geklappt. Wie Bild 10 zeigt, wurde nicht nur die als Datenherkunft verwendet Tabelle von Kunden in tblkunden geändert, sondern auch die Namen der von den Steuerelementen referenzierten Felder. Bild 9: Die Abfrage qrykundenmitanrede nach den Änderungen Das zweite Formular namens frm- KundenMitAnrede, das Sie in Bild 11 der Grund, warum man bei Datenbanken, deren Performance nicht überzeugt, als eine der ersten Aktionen die Autokorrektur abschaltet. Die Tabelle Objektnamen- Autokorrekturprotokoll sieht schließlich wie in Bild 8 aus. Sie enthält bereits Einträge, die durch die nachfolgend beschriebenen Änderungen verursacht wurden. Bild 10: Formular mit geänderten Steuerelementinhalten und Quelltabelle Abfragen Dann gehen wir weiter zur Abfrage qrykundenmitanrede. Diese sah zuvor wie in Bild 7 aus. Nachdem wir sie nach den Änderungen erneut geöffnet haben, finden wir dort ebenfalls Bild 11: Auch mit einer Abfrage als Datenherkunft gelingt die Autokorrektur. Seite 59

62 VBA UND PROGRAMMIERTECHNIK OBJEKT- UND FELDNAMEN REFAKTORIEREN sehen, wurde wie gewünscht angepasst. Es bezieht seine Daten ja aus einer Abfrage, welche die Daten der beiden Tabellen tblkunden und tblanreden zusammenfasst. Das dritte Formular frmkundennachanrede hatte die Besonderheit, dass wir die Daten der Kunden-Tabelle zu einer im Hauptformular ausgewählten Anrede in einem Unterformular anzeigen. Hier haben wir nach der Änderung der Tabellen- und Feldnamen also besonderes Augenmerk auf die beiden Eigenschaften Verknüpfen von und Verknüpfen nach des Unterformular-Steuerelements gelegt. Aber auch hier hat die Objektnamen-Autokorrektur wie erwartet gearbeitet Bild 12: Sogar die Einstellung der Verknüpfungsfelder für das Unterformular erfolgt korrekt. (siehe Bild 12). An dieser Stelle gehen wir davon aus, dass die Objektnamen-Autokorrektur auch in Berichten und Makros funktioniert und wenden uns dem VBA-Code zu. Bild 13: VBA-Code wird nicht automatisch korrigiert. Refactoring im VBA- Code Eine Stelle, die definitiv nicht von der Objektnamen- Autokorrektur erfasst wird, ist der VBA-Code. Die von uns vorbereitete Testprozedur, welche die Kunden- Tabelle unter dem alten Namen referenziert und auch die alten Feldnamen verwendet, löst nach dem Ändern von Objekt- und Feldnamen einen Fehler aus, weil die Tabelle nicht Seite 60

63 VBA UND PROGRAMMIERTECHNIK OBJEKT- UND FELDNAMEN REFAKTORIEREN mehr gefunden werden konnte (siehe Bild 13). Selbst wenn der Tabellenname korrekt wäre, würde die übernächste Zeile beim Zugriff auf die alten Feldnamen eine Fehlermeldung liefern. Was tun? Es gibt drei Möglichkeiten: Sie durchsuchen den VBA-Code Zeile für Zeile und ersetzen die geänderten Objekt- und Feldnamen. Sie holen sich Unterstützung vom Suchen und Ersetzen-Dialog. Sie programmieren Code, der automatisch die gewünschten Ersetzungen durchführt. Bild 14: Suchen und Ersetzen per Dialog Suchen und Ersetzen Diese Variante starten Sie im VBA-Editor über den Menü- Eintrag Bearbeiten Ersetzen... oder über die Tastenkombination Strg + H. Letztere hat aber, wenn Sie das Tool MZ-Tools nutzen, eine andere Funktion; Sie müssen dann den Menüeintrag bemühen. Der Dialog sieht wie in Bild 14 aus und bietet zunächst einmal zwei Felder, in die Sie den gesuchten Ausdruck und den ersetzenden Ausdruck eingeben müssen. Die erste Option fällt flach außer, Sie haben nur extrem wenig Code in der Datenbank. Die zweite Option ist möglich, aber je nach der Anzahl der zu ändernden Objektnamen und Feldnamen auch aufwendig. Die dritte Option ist natürlich die Lieblingsoption des versierten VBA-Entwicklers, denn hier kann er selbst programmieren und muss keine niederen Arbeiten verrichten. Außerdem trainieren wir das Gehirn damit viel mehr als mit manuellem Suchen und Ersetzen! Wir schauen uns also die zweite und die dritte Option an. Änderungen notieren Bevor Sie eine der beiden verbleibenden Methoden nutzen, sollten Sie eine Liste der Änderungen an den Objektnamen und Feldnamen anlegen, damit Sie später keine Änderung im Code vergessen. Die Optionsgruppe Suchen in legt den Bereich fest, in dem Sie suchen und ersetzen möchten. Hier wählen wir logischerweise das aktuelle Projekt aus. Die Werte für die Suchrichtung und die beiden Optionen Nur ganzes Wort suchen und Groß-/Kleinschreibung beachten lassen wir deaktiviert. Die Option Mit Mustervergleich ist jedoch interessant. Warum, das erfahren Sie gleich. Kunden-Tabelle im VBA-Code suchen und ersetzen Geben wir einmal den Tabellennamen Kunden in das Feld Suchen nach: des Dialogs ein und den neuen Namen tblkunden in das Feld Ersetzen durch:, führt das nicht ganz zum gewünschten Ergebnis: Auf diese Weise werden nämlich auch die Vorkommen der Zeichenkette Kunden gefunden, die nicht ersetzt werden sollen und in einem anderen Zusammenhang verwendet werden beispielsweise der Name der Objektvariablen rstkunden (siehe Bild 15). Hier könnte man nun prüfen, in welchen Kontexten die Zeichenfolge Kunden wirklich ersetzt werden soll. In Seite 61

64 VBA UND PROGRAMMIERTECHNIK OBJEKT- UND FELDNAMEN REFAKTORIEREN Nun kann es auch noch sein, dass hinter dem Tabellennamen noch ein Leerzeichen folgt, weil sich dahinter eine WHERE-Klausel befindet. Kein Problem: Dann fügen wir noch das Leerzeichen in das hintere eckige Klammernpaar ein. Bild 15: Beim Suchen und Ersetzen ist Vorsicht geboten. unserem Fall wird der Tabellenname nur innerhalb eines SQL-Ausdrucks verwendet, aber es gibt noch andere Möglichkeiten. Die gleiche Anweisung könnte etwa nur den Namen der Tabelle Kunden enthalten, wenn keine WHERE-Klausel oder andere Einschränkungen benötigt werden: Set rstkunden = db.openrecordset("kunden", dbopendynaset) Dummerweise können wir aber nicht wie bei normalen regulären Ausdrücken die zu durchsuchende Information in Klammern fassen und diese später durch die gewünschten Informationen ersetzen. Damit fällt das Suchen und Ersetzen mit Mustervergleich leider aus und wir müssen Schritt für Schritt die verschiedenen Möglichkeiten durchlaufen. In diesem Fall würden wir also etwa nach Ausdrücken suchen, die ein führendes Leerzeichen, den Ausdruck Kunden und ein Anführungszeichen enthalten und diese Wir müssten also zumindest nach Vorkommen suchen, die auf ein Leerzeichen folgen oder auf ein Anführungszeichen. Das bedeutet, dass wir den Suchen und Ersetzen- Dialog bereits zwei Mal füllen und ausführen müssten. Wenn wir allerdings die Option Mit Mustervergleich aktivieren, sollten wir theoretisch ein paar Schritte. Wir können dann nämlich festlegen, für welche Kombinationen aus führenden und folgenden Zeichen die Zeichenkette Kunden durch tblkunden ersetzt werden soll. Bild 16: Suchen und Ersetzen mit Mustervergleich. Im vorliegenden Fall wollen wir alle Vorkommen finden, denen ein Leerzeichen oder ein Anführungszeichen vorangeht und die mit einem Anführungszeichen abgeschlossen werden. Das gelingt mit dem Ausdruck aus Bild 16: [" ]Kunden["] Bild 17: Suchen und Ersetzen eines Tabellennamens Seite 62

65 VBA UND PROGRAMMIERTECHNIK OBJEKT- UND FELDNAMEN REFAKTORIEREN durch ein Leerzeichen, den Tabellennamen tblkunden und das Anführungszeichen ersetzen (siehe Bild 17). Allerdings können wir durchaus die Suche mit Mustervergleich nutzen, um alle möglichen Konstellationen zu finden. Dazu geben wir dann etwa den folgenden Suchausdruck an: [!a-za-z0-9]kunden[!a-za-z0-9] Suchen und ersetzen per VBA Wenn Sie alle möglichen zu ersetzenden Elemente identifiziert haben, können Sie das Suchen und Ersetzen auch per VBA durchführen. Dazu benötigen Sie zunächst einen Verweis auf die Bibliothek Microsoft Visual Basic for Applications Extensibility 5.3. Diese legen Sie an, indem Sie im VBA-Editor mit dem Menübefehl Extras Verweise den Verweise-Dialog öffnen und hier die angegebene Bibliothek suchen und anhaken (siehe Bild 18). Suchen nach Objektnamen Wenn wir nach einem Objektnamen suchen und diesen ersetzen wollen, müssten wir uns zunächst überlegen, wie das Biotop eines solchen Objektnamens in VBA-Code aussieht Dabei ist vor allem wichtig, dass wir einen Objektnamen wie den Tabellennamen Kunden von den Vorkommen der gleichen Zeichenkette innerhalb anderer Zeichenketten unterscheiden wie etwa in rstkunden. Um es kurz zu machen: Wenn die gesuchte Zeichenkette für den Objektnamen keinen Buchstaben und keine Ziffer als führendes oder folgendes Zeichen aufweist, können wir davon ausgehen, dass es sich um einen Objektnamen handelt. Dabei gehen wir davon aus, dass die Variablen und Konstanten im Code mit entsprechenden Präfixen versehen sind ansonsten wird es kompliziert. Bild 18: Hinzufügen eines Verweises auf die Visual Basic-Erweiterungsbibliothek Nachdem die Anforderung definiert ist, können Sie eine Funktion wie die aus Listing 1 anlegen. Diese durchläuft alle Elemente der Auflistung VBComponents des aktuellen VBA-Projekts, was den Klassenmodulen und Standardmodulen entspricht, in einer For Each-Schleife und speichert das aktuelle Element jeweils in der Laufvariablen objvbcomponent. Die Ausnahme ist das Modul mdlsuchenersetzen, welches die Funktionen zum Suchen und Ersetzen selbst enthält. Dann stellen wir die Objektvariable objcodemodule mit dem Typ CodeModule auf das CodeModule-Objekt von objvbcomponent ein. Hier geschehen nun einige Operationen: Die Lines-Funktion liefert alle Zeilen des Moduls von der mit 1 angegebenen Zeile bis zu der mit objcode- Module.CountOfLines ermittelten Zeile und schreibt diese in die Variable strcode. Mit der InStr-Funktion ermitteln wir die Position des ersten Auftretens des mit dem Parameter strsuchen übergebenen Parameters. Dieser übergeben wir dazu Seite 63

66 VBA UND PROGRAMMIERTECHNIK OBJEKT- UND FELDNAMEN REFAKTORIEREN Public Sub SuchenErsetzen(strSuchen As String, strersetzen As String) Dim objvbcomponent As VBComponent Dim objcodemodule As CodeModule Dim strcode As String Dim lngstart As String Dim lngende As String Dim strvorher As String Dim strnachher As String For Each objvbcomponent In VBE.ActiveVBProject.VBComponents If Not objvbcomponent.name = "mdlsuchenersetzen" Then Set objcodemodule = objvbcomponent.codemodule strcode = objcodemodule.lines(1, objcodemodule.countoflines) lngstart = InStr(1, strcode, strsuchen) Do While Not lngstart = 0 lngende = lngstart + Len(strSuchen) strvorher = Mid(strCode, lngstart - 1, 1) If Not strvorher Like "[a-za-z0-9]" Then strnachher = Mid(strCode, lngstart + Len(strSuchen), 1) If Not strnachher Like "[a-za-z0-9]" Then strcode = Left(strCode, lngstart - 1) & strersetzen & Mid(strCode, lngende) lngende = lngende - Len(strSuchen) + Len(strErsetzen) lngstart = InStr(lngEnde, strcode, strsuchen) Loop objcodemodule.deletelines 1, objcodemodule.countoflines objcodemodule.addfromstring strcode Next objvbcomponent Listing 1: Die Funktion zum Suchen und Ersetzen von Objektnamen im VBA-Code als Startposition den Wert 1, als zu durchsuchenden Text die Variable strcode und als zu suchenden Text die Variable strsuchen. Das Ergebnis dieser Funktion speichern wir in der Variablen lngstart. Sofern wir einen Eintrag finden, erhält lngstart einen Wert ungleich 0. Nur unter dieser Bedingung steigen wir in die folgende Do While-Schleife ein. Diese wird genau dann abgebrochen, wenn lng- Start den Wert 0 aufweist, was bedeutet, dass der Suchbegriff entweder gar nicht auftaucht oder wir innerhalb der Schleife irgendwann alle Vorkommen durchlaufen haben. Steigen wir in die Do While-Schleife ein, ermittelt die Prozedur mit einem weiteren Aufruf der InStr-Funktion die Endposition des mit lngstart eingeleiteten Auftretens des Suchbegriffs. lngstart und lngende enthalten also nun zwei Zahlenwerte, mit denen die Position des Suchbegriffs markiert wird. Nun ermitteln wir für die Variable strvorher das Zeichen, dass sich genau vor dem aktuellen Vorkommen des Suchbegriffs findet. Wir prüfen in einer If...Then- Bedingung, ob dieses Zeichen ein Buchstabe oder eine Zahl ist, was wir durch einen Like-Vergleich mit der Zeichenkette [a-za-z0-9] herausfinden. Seite 64

67 VBA UND PROGRAMMIERTECHNIK OBJEKT- UND FELDNAMEN REFAKTORIEREN Ist dies der Fall, handelt es sich nicht um einen Objektnamen, sondern vermutlich um die Einbettung des gesuchten Textes in eine andere Zeichenkette. In diesem Fall geht es weiter mit der Suche nach dem nächsten Vorkommen siehe weiter unten. Finden wir hier jedoch heraus, dass das vorherige Zeichen kein Buchstabe und keine Zahl ist, untersuchen wir das Zeichen hinter dem gesuchten Ausdruck auf die gleiche Weise. Entspricht das Zeichen einem Buchstaben oder einer Zahl, ist die Bedingung falsch und wir steigen aus dieser aus. Ist jedoch auch das folgende Zeichen kein Buchstabe und keine Zahl, haben wir ein Vorkommen des Suchbegriffs als Objektname gefunden. In diesem Fall fügen wir eine neue Zeichenkette zusammen, die aus dem Inhalt von strcode vor dem aktuell untersuchten Vorkommen des Suchbegriffs, dem zu ersetzenden Begriff und dem Inhalt von strcode nach dem aktuell untersuchten Suchbegriff zusammengestellt wird. Die so erstellte Zeichenkette landet dann wieder in strcode. Damit haben wir nun den Inhalt von strcode verändert. Gegebenenfalls ist der Text aus strersetzen länger als der aus strsuchen. Damit stimmt nun die Endposition des ersetzten Ausdrucks möglicherweise nicht mehr mit der Endposition des gesuchten Ausdrucks überein. Daher passen wir lngende an, indem wir die Länge des gesuchten Ausdrucks subtrahieren und die Länge des ersetzten Ausdrucks addieren. Anschließend ermitteln wir, unabhängig davon, ob der Begriff ersetzt wurde, den Start des nächsten Vorkommens des Suchbegriffs und speichern diese Position in der Variablen lngende. Sollte es kein weiteres Vorkommen mehr geben, erhält lngende den Wert 0, was beim nächsten Durchlaufen des Schleifenkopfes zum Abbruch der Schleife führt. Sind auf diese Weise alle Vorkommen des Suchausdrucks ersetzt worden, können wir den Inhalt von strcode für den bisherigen Inhalt des Moduls einfügen. Dazu löschen wir zunächst den vorherigen Inhalt, und zwar mit der DeleteLines-Methode. Dieser übergeben wir die Nummer der ersten und letzten zu löschenden Zeile und fügen dann mit AddFromString den Inhalt von strcode in das nun leere Modul ein. Damit ist das Ende des Ablaufs für das aktuelle Modul erreicht und wir starten mit dem nächsten Modul der VBComponents-Auflistung. Suchen nach Feldnamen Neben den Objektnamen wollen wir uns auch um die Feldnamen kümmern, die wir geändert haben also beispielsweise die Umbenennung von Kunde-ID in KundeID. Dazu müssen identifizieren, in welcher Form Feldnamen im Code vorkommen. Nach Durchsicht einiger meiner Programmierbeispiele wurde aber schnell klar: Hier gelten die gleichen Regeln wir für die Objektnamen. Wenn die gesuchte Zeichenkette für den Feldnamen nicht von einem führenden oder folgenden Buchstaben oder einer Zahl begleitet wird, handelt es sich um einen Feldnamen. Sie können die Funktion SuchenErsetzen also einfach wie folgt aufrufen: SuchenErsetzen "Kunde-ID", "KundeID" Zusammenfassung und Ausblick Das Refaktorieren von Code nach der Änderungen von Objektnamen oder Feldnamen ist eine Aufgabe, die man mit höchster Präzision durchführen sollte. Die in diesem Beitrag beschriebenen Techniken können Sie dabei zu Hilfe nehmen. Wichtig ist, dass Sie alle Änderungen in den Objekt- und Feldnamen notieren, damit Sie diese nachher auch im VBA-Code reproduzieren können. Seite 65

68 LÖSUNGEN VEREINSVERWALTUNG: FORMULARE, TEIL 1 Vereinsverwaltung: Formulare, Teil 1 In den Beiträgen»Vereinsverwaltung: Von Excel zum Datenmodell«und»Vereinsverwaltung: Migration«haben wir uns um die Erstellung eines Datenmodells und die Migration bestehender Beispieldaten aus einer Excel-Tabelle gekümmert. Für die Bearbeitung der Daten aus den so erstellten und gefüllten Tabellen wollen wir nun eine Benutzeroberfläche programmieren. Der vorliegende Beitrag zeigt, wie wir die Formulare gestalten. Ausgangsposition In den beiden vorhergehenden Teilen dieser Beitragsreihe, nämlich Vereinsverwaltung: Von Excel zum Datenmodell ( und Vereinsverwaltung: Migration ( haben wir exemplarisch gezeigt, wie wir aus den vorliegenden Daten und Anforderungen ein Datenmodell für eine Vereinsverwaltung erstellen. Das Ergebnis finden Sie in Bild 1. Der Kern des Datenmodells ist die Tabelle tblmitglieder, um die herum wir eine Menge weiterer Tabellen angelegt haben. Diese dienen entweder als Lookup-Tabellen für die Fremdschlüsselfelder der Tabelle tblmitglieder oder nehmen andere Daten auf zum Beispiel die Dauer der Vereinszugehörigkeit (tblvereinszugehoerigkeiten), die Altersklassen oder die Zusammenfassung von mehreren Mitgliedern zu einer Familie (tblfamilienmitglieder). Vorbereiten der Feldbeschriftungen Wir wollen ganz einfach mit einem Detailformular für die Tabelle tblmitglieder starten. Bevor wir damit beginnen, wollen wir noch die Feldbeschriftungen für die einzelnen Felder anpassen. Wenn wir nämlich die Felder einer Tabelle nach dem Zuweisen der Tabelle an die Eigenschaft Da- Bild 1: Datenmodell der Vereinsverwaltung Seite 66

69 LÖSUNGEN VEREINSVERWALTUNG: FORMULARE, TEIL 1 tenherkunft des Formulars aus der Feldliste in den Formularentwurf ziehen wollen, trägt Access immer den Feldnamen in das automatisch angelegte Bezeichnungsfeld ein. Das heißt, dass für die Anrede beispielsweise der Feldname AnredeID als Bezeichnungsfeld verwendet wird. Damit wir nicht immer die Bezeichnungsfelder anpassen müssen, stellen wir gleich den gewünschten Feldnamen in der Tabellendefinition ein, und zwar wie in Bild 2. Hier legen wir beispielsweise den Wert Anrede für die Eigenschaft Beschriftung fest. Auf die gleiche Weise gehen wir mit den folgenden Feldern vor: Strasse wird zu Straße RabattklasseID wird zu Rabattklasse MitgliedsnummerHauptverein wird zu Mitgliedsnr. (Hauptverein) wird zu GeschlechtID wird zu Geschlecht Doppelpunkt aktivieren Damit Access beim Hinzufügen der Felder aus der Feldliste in den Detailbereich des Formulars nicht nur die passenden Beschriftungen für die Bezeichnungsfelder einträgt, sondern diese auch noch um einen Bild 2: Anpassen der Feldbeschriftungen Doppelpunkt ergänzt, müssen Sie gegebenenfalls noch einmal Hand anlegen. Öffnen Sie dazu ein neues, leeres Formular und klicken Sie auf die Schaltfläche zum Hinzufügen eines neuen Textfeldes (nur anklicken, noch nicht einfügen!). Das Eigenschaftenblatt zeigt nun auf der Registerseite Format fast ganz unten die Eigenschaft Mit Doppelpunkt an. Diese stellen Sie auf den Wert Ja ein (siehe Bild 3). Bild 3: Aktivieren der Doppelpunkte für Beschriftungen Seite 67

70 LÖSUNGEN VEREINSVERWALTUNG: FORMULARE, TEIL 1 Speichern Sie das Formular nun unter dem Namen Normal. Prüfen Sie dann, ob in den Access-Optionen wie in Bild 4 diese Bezeichnung für die Eigenschaft Formularvorlage festgelegt ist. Wenn Sie nun neue Formulare anlegen, berücksichtigen diese die Einstellungen, die wir hier für den Steuerelementtyp Textfeld vorgenommen haben. Bild 4: Bezeichnungen der Vorlagen für Formulare und Berichte Formular für die Mitglieder anlegen Nun legen wir ein neues, leeres Formular an und stellen seine Eigenschaft Datenherkunft auf die Tabelle tblmitglieder ein. Speichern Sie das Formular vorsichtshalber gleich unter dem Namen frmmitglieddetails. Nun aktivieren Sie die Feldliste mit dem Ribbon-Befehl Entwurf Tools Vorhandene Felder hinzufügen. Markieren Sie alle Einträge des Fensters Feldliste und ziehen Sie diese in den Detailbereich des Formularentwurfs (siehe Bild 5). tblvereinszugehoerigkeiten: Die Vereinszugehörigkeiten sollten auf jeden Fall in einem Unterformular angezeigt werden. tblmitgliedermannschaften/tblmannschaften: Auch die Zugehörigkeiten zu einer oder mehrerer Mannschaften könnte man in diesem Formular dokumentieren und auch bearbeiten. Hier tragen unsere Vorbereitungen nun Früchte: Sowohl die Beschriftungen der Bezeichnungsfelder entsprechen den von uns angegebenen Werten als auch die Doppelpunkte wurden wir gewünscht hinzugefügt. Daten aus weiteren Tabellen hinzufügen Bevor wir die bisher hinzugefügten Steuer elemente in sinnvollen Gruppen anordnen, schauen wir uns noch an, welche Daten aus den anderen Tabellen wir noch benötigen: tblfamilienmitglieder: Wir könnten in einem Unterformular die übrigen Familienmitglieder anzeigen, sofern diese vorhanden sind. Bild 5: Hinzufügen der Felder zum Formular frmmitglieddetails Seite 68

71 LÖSUNGEN VEREINSVERWALTUNG: FORMULARE, TEIL 1 tblanreden, tblrabattklassen, tblgeschlecht: Die Werte dieser Felder werden bereits über Nachschlagefelder dargestellt. Bei den Daten der Tabellen tblfamilienmitglieder, tblvereinszugehoerigkeiten und tblmitgliedermannschaften können wir festlegen, auf welche Art wir die Daten anzeigen wollen. Wir wollen in dieser Beispiellösung ein paar verschiedene Ansätze demonstrieren, aber die Ergonomie soll auch nicht zu kurz kommen. Schauen wir uns die Tabellen im Detail an. Bearbeiten der Vereinszugehörigkeiten Die Vereinszugehörigkeiten sind die einfachste Darstellung. Es handelt sich ja einfach nur um eine 1:n- Beziehung, wobei jedes Mitglied eine oder mehrere Vereinszugehörigkeiten aufweisen kann. Da es vermutlich nicht allzuoft geschieht, dass ein Mitglied den Verein verlässt und dann zurückkehrt, wollen wir diese Daten in einem Listenfeld anzeigen, dessen Einträge wir über einen Doppelklick bearbeiten können beziehungsweise durch entsprechende Schaltflächen neue Vereinszugehörigkeiten anlegen oder löschen können. An dieser Stelle fällt auf, dass wir die Eintritts- und Austrittsdaten noch gar nicht aus der Excel-Tabelle in das neue Datenmodell übernommen haben. Das holen wir nun noch nach. Dabei unterstützt uns die Funktion Eintrittsdatum aus Listing 1. Diese öffnet zunächst ein Recordset auf Basis der Tabelle mit den aus der Excel-Datei importierten Mitgliedsdaten namens Mitglieder. In einer Do While- Schleife durchläuft sie alle Datensätze dieser Tabelle und ermittelt dabei zunächst den Wert des Primärschlüsselfeldes MitgliedID der Tabelle tblmitglieder, unter der das Mitglied importiert wurde. Um den entsprechenden Datensatz zu ermitteln, nutzen wir die DLookup-Funktion und suchen in der Tabelle tblmitglieder nach dem Datensatz, in dessen Feld Mitgliedsnummer der Wert gespeichert ist, den wir in der Excel-Datei in der Spalte Mitgl-Nr finden. Wenn wider Erwarten kein passender Datensatz gefunden wird, erhält lngmitgliedid den Wert 0 und die Anweisungen innerhalb der folgenden If...Then- Bedingung werden nicht ausgeführt. Stattdessen gibt die Routine eine entsprechende Meldung im Direktbereich des VBA-Editors aus. Wurde das Mitglied jedoch gefunden, ermittelt die Prozedur den Wert der Spalte Eintrittsdatum und speichert diesen in der Variablen dateintrittsdatum. Während diese Spalte konsistent mit Datumswerten gefüllt war, müssen wir das eventuell vorhandene Austrittsdatum mühsam aus der Spalte Bemerkungen ermitteln. Dazu schreiben wir den Inhalt dieser Spalte in die Variable straustritt. Es gibt in der Excel-Datei zwei Varianten von Austrittsdaten. Hat sich ein Mitglied abgemeldet, findet sich ein Eintrag wie» abg.«, wenn ein Mitglied verstorben ist, ein Eintrag wie»am gest.«. Wir bearbeiten beide Fälle in jeweils einer If...Then-Bedingung. Die erste prüft, ob straustritt die Zeichenfolge abg. enthält. Falls ja, ersetzen wir mit der Replace-Methode die Zeichenfolge abg. durch eine leere Zeichenfolge und speichern das Ergebnis wieder in straustritt. Anschließend entfernen wir alle führenden und folgenden Leerzeichen mit der Trim-Funktion. Die folgende If...Then-Bedingung prüft mit der IsDate-Funktion, ob straustritt nun ein gültiges Datum enthält. Falls ja, landet dieser Wert nach Konvertierung mit CDate in der Variablen dataustritt, falls nicht, erscheint wieder eine entsprechende Meldung im VBA-Direktbereich. Auf ähnliche Weise kümmern wir uns dann um verstorbene Mitglieder. Hier ersetzen wir allerdings die beiden Zeichenketten gest. und am durch leere Zeichenketten und verarbeiten die verbleibende Zeichenkette wie zuvor beschrieben. Anschließend folgen je nach Inhalt von dataustritt zwei verschiedene SQL-Anfügeabfragen. Enthält die Variable dataustritt einen Wert größer 0, was einem gültigen Datum entspricht, dann führen wir die erste INSERT INTO-Abfrage aus. Diese legt einen neuen Datensatz in der Tabelle tblver- Seite 69

72 LÖSUNGEN VEREINSVERWALTUNG: FORMULARE, TEIL 1 Public Function Eintrittsdatum() Dim db As DAO.Database Dim rst As DAO.Recordset Dim lngmitgliedid As Long Dim dateintritt As Date, dataustritt As Date Dim straustritt As String Set db = CurrentDb Set rst = db.openrecordset("select * FROM Mitglieder", dbopendynaset) Do While Not rst.eof lngmitgliedid = Nz(DLookup("MitgliedID", "tblmitglieder", "Mitgliedsnummer = '" & rst![mitgl-nr] & "'"), 0) If Not lngmitgliedid = 0 Then dateintritt = rst!eintrittsdatum straustritt = Nz(rst!Bemerkung, "") dataustritt = 0 If InStr(1, straustritt, "abg.") > 0 Then straustritt = Replace(strAustritt, "abg.", "") straustritt = Trim(strAustritt) If IsDate(strAustritt) Then dataustritt = CDate(strAustritt) Else Debug.Print "Austrittsdatum aus '" & rst!bemerkung & "' konnte nicht ermittelt werden." If InStr(1, straustritt, "gest.") > 0 Then straustritt = Replace(strAustritt, "gest.", "") straustritt = Replace(strAustritt, "am", "") straustritt = Trim(strAustritt) If IsDate(strAustritt) Then dataustritt = CDate(strAustritt) Else Debug.Print "Austrittsdatum aus '" & rst!bemerkung & "' konnte nicht ermittelt werden." If dataustritt > 0 Then db.execute "INSERT INTO tblvereinszugehoerigkeiten(mitgliedid, Eintrittsdatum, Austrittsdatum) " _ & "VALUES(" & lngmitgliedid & ", " & SQLDatum(datEintritt) & ", " & SQLDatum(datAustritt) & ")", _ dbfailonerror Else db.execute "INSERT INTO tblvereinszugehoerigkeiten(mitgliedid, Eintrittsdatum) VALUES(" _ & lngmitgliedid & ", " & SQLDatum(datEintritt) & ")", dbfailonerror Else Debug.Print "Mitglied mit Mitgliedsnummer '" & rst![mitgl-nr] & "' nicht gefunden." rst.movenext Loop End Function Listing 1: Prozedur zum Ermitteln der Eintritts- und Austrittsdaten Seite 70

73 LÖSUNGEN VEREINSVERWALTUNG: FORMULARE, TEIL 1 einszugehoerigkeiten an und trägt die ermittelten Werte für die Felder MitgliedID, Eintrittsdatum und Austrittsdatum ein. Sollte dataustritt keinen Wert enthalten, soll das Feld Austrittsdatum leer bleiben in diesem Fall verwenden wir die INSERT INTO-Anweisung aus dem Else-Teil der Bedingung und tragen nur die beiden Werte MitgliedID und Eintrittsdatum in die Tabelle ein. Das Ergebnis in der Tabelle tblvereinszugehoerigkeiten sieht dann wie in Bild 6 aus. Listenfeld für die Vereinszugehörigkeiten Damit können wir nun das Listenfeld namens lstvereinszugehoerigkeiten zum Formular frmmitglieder hinzufügen, mit dem wir die Vereinszugehörigkeiten dokumentieren wollen. Diese platzieren wir zunächst an einer beliebigen freien Stelle im Formular, das Finetuning nehmen wir später vor. Außerdem fügen wir zwei Schaltflächen namens cmd- NeueVereinszugehoerigkeit und cmdvereinszugehoerigkeitloeschen hinzu. Damit diese nicht allzu viel Platz wegnehmen, erhalten sie keinen Text, sondern Icons. Diese wiesen wir etwa unter Access 2016 zu, indem wir in der Entwurfsansicht des Formulars die Schaltfläche markieren, im Ribbon den Befehl Entwurf Steuerelemente Bild einfügen Durchsuchen... anklicken und das gewünschte Bild auswählen. Das fällt natürlich leichter, wenn Sie eine Icon-Sammlung besitzen. Bild 6: Vereinszugehörigkeiten nach dem Import Nun fehlen noch die Funktionen. Wir wollen das Bearbeiten der aktuellen Vereinszugehörigkeit über einen Doppelklick auf den entsprechenden Eintrag ermöglichen, also legen wir den Wert [Ereignisprozedur] für die Eigenschaft Beim Doppelklicken des Listenfeldes fest. Für die beiden Schaltflächen erstellen wir jeweils Ereignisprozeduren für das Ereignis Beim Klicken. Wie aber bearbeiten wir die Eintritts- und Austrittsdaten? Zeigen wir den aktuellen Datensatz in einem eigenen Formular an? Nein, wir machen es uns ein wenig leichter und lassen den Benutzer die einzigen beiden gefragten Werte per InputBox eingeben. Die Details hierzu finden Sie im Beitrag Zeiträume per Listenfeld und InputBox ( Nach dem Zuweisen eines Icons stellen Sie noch die Eigenschaft Hintergrundart auf Transparent ein. Außerdem passen Sie die Größe der Schaltflächen noch so an, dass diese etwa der Größe des Icons entspricht. Das Formular sieht im Entwurf dann etwa wie in Bild 7 aus. Bild 7: Listenfeld für die Vereinszugehörigkeiten plus Schaltflächen Seite 71

74 LÖSUNGEN VEREINSVERWALTUNG: FORMULARE, TEIL 1 Bearbeiten der Zuordnung zu den Mannschaften Die Zuordnung von Mitgliedern zu Mannschaften ist eine wichtige Funktion, um einen Überblick darüber zu erhalten, wie viele Spieler man pro Mannschaft zur Verfügung hat und ob man gegebenenfalls eine zweite Mannschaft aufstellen kann. Für unser Beispiel haben wir in der Tabelle tblaltersklassen die Altersklassen definiert, die Mitglieder weisen über die Eigenschaften GeschlechtID und Geburtsdatum die Informationen aus, die für die Zuteilung in eine Altersklasse wichtig sind. Wir bauen uns eine kleine VBA-Prozedur, welche die Tabelle tblmannschaften und tblmitgliedermannschaften füllt. Dabei soll automatisch erstmal je eine Mannschaft je Altersklasse erstellt werden, danach weisen wir über die Tabelle tblmitgliedermannschaften die Mitglieder den Mannschaften zu. Die von den folgenden Ausführungen betroffenen Tabellen finden Sie in Bild 8: tblmitglieder, tblmitgliedermannschaften, tblmannschaften, tblaltersklassen und tblgeschlecht. Hinzu kommt noch die Tabelle tbloptionen, deren Feld Stichtag das Datum für die Berechnung des für die aktuelle Spielzeit benötigten Alters liefert. Initiale Zusammenstellung der Mannschaften Wir haben bereits beim Import der Daten aus der Excel- Tabelle fast alle relevanten Informationen zusammengetragen: das Alter und das Geschlecht der Mitglieder sowie den Status (Aktiv/Passiv), den Stichtag für die Altersklassen sowie die Festlegung von Altersklassen. Daraus wollen wir nun automatisiert die Mannschaften initial zusammenstellen. Hier muss ohnehin nachgearbeitet werden in der einen Altersklasse gibt es vielleicht so viele Bild 8: Tabellen, welche die Zuordnung zu den Mannschaften betreffen Spieler, dass eine zweite Mannschaft angelegt wird, in der anderen so wenige, dass die Spieler in einer höheren Altersklasse antreten müssen. Die Prozedur finden Sie im Modul mdlmigration und in Listing 2. Sie ermittelt zunächst ein Recordset mit allen Einträgen der Tabelle tblmitglieder, deren Feld Aktiv den Wert True enthält, die also nicht als passive Mitglieder deklariert sind. Außerdem ermitteln wir aus der Tabelle tbloptionen den Stichtag, als das Datum, an dem das Alter der Mitglieder in Jahren für die laufende Saison berechnet wird. Die beiden Execute-Methoden rufen zwei DELETE-Aktionsabfragen auf, welche die beiden Tabellen tblmannschaften und tblmitgliedermannschaften leeren. Dann beginnen wir mit dem Durchlaufen der Datensätze des Recordsets rst. Im ersten Schritt ermitteln wir das aktuelle Alter des Mitglieds basierend auf dem zuvor eingelesenen Stichtag und speichern dieses in der Variablen intalter. Dann ermitteln wir mit einer DLookup-Abfrage die Altersklasse gemäß dem Alter und dem Geschlecht des Mitglieds aus der Tabelle tblaltersklassen und tragen den entsprechenden Primärschlüsselwert in die Variable lngaltersklasseid ein. Dann prüfen wir mit einem weiteren DLookup-Aufruf, ob es in der Tabelle tblmannschaf- Seite 72

75 LÖSUNGEN VEREINSVERWALTUNG: FORMULARE, TEIL 1 Public Sub Mannschaften() Dim db As DAO.Database Dim rst As DAO.Recordset Dim lngaltersklasseid As Long, intalter As Integer Dim datstichtag As Date, lngmannschaftid As Long Dim straltersklasse As String Set db = CurrentDb Set rst = db.openrecordset("select * FROM tblmitglieder WHERE Aktiv = True", dbopendynaset) datstichtag = DLookup("Stichtag", "tbloptionen") db.execute "DELETE FROM tblmitgliedermannschaften", dbfailonerror db.execute "DELETE FROM tblmannschaften", dbfailonerror Do While Not rst.eof intalter = DateDiff("yyyy", rst!geburtsdatum, datstichtag) lngaltersklasseid = DLookup("AltersklasseID", "tblaltersklassen", "GeschlechtID = " & rst!geschlechtid _ & " AND Mindestalter <= " & intalter & " AND Hoechstalter >= " & intalter) lngmannschaftid = Nz(DLookup("MannschaftID", "tblmannschaften", "AltersklasseID = " & lngaltersklasseid), 0) If lngmannschaftid = 0 Then straltersklasse = DLookup("Altersklasse", "tblaltersklassen", "AltersklasseID = " & lngaltersklasseid) db.execute "INSERT INTO tblmannschaften(mannschaft, AltersklasseID) VALUES('" & straltersklasse & "', " _ & lngaltersklasseid & ")", dbfailonerror lngmannschaftid = db.execute "INSERT INTO tblmitgliedermannschaften(mitgliedid, MannschaftID) VALUES(" & rst!mitgliedid & ", " _ & lngmannschaftid & ")", dbfailonerror rst.movenext Loop Listing 2: Prozedur zum Zuordnung der Mitglieder zu den Mannschaften ten bereits einen Datensatz für diese Altersklasse gibt. Falls ja, wird der Primärschlüsselwert des entsprechenden Datensatzes der Tabelle tblmannschaften in der Variablen lngmannschaftid gespeichert. Ist lngmannschaftid danach 0, ermitteln wir die Bezeichnung der Altersklasse aus der Tabelle tblaltersklassen und speichern diese in der Variablen straltersklasse. Wir legen Bild 9: Mannschaften und Zuordnungen Seite 73

76 LÖSUNGEN VEREINSVERWALTUNG: FORMULARE, TEIL 1 einen neuen Datensatz in der Tabelle tblmannschaften mit der Bezeichnung aus straltersklasse und dem Primärschlüsselwert dieser Altersklasse an und ermitteln danach den Wert des Primärschlüsselfeldes des neuen Datensatzes der Tabelle tblmannschaften. Dieser landet in der Variablen lngmannschaftid. Mit diesen Informationen können wir nun endlich den Eintrag in der Tabelle tblmitgliedermannschaften vornehmen, der die Zuordnung der Mitglieder zu den Mannschaften enthält. Die beiden Tabellen tblmannschaften und tblmitglieder- Mannschaften enthalten nun die Daten wie in Bild 9. Unterformular für die Mannschaften eines Mitglieds Um die Mannschaften eines Mitglieds im Formular frm- Mitglieddetails anzuzeigen, erstellen wir ein Unterformular namens sfmmitglieddetailsmannschaften, das die Tabelle tblmitgliedermannschaften als Datenherkunft verwendet und daraus das Feld MannschaftID im Detailbereich anzeigt. Die Eigenschaft Standardansicht stellen wir auf Datenblatt ein. Wenn Sie dann das Unterformular schließen und es in den Detailbereich des Entwurfs des Formulars frmmitglieddetails ziehen, sieht das Ergebnis wie in Bild 10 aus. Bild 10: Mannschaften eines Mitglieds In der Formularansicht können Sie dann die aktuell zugewiesene Mannschaft betrachten, ändern oder neue Mannschaften hinzufügen (siehe Bild 11). Zusammenfassung und Ausblick Das war der erste Teil der Beschreibung der Formulare der Vereinsverwaltung. Im folgenden Teil werden wir unter anderem das Formular frmmitglieddetails weiter ausbauen und ein Übersichtsformular zur Auswahl der Mitglieder liefern. Bild 11: Mannschaften eines Mitglieds in der Formularansicht Seite 74

ACCESS. Formulare per VBA referenzieren FORMULARE MIT VBA PROGRAMMIEREN FORMULARE PER VBA REFERENZIEREN BASICS

ACCESS. Formulare per VBA referenzieren FORMULARE MIT VBA PROGRAMMIEREN FORMULARE PER VBA REFERENZIEREN BASICS Formulare per VBA referenzieren Wenn Sie Formulare und Steuerelemente programmieren wollen, müssen Sie wissen, wie Sie diese referenzieren. Nicht immer geschieht dies vom Klassenmodul des Formulars selbst

Mehr

ACCESS. Laufende Summen in Tabellen TABELLEN ENTWERFEN LAUFENDE SUMMEN IN TABELLEN BASICS

ACCESS. Laufende Summen in Tabellen TABELLEN ENTWERFEN LAUFENDE SUMMEN IN TABELLEN BASICS Laufende Summen in Tabellen Immer wieder taucht die Frage auf, wie man in Access die Inhalte von Zahlenfelder mehrerer Datensätze aufsummiert. Unter Excel ist das einfach dort trägt man einfach die Summe

Mehr

ACCESS. Access-Daten nach Excel verknüpfen INTERAKTIV ACCESS-DATEN NACH EXCEL VERKNÜPFEN BASICS

ACCESS. Access-Daten nach Excel verknüpfen INTERAKTIV ACCESS-DATEN NACH EXCEL VERKNÜPFEN BASICS -DATEN NACH EXCEL VERKNÜPFEN Access-Daten nach Excel verknüpfen Wir haben uns bereits in verschiedenen Artikeln angesehen, wie Sie von Access aus auf die Daten einer Excel-Datei zugreifen können ob per

Mehr

Zweitens über eine Abfrage, welche die Tabellen tblartikel und tbllieferanten verknüpft. Auf diese Weise würde das Kombinationsfeld

Zweitens über eine Abfrage, welche die Tabellen tblartikel und tbllieferanten verknüpft. Auf diese Weise würde das Kombinationsfeld Filterkriterien für Formulare, Teil III: Kombinationsfelder In den ersten beiden Teilen dieser Artikelreihe haben Sie erfahren, wie Sie Felder der verschiedenen Datentypen filtern. Nun geht es ans Eingemachte:

Mehr

Verknüpfte Daten kopieren

Verknüpfte Daten kopieren Das Kopieren einfacher Datensätze ist schnell erledigt. Markieren, kopieren, einfügen schon liegt der neue Datensatz vor. Was aber geschieht, wenn an dem zu kopierenden Datensatz noch weitere Daten hängen

Mehr

Formulare für die Dateneingabe Mehrere Formularinstanzen anzeigen

Formulare für die Dateneingabe Mehrere Formularinstanzen anzeigen Die Datensätze einer Tabelle zeigen Sie meist in einer Übersicht wie einem Datenblatt oder einem Listenfeld an. Für die Bearbeitung öffnen Sie den gewünschten Datensatz in einem Detailformular, das die

Mehr

ACCESS. Berechnete Felder in Tabellen TABELLEN ENTWERFEN BERECHNETE FELDER IN TABELLEN BASICS

ACCESS. Berechnete Felder in Tabellen TABELLEN ENTWERFEN BERECHNETE FELDER IN TABELLEN BASICS Berechnete Felder in Tabellen Berechnete Felder in Tabellen sind ein Feature, das mit der Version 2010 von Access hinzugekommen ist. Dabei handelt es sich um die Möglichkeit, die Inhalte der übrigen Felder

Mehr

Werte zu Kombinationsfeldern hinzufügen

Werte zu Kombinationsfeldern hinzufügen Kombinationsfelder bieten meist Daten aus Lookup-Tabellen zur Auswahl an. Das bedeutet, dass Sie damit etwa die Anrede oder den Titel einer Person festlegen können, wobei Anreden und Titel in separaten

Mehr

ACCESS. Kombinationsfeld um Suche erweitern FORMULARE FÜR DIE DATENEINGABE KOMBINATIONSFELD UM SUCHE ERWEITERN BASICS

ACCESS. Kombinationsfeld um Suche erweitern FORMULARE FÜR DIE DATENEINGABE KOMBINATIONSFELD UM SUCHE ERWEITERN BASICS Kombinationsfeld um Suche erweitern Kombinationsfelder sind schon eine praktische Einrichtung: Sie erlauben nicht nur die Auswahl von Einträgen, die schon nach dem Alphabet voreingestellt sind, sondern

Mehr

öffnen den Tabellenverknüpfungs-Manager. Bild 1: Verknüpfte Tabellen in einer Datenbank Bild 2: Fehlgeschlagener Zugriff auf eine verknüpfte Tabelle

öffnen den Tabellenverknüpfungs-Manager. Bild 1: Verknüpfte Tabellen in einer Datenbank Bild 2: Fehlgeschlagener Zugriff auf eine verknüpfte Tabelle Der neue Tabellenverknüpfungs-Manager Still und heimlich wurde der alte Tabellen-Verknüpfungsmanager von Microsoft ausgetauscht. Der Tabellenverknüpfungs-Manager dient dazu, bestehende Verknüpfungen mit

Mehr

ACCESS. Aufgabenplaner LÖSUNGEN AUFGABENPLANER BASICS

ACCESS. Aufgabenplaner LÖSUNGEN AUFGABENPLANER BASICS Aufgabenplaner Im Artikel Berichtsansicht haben Sie eine neue Ansicht für Berichte kennen gelernt, die ganz neue Möglichkeiten eröffnet. Sie können damit hierarchische Daten anzeigen, ohne das Tree- View-Steuerelement

Mehr

ACCESS GRAFIKEN MIT SVG IM UNTERNEHMEN. In diesem Heft: VEREINSVERWALTUNG: FORMULARE, TEIL 1 REFACTORING EINFACHE REIHENFOLGE SEITE 66

ACCESS GRAFIKEN MIT SVG IM UNTERNEHMEN. In diesem Heft: VEREINSVERWALTUNG: FORMULARE, TEIL 1 REFACTORING EINFACHE REIHENFOLGE SEITE 66 Ausgabe 03/2018 ACCESS GRAFIKEN MIT SVG Peppen Sie Ihre Anwendung mit grafischen Darstellungen Ihrer Daten auf und nutzen Sie SVG dazu (ab S. 37) In diesem Heft: VEREINSVERWALTUNG: FORMULARE, TEIL 1 Entwickeln

Mehr

Vorwort...10 Einleitung...12 Lernen Üben Anwenden...12 Inhalt und Aufbau des Buches...13 Inhalt...13 Aufbau Access 2007 (fast) alles ist

Vorwort...10 Einleitung...12 Lernen Üben Anwenden...12 Inhalt und Aufbau des Buches...13 Inhalt...13 Aufbau Access 2007 (fast) alles ist Vorwort...10 Einleitung...12 Lernen Üben Anwenden...12 Inhalt und Aufbau des Buches...13 Inhalt...13 Aufbau...14 1 Access 2007 (fast) alles ist neu...16 Sinnvolle Optionseinstellungen...17 Standarddatenbankordner

Mehr

4 Artikel verwalten. 4.1 Übersichtsformular erstellen

4 Artikel verwalten. 4.1 Übersichtsformular erstellen 4 Artikel verwalten Ich glaube, dass ich es bereits vor einigen Kapiteln erwähnt habe: Faule Access-Entwickler kommen grundsätzlich schneller zum Ziel als andere. So werden wir es uns beim Gestalten der

Mehr

MiniPPS - Systembeschreibung

MiniPPS - Systembeschreibung MiniPPS - Systembeschreibung Hans-Christian Walter Beuth Hochschule für Technik Einführungsbeispiel für Access Version 04.11.2012 Inhalt 1. Access einrichten 2 2. Tabellen 5 3. Abfrage 9 4. Formulare 10

Mehr

Inhalt. Dokument Beschreibung. Bentley Technical Support ProStructures.Net - Zusatzprogrammierung Visual Basic Express PST_Bearbeitung_Dialog

Inhalt. Dokument Beschreibung. Bentley Technical Support ProStructures.Net - Zusatzprogrammierung Visual Basic Express PST_Bearbeitung_Dialog Bentley Technical Support ProStructures.Net - Zusatzprogrammierung Inhalt Dokument Beschreibung... 1 Windows Form einfügen... 2 Steuerelemente einfügen... 2 Steuerelemente Titel und Name... 3 Dialog Laden

Mehr

Formulare. Datenbankanwendung 113

Formulare. Datenbankanwendung 113 Formulare Wenn Sie mit sehr umfangreichen Tabellen arbeiten, werden Sie an der Datenblattansicht von Access nicht lange Ihre Freude haben, sind dort doch immer zu wenig Felder gleichzeitig sichtbar. Um

Mehr

Access Programmierung. Ricardo Hernández García. 1. Ausgabe, November 2013 ACC2013P

Access Programmierung. Ricardo Hernández García. 1. Ausgabe, November 2013 ACC2013P Access 2013 Ricardo Hernández García 1. Ausgabe, November 2013 Programmierung ACC2013P Die VBA-Entwicklungsumgebung 5 Weitere Eingabehilfen Im Menü Bearbeiten finden Sie noch weitere Hilfen, die Ihnen

Mehr

9. Sortieren / Suchen / Filtern in Tabellen

9. Sortieren / Suchen / Filtern in Tabellen 9. Sortieren / Suchen / Filtern in Tabellen Informationen werden in Access nicht nur gespeichert, sondern können auch gezielt abgerufen werden. Dazu stehen Ihnen eine Vielzahl von Werkzeugen zur Verfügung,

Mehr

Sie haben mehrere Möglichkeiten neue Formulare zu erstellen. Achten Sie darauf, dass das Objekt Formulare aktiviert ist: Klicken Sie auf.

Sie haben mehrere Möglichkeiten neue Formulare zu erstellen. Achten Sie darauf, dass das Objekt Formulare aktiviert ist: Klicken Sie auf. 9. FORMULARE Mit Formularen können Sie sehr komfortabel Daten in eine Tabelle eingeben und auch anzeigen lassen, da Sie viele Eingabemöglichkeiten zur Verfügung haben. EIN EINFACHES FORMULAR ERSTELLEN

Mehr

Kennen, können, beherrschen lernen was gebraucht wird

Kennen, können, beherrschen lernen was gebraucht wird Formulare Formulare erstellen Was ist ein Formular? Formulare sind standardisierte Dokumente (z.b. Vordrucke, Formblätter) In Formularen sind spezielle Bereiche dafür vorgesehen, mit Informationen gefüllt

Mehr

17 VBA-Praxisbeispiel

17 VBA-Praxisbeispiel 17 VBA-Praxisbeispiel Nicht nur in unserer Ferienappartementsiedlung Casa Maria, auch im Rest der Toskana hält man sich an das alte Sprichwort»Di giove e di marte non si sposa e non si parte. Donnerstags

Mehr

5 Tabellenanpassung. 5.1 Spaltenbreite und Zeilenhöhe Spaltenbreite verändern

5 Tabellenanpassung. 5.1 Spaltenbreite und Zeilenhöhe Spaltenbreite verändern Um Tabellen effizient bearbeiten können, ist es notwendig, dass Sie die Struktur der Tabelle Ihren Bedürfnissen anpassen. Sie können mit Excel die Elemente einer Tabelle also Zellen, Zeilen und Spalten

Mehr

1 Excel Schulung Andreas Todt

1 Excel Schulung Andreas Todt 1 Excel Schulung Andreas Todt Inhalt 1 Darum geht es hier... 1 2 So wird es gemacht... 1 2.1 Zellen und Blatt schützen... 1 2.2 Arbeitsmappe schützen... 5 2.3 Schritt für Schritt... 6 1 Darum geht es hier

Mehr

Das Kapitel im Überblick

Das Kapitel im Überblick Das Kapitel im Überblick Ihr persönliches Menü Persönliche Registerkarten Damit der Schnellzugriff auch schnell ist So haben Sie wichtige Befehle im Griff Weitere Befehle Befehle auswählen Alle Befehle

Mehr

ACCESS. 1:1-Beziehungen TABELLEN ENTWERFEN 1:1-BEZIEHUNGEN BASICS

ACCESS. 1:1-Beziehungen TABELLEN ENTWERFEN 1:1-BEZIEHUNGEN BASICS 1:1-Beziehungen 1:1-Beziehungen können für eine ganze Reihe von Anwendungzwecken sinnvoll sein. Sie können damit beispielsweise die Liefer- und/oder die Rechnungsanschrift für einen Kundendatensatz in

Mehr

Bilderverwaltung mit Access

Bilderverwaltung mit Access Bilderverwaltung mit Access (1) Tabelle anlegen Der große Nachteil beim Speichern von Bilddateien in Datenbanken ist, dass sie nicht in dem herkömmlichen Format, sondern in einem Windows-internen Format

Mehr

Arrays. Arrays werden verwendet, wenn viele Variablen benötigt werden. Der Vorteil in Arrays liegt darin, dass man nur eine Variable deklarieren muss

Arrays. Arrays werden verwendet, wenn viele Variablen benötigt werden. Der Vorteil in Arrays liegt darin, dass man nur eine Variable deklarieren muss Arrays FTI 41 2005-09-09 Arrays werden verwendet, wenn viele Variablen benötigt werden. Der Vorteil in Arrays liegt darin, dass man nur eine Variable deklarieren muss z.b. Dim Werte(x) As Single. Wobei

Mehr

Handbuch ECDL 2003 Modul 5: Datenbank Formulare anpassen

Handbuch ECDL 2003 Modul 5: Datenbank Formulare anpassen Handbuch ECDL 2003 Modul 5: Datenbank Formulare anpassen Dateiname: ecdl5_04_02_documentation Speicherdatum: 24.11.2004 ECDL 2003 Modul 5 Datenbank - Formulare anpassen Inhaltsverzeichnis 1 EINLEITUNG...

Mehr

TREEVIEW: VERKNKÜPF- TE DATEN, TEIL I

TREEVIEW: VERKNKÜPF- TE DATEN, TEIL I Ausgabe 09/2013 ACCESS DAS ACCESS-MAGAZIN FÜR ALLE, DIE VON 0 AUF 100 WOLLEN In diesem Heft: ANLAGEFELDER UND VBA ERFAHREN SIE, WIE SIE BIL- DER UND ANDERE DATEIEN PER VBA IN ANLAGEFELDER EINFÜGEN UND

Mehr

ACCESS IM UNTERNEHMEN

ACCESS IM UNTERNEHMEN Ausgabe 02/2017 ACCESS STÜCKLISTEN, TEIL II Erweitern Sie die Stücklisten-Lösung um komfortable Kontextmenüs (ab S. 46). In diesem Heft: TICKETSYSTEM Beantworten Sie Kundenanfragen mit vorgefertigten und

Mehr

4 SERIENDRUCK. 4.1 Vorbereitung

4 SERIENDRUCK. 4.1 Vorbereitung MS Word 2010 Aufbau Seriendruck 4 SERIENDRUCK Mit Hilfe eines Seriendrucks haben Sie die Möglichkeit, Dokumente wie zb Briefe, Angebote oder Einladungen an einen größeren Personenkreis zu adressieren,

Mehr

Microsoft Access 2010 Bilder

Microsoft Access 2010 Bilder Microsoft Access 2010 Bilder Hyperlinks... arbeiten ähnlich wie ein Link in einer Webseite. sind ein Verweis auf eine Datei (access2010\material\beispiel\tabledevelop\automat.accdb). können ein Verweis

Mehr

Michael Kolberg. einfach klipp & klar. Microsofft* Press

Michael Kolberg. einfach klipp & klar. Microsofft* Press Michael Kolberg einfach klipp & klar Microsofft* Press III Vorwort 11 Die CD-ROM zum Buch 21 Die AutoPlay-Funktion der CD-ROM 22 Installation der Testfragen und des Ebooks 23 Installation des Computer-Lexikons

Mehr

Schnellübersichten. ECDL Datenbanken mit Windows 10 und Access 2016

Schnellübersichten. ECDL Datenbanken mit Windows 10 und Access 2016 Schnellübersichten ECDL Datenbanken mit Windows 10 und Access 2016 1 Access kennenlernen 2 2 Access verwenden 3 3 Tabellen 4 4 Informationen abfragen 5 5 Formulare 6 6 Outputs 7 1 Access kennenlernen Datenbank

Mehr

Hochschulrechenzentrum Justus-Liebig-Universität Gießen. CorelDRAW X7. Zoomen

Hochschulrechenzentrum Justus-Liebig-Universität Gießen. CorelDRAW X7. Zoomen Hochschulrechenzentrum Justus-Liebig-Universität Gießen CorelDRAW X7 Zoomen Zoomen in CorelDRAW Seite 1 von 11 Inhaltsverzeichnis Einleitung... 2 Die Standardzoomgröße... 2 Zoomgröße ändern... 2 Einstellungen

Mehr

Xnet-Botendienst. Anleitung Botendienstversion

Xnet-Botendienst. Anleitung Botendienstversion Anleitung 24.11.2017 Botendienstversion 1.6.5-1 Inhaltsverzeichnis (Webserver)... 2 Anmeldung... 2 Symbolleiste... 3 Suchfunktion... 4 Tourplanung... 5 Nicht zugeordnete Lieferungen... 6 Einzelne Lieferungen

Mehr

Verknüpfen & Einbetten von Daten

Verknüpfen & Einbetten von Daten Verknüpfen & Einbetten von Daten In Office haben Sie die Möglichkeit, Daten in mehreren Anwendungen gemeinsam zu nutzen. Dies geschieht entweder durch Verknüpfen oder durch Einbetten von Objekten (Diagramme,

Mehr

1. Benennen Sie die Elemente der Access-Benutzeroberfläche: Wann müssen Sie einer Datenbank einen Namen geben?

1. Benennen Sie die Elemente der Access-Benutzeroberfläche: Wann müssen Sie einer Datenbank einen Namen geben? Fragenkatalog Access 2007 501 Benutzeroberfläche 1. Benennen Sie die Elemente der Access-Benutzeroberfläche: 2. Wann müssen Sie einer Datenbank einen Namen geben? o Beim Erstellen o Spätestens beim Schließen

Mehr

Im Original veränderbare Word-Dateien

Im Original veränderbare Word-Dateien Dateneingabe Die Tabelle präsentiert sich bereit zur Dateneingabe. Wir erkennen sofort die von uns erstellten Datenfeldnamen (Buch-ID, Buchtitel, Autor, Verlag usw.), die Access als Spaltenüberschriften

Mehr

MUNIA Bedienungsanleitung

MUNIA Bedienungsanleitung MUNIA Bedienungsanleitung by Daisoft www.daisoft.it 2 Inhaltsverzeichnis I II 2.1 2.2 2.3 2.4 2.5 2.6 III 3.1 3.2 3.3 IV 4.1 4.2 V 5.1 5.2 5.3 Einleitung... 3 Fälligkeitsarten... 5 Fälligkeitsarten...

Mehr

Microsoft Access Abfragen. Verknüpfung von Tabellen

Microsoft Access Abfragen. Verknüpfung von Tabellen Microsoft Access Abfragen. Verknüpfung von Tabellen Welche Bestellungen hat Kunde... aufgegeben? Welche Kunden im Staat USA haben Waren im Wert von mindestens... bestellt? Welche Waren wurden nicht bestellt?

Mehr

Kommentierte Lösung zur Aufgabe Kiosk

Kommentierte Lösung zur Aufgabe Kiosk Kommentierte Lösung zur Aufgabe Kiosk Aufgabenstellung: Ein Betreiber einer Reihe von Kiosken möchte die Tagesumsätze in anschaulicher Form dargestellt haben. Die Umsätze jeder Filiale sollen in eine vorbereitete

Mehr

Anpassung der Views Symbio. das einfache, effiziente, digitale & intellegente QM-BPM- Managementsystem

Anpassung der Views Symbio. das einfache, effiziente, digitale & intellegente QM-BPM- Managementsystem Anpassung der Views Symbio das einfache, effiziente, digitale & intellegente QM-BPM- Managementsystem Stand: Version 1808-2018 2018 Ploetz + Zeller GmbH 2 1 Einführung 3 1.1 Inhalt des Dokumentes 3 1.2

Mehr

4. Mit Fenstern arbeiten

4. Mit Fenstern arbeiten 4. Mit Fenstern arbeiten In dieser Lektion lernen Sie... wie Sie Fenster wieder schließen das Aussehen der Fenster steuern mit mehreren Fenstern gleichzeitig arbeiten Elemente von Dialogfenstern Was Sie

Mehr

AG-VIP App Erste Schritte

AG-VIP App Erste Schritte AG-VIP App Erste Schritte Seite: 1 AG-VIP App Erste Schritte Stand 26.02.2019 17:12:00 Autor Markus Grutzeck Grutzeck-Software GmbH Inhalt 1 Einrichtung... 1 1.1 Verbindungseinstellungen... 1 1.2 Anmeldung...

Mehr

Felder können in zwei Ansichten erscheinen. Entweder wird der Inhalt des Feldes angezeigt (Feldergebnis) oder die so genannte Feldfunktion.

Felder können in zwei Ansichten erscheinen. Entweder wird der Inhalt des Feldes angezeigt (Feldergebnis) oder die so genannte Feldfunktion. 4. FELDER Felder sind besondere Stellen im Text, bei denen eine Feldfunktion im Hintergrund arbeitet und für die Anzeige eines Feldergebnisses sorgt. Felder werden als Platzhalter für verschiedene Informationen

Mehr

sodab V2.50 Vom Klientendatenblatt bis zum Beratungsbogen

sodab V2.50 Vom Klientendatenblatt bis zum Beratungsbogen sodab V2.50 Vom Klientendatenblatt bis zum Beratungsbogen Schritt für Schritt - Anleitung Inhalt Schritt 1: Klienten suchen und bei Bedarf anlegen... 2 Schritt 2: Neuen Klienten anlegen... Schritt : Stammbogen

Mehr

INTELLISENSE IN TEXTFELDERN NUTZEN. Statten Sie Textfelder mit einer automatischen Ergänzung der Eingabe um zuletzt verwendete Werte aus.

INTELLISENSE IN TEXTFELDERN NUTZEN. Statten Sie Textfelder mit einer automatischen Ergänzung der Eingabe um zuletzt verwendete Werte aus. Ausgabe 01/2015 ONLINEBANKING PER WEBSERVICE Statten Sie Ihre Datenbank mit Funktionen aus, um per Webservice BLZ und Kontonummer in IBAN und BIC zu konvertieren, Kontostände und -umsätze abzurufen oder

Mehr

Access [basics] Filterkriterien für Formulare, Teil I. Beispieldatenbank. Basisaufbau. Abfragen für die Datenauswahl Balkendiagramme mit Bordmitteln

Access [basics] Filterkriterien für Formulare, Teil I. Beispieldatenbank. Basisaufbau. Abfragen für die Datenauswahl Balkendiagramme mit Bordmitteln Filterkriterien für Formulare, Teil I Was helfen Artikel-, Kunden-, Bestell- und sonstige Daten, wenn Sie diese zwar schön in einem Formular darstellen, aber diese nicht entsprechend filtern können? In

Mehr

1. Benennen Sie die Elemente der Access-Benutzeroberfläche: Wann müssen Sie einer Datenbank einen Namen geben?

1. Benennen Sie die Elemente der Access-Benutzeroberfläche: Wann müssen Sie einer Datenbank einen Namen geben? Fragenkatalog Access 2003 Daniela Wagner 501 Benutzeroberfläche 1. Benennen Sie die Elemente der Access-Benutzeroberfläche: 2. Wann müssen Sie einer Datenbank einen Namen geben? o Beim Erstellen o Spätestens

Mehr

1. Tabellen-Beziehungen

1. Tabellen-Beziehungen 1. Tabellen-Beziehungen Ein relationales DBMS wie MS ACCESS verteilt, wie in vorherigen Kapiteln gezeigt, die Daten auf verschiedene Tabellen. Trotz dieser sinnvollen und notwendigen Trennung der Daten

Mehr

CorelDRAW 2017 Zoomen

CorelDRAW 2017 Zoomen Hochschulrechenzentrum Justus-Liebig-Universität Gießen CorelDRAW 2017 Zoomen Zoomen in CorelDRAW Seite 1 von 11 Inhaltsverzeichnis Einleitung... 2 Die Standardzoomgröße... 2 Zoomgröße ändern... 2 Einstellungen

Mehr

Xpert - Europäischer ComputerPass. Konrad Stulle, Andrea Weikert, Tanja Bossert. Datenbankanwendung (mit Access 2010)

Xpert - Europäischer ComputerPass. Konrad Stulle, Andrea Weikert, Tanja Bossert. Datenbankanwendung (mit Access 2010) Xpert - Europäischer ComputerPass Konrad Stulle, Andrea Weikert, Tanja Bossert 1. Ausgabe, 1. Aktualisierung, Juli 2012 Datenbankanwendung (mit Access 2010) XP-ACC2010 3 Xpert - Europäischer ComputerPass

Mehr

ECDL Information und Kommunikation Kapitel 7

ECDL Information und Kommunikation Kapitel 7 Kapitel 7 Bearbeiten von E-Mails Outlook Express bietet Ihnen noch weitere Möglichkeiten, als nur das Empfangen und Versenden von Mails. Sie können empfangene Mails direkt beantworten oder an andere Personen

Mehr

Administrative Tätigkeiten

Administrative Tätigkeiten Administrative Tätigkeiten Benutzer verwalten Mit der Benutzerverwaltung sind Sie in der Lage, Zuständigkeiten innerhalb eines Unternehmens gezielt abzubilden und den Zugang zu sensiblen Daten auf wenige

Mehr

Access 2010. für Windows. Andrea Weikert 1. Ausgabe, 4. Aktualisierung, Juni 2012. Grundlagen für Anwender

Access 2010. für Windows. Andrea Weikert 1. Ausgabe, 4. Aktualisierung, Juni 2012. Grundlagen für Anwender Andrea Weikert 1. Ausgabe, 4. Aktualisierung, Juni 2012 Access 2010 für Windows Grundlagen für Anwender ACC2010 2 Access 2010 - Grundlagen für Anwender 2 Mit Datenbanken arbeiten In diesem Kapitel erfahren

Mehr

Der Pivot-Cache in Excel

Der Pivot-Cache in Excel Der Pivot-Cache in Excel Ein Tutorial von office-kompetenz Hildegard Hügemann www.office-kompetenz.de/tutorials Januar 2018 INHALTSVERZEICHNIS Der Kurzeinstieg... 2 Woran liegt es, dass Datumsfelder in

Mehr

3. Synchronisation von Formularen

3. Synchronisation von Formularen 3. 3.1. Datenmodell Die folgenden Überlegungen basieren auf einem einfachen Datenmodell von drei Tabellen, die jeweils in 1:n Beziehungen zueinander stehen. Gemeinden sind Bezirken zugeordnet und Bezirke

Mehr

Erstellung von abhängigen Kombinationsfeldern mit dazugehörigen Unterformular...1

Erstellung von abhängigen Kombinationsfeldern mit dazugehörigen Unterformular...1 Erstellung von abhängigen Kombinationsfeldern mit dazugehörigen Inhaltsverzeichnis Erstellung von abhängigen Kombinationsfeldern mit dazugehörigen...1 Inhaltsverzeichnis...1 Einleitung...1 Vorgaben...1

Mehr

Nutzerleitfaden für den Wissensverbund Innenverwaltung

Nutzerleitfaden für den Wissensverbund Innenverwaltung BILDUNG21 Wissensmanagement Nutzerleitfaden für den Wissensverbund Innenverwaltung Version 0.2 Oliver Altmann September 2008 Führungsakademie Baden-Württemberg Hans-Thoma-Str. 1 76133 Karlsruhe Telefon:

Mehr

Daten verknüpfen und einbetten

Daten verknüpfen und einbetten Daten verknüpfen und einbetten Office 2010 - Aufbau ZID/Dagmar Serb V.02/Okt 2018 VERKNÜPFEN & EINBETTEN VON DATEN... 2 VERKNÜPFEN... 2 Bearbeiten verknüpfter Informationen... 3 EINBETTEN... 3 Bearbeiten

Mehr

Sage 50. Inventur. Impressum. Sage GmbH Emil-von-Behring-Str Frankfurt am Main

Sage 50. Inventur. Impressum. Sage GmbH Emil-von-Behring-Str Frankfurt am Main Sage 50 Inventur Impressum Sage GmbH Emil-von-Behring-Str. 8-14 60439 Frankfurt am Main Copyright 2016 Sage GmbH Die Inhalte und Themen in dieser Unterlage wurden mit sehr großer Sorgfalt ausgewählt, erstellt

Mehr

Excel + VBA. Ergänzungen. Kapitel 1 Einführung in VBA CustomViews in VBA nutzen HARALD NAHRSTEDT. Erstellt am

Excel + VBA. Ergänzungen. Kapitel 1 Einführung in VBA CustomViews in VBA nutzen HARALD NAHRSTEDT. Erstellt am HARALD NAHRSTEDT Excel + VBA Ergänzungen Kapitel 1 Einführung in VBA 1.5.9 CustomViews in VBA nutzen Erstellt am 15.10.2011 Beschreibung In Excel besteht die Möglichkeit, benutzerdefinierten Ansicht zu

Mehr

IM UNTERNEHMEN EINFACH NAVIGIEREN LOOKUP-DATEN: AUS ZWEI MACH EINS

IM UNTERNEHMEN EINFACH NAVIGIEREN LOOKUP-DATEN: AUS ZWEI MACH EINS Ausgabe 04/2014 EINFACH NAVIGIEREN Sie sind während der Bearbeitung einer Bestellung schnell zur nächsten gesprungen, weil ein neuer Kunde anruft? Und dann wollen Sie schnell wieder zurück zur vorherigen

Mehr

1 Einführung Was macht eine Datenbank? Entwickler und Benutzer Warum sollten Sie mit Datenbanken arbeiten?

1 Einführung Was macht eine Datenbank? Entwickler und Benutzer Warum sollten Sie mit Datenbanken arbeiten? Inhalt 1 Einführung 19 1.1 Was macht eine Datenbank? 19 1.2 Entwickler und Benutzer 20 1.3 Warum sollten Sie mit Datenbanken arbeiten? 20 1.4 Wer sollte dieses Buch lesen? 21 1.5 Wie sind relationale Datenbanken

Mehr

4 Schleifen -= Entstanden unter Excel 2003 =-

4 Schleifen -= Entstanden unter Excel 2003 =- Excel KnowHow / Visual Basic (VBA) 4 Schleifen -= Entstanden unter Excel 2003 =- Die erste Schleife habe ich bereits im letzten Kapitel benutzt: Die FOR-Schleife. Schauen wir uns das Beispiel noch einmal

Mehr

Access [basics] Programmieren mit Arrays. Beispieldatenbank. Arrays. Eindimensionale Arrays. VBA-Grundlagen Programmieren mit Arrays

Access [basics] Programmieren mit Arrays. Beispieldatenbank. Arrays. Eindimensionale Arrays. VBA-Grundlagen Programmieren mit Arrays Dass Sie unter Access Daten in Tabellen speichern und gezielt darauf zugreifen können, wissen Sie als Access [basics]-leser schon längst. Aber was, wenn Sie nur ein paar gleichartige Daten zwischenspeichern

Mehr

Microsoft Access Arbeiten mit Tabellen. Anja Aue

Microsoft Access Arbeiten mit Tabellen. Anja Aue Microsoft Access Arbeiten mit Tabellen Anja Aue 10.11.16 Tabellen in der Datenblattansicht Ansicht des Anwenders. Eingabe von neuen Daten. Bearbeiten von vorhandenen Informationen. Microsoft Access Einführung

Mehr

Access [basics] Kunden per Bezeichnung verwalten. Beispieldatenbank. Kundenbezeichnung erwünscht. Separates Bezeichnungsfeld

Access [basics] Kunden per Bezeichnung verwalten. Beispieldatenbank. Kundenbezeichnung erwünscht. Separates Bezeichnungsfeld Beim Umgang mit Kundendaten benötigen Sie oft eine allgemeine Bezeichnung eines Kunden. Diese soll beispielsweise als Überschrift im Formular zur Verwaltung der Stammdaten eines Kunden dargestellt werden

Mehr

Microsoft Access Relationen. Anja Aue

Microsoft Access Relationen. Anja Aue Microsoft Access Relationen Anja Aue 10.11.16 Beziehungen zwischen Tabellen Verknüpfung zwischen zwei Tabellen. Darstellung von Beziehungen zwischen Objektgruppen. Verweis in einer Tabelle auf den Datensatz

Mehr

myfactory.go! - Dokumente

myfactory.go! - Dokumente Tutorial: Wie arbeite ich mit der Dokumentenverwaltung? Immer und überall auf Firmen-Unterlagen zugreifen zu können, kann in manchen Situationen einen Wettbewerbsvorteil darstellen. Dieses Tutorial zeigt

Mehr

NAVIGATOR MODUL 1 BESCHREIBUNG. Mit dem Navigator hat man die Möglichkeit in ASV bestimmte Daten zu selektieren, zu suchen und -Daten zu filtern.

NAVIGATOR MODUL 1 BESCHREIBUNG. Mit dem Navigator hat man die Möglichkeit in ASV bestimmte Daten zu selektieren, zu suchen und -Daten zu filtern. Büro/Verwaltung Willibald Heßlinger Multiplikator für das Schulverwaltungsprogramm asv MODUL 05 NAVIGATOR 1 BESCHREIBUNG Mit dem Navigator hat man die Möglichkeit in ASV bestimmte Daten zu selektieren,

Mehr

7 DOKUMENTEE FORMATIEREN

7 DOKUMENTEE FORMATIEREN 7 DOKUMENTEE FORMATIEREN Sie können grundlegende Einstellungen von Dokumenten wie die Seitenausrichtung oder die Papiergröße bearbeiten und so Ihren Bedürfnissen anpassen. Die Befehle dazu finden Sie unter

Mehr

Predata AG winmedio.net Version Seite 1 von 8

Predata AG winmedio.net Version Seite 1 von 8 quicklisten erstellen, ansehen, bearbeiten, gruppieren, löschen Was ist eine quickliste Eine quickliste ist eine Liste mit Medien, die nach verschiedenen Kriterien frei zusammengestellt werden kann. Diese

Mehr

Allgemeine Hinweise zum Erstellen einer einfachen Datenbank

Allgemeine Hinweise zum Erstellen einer einfachen Datenbank Allgemeine Hinweise zum Erstellen einer einfachen Datenbank 1. Tabellen Öffnen Sie das Programm MS-ACCESS durch Doppelklick auf das Symbol oder durch Auswahl des Programms in der Taskleiste Start Programme

Mehr

Excel Tipps & Tricks Umgang mit umfangreichen Tabellen

Excel Tipps & Tricks Umgang mit umfangreichen Tabellen 3 Umgang mit umfangreichen Tabellen Das Bewegen in großen Tabellen in Excel kann mit der Maus manchmal etwas umständlich sein. Deshalb ist es nützlich, ein paar Tastenkombinationen zum Bewegen und zum

Mehr

Bedienungsanleitung Vereinsbuchhaltung. Bedienungsanleitung Appell. Inhalt

Bedienungsanleitung Vereinsbuchhaltung. Bedienungsanleitung Appell. Inhalt Bedienungsanleitung Appell Dieses Programm ist lizenzfrei verwendbar. Das Programm ist mit Excel 2016 erstellt worden und enthält VBA Programmierungen, also Typ.xlsm, deshalb werden Sie beim Öffnen darauf

Mehr

SCHULSPEZIFISCHEN ROLLENRECHTE

SCHULSPEZIFISCHEN ROLLENRECHTE Bei BASISDATEN > ADMINISTRATION organisieren Sie, wer SOKRATES an Ihrer Schule mit welchen Rechten nutzen kann. Außerdem können unter ADMINISTRATION mit SOKRATES intern Texte an andere Schulen geschickt

Mehr

Im Original veränderbare Word-Dateien

Im Original veränderbare Word-Dateien Die Benutzeroberfläche von Access Menüband Das am oberen Bildschirmrand befindliche Menüband beinhaltet die meisten Befehle von Access. Im Menüband sind Schnellzugriffsleiste und Titelleiste integriert.

Mehr

Auswerten der Startkontrolle mit OLEinzel

Auswerten der Startkontrolle mit OLEinzel Auswerten der Startkontrolle mit OLEinzel Diese Anleitung soll dazu dienen, die Check-Stationen von der Startkontrolle richtig auszuwerten. Dank diesen Stationen können zwei wichtige Arbeiten erledigt

Mehr

Kurzanleitung. Zitiertil-Creator. Dokumentvorlagen Dokumente Formatvorlagen Format Zeichen Format Absatz

Kurzanleitung. Zitiertil-Creator. Dokumentvorlagen Dokumente Formatvorlagen Format Zeichen Format Absatz Dokumentvorlagen Dokumente Formatvorlagen Format Zeichen Format Absatz Datei Neu... Datei öffnen Datei schließen Beenden Suchen Suchen & Ersetzen Verknüpfungen Optionen Einfügen Inhalte einfügen Format

Mehr

Inhaltsverzeichnisse

Inhaltsverzeichnisse Inhaltsverzeichnisse Überschriften zuweisen Formatieren Sie die Überschriften mit Hilfe der integrierten Formatvorlagen als Überschrift. Klicken Sie dazu in die jeweilige Überschrift und dann auf der Registerkarte

Mehr

Transport IT.S FAIR. Handbuch. Dauner Str.12, D Mönchengladbach, Hotline: 0900/ (1,30 /Min)

Transport IT.S FAIR. Handbuch. Dauner Str.12, D Mönchengladbach, Hotline: 0900/ (1,30 /Min) IT.S FAIR Handbuch Dauner Str.12, D-41236 Mönchengladbach, Hotline: 0900/1 296 607 (1,30 /Min) 1. Inhalt 1. Inhalt... 2 2. Wie lege ich einen Kunden oder Fahrer an?... 3 3. Wie erstelle ich eine Aktion

Mehr

3 Die Tabellen von PROJEKT. Tabellenerstellung in der Entwurfsansicht Dateneingabe direkt in die Tabelle Tabellen mit dem Tabellen-Assistenten anlegen

3 Die Tabellen von PROJEKT. Tabellenerstellung in der Entwurfsansicht Dateneingabe direkt in die Tabelle Tabellen mit dem Tabellen-Assistenten anlegen PROJEKT 3 Die Tabellen von Access 2002 TRAINING Access 2002 ISBN 3-8272-6192-9 Lektion 1 Lektion 2 Lektion 3 Lektion 4 Lektion 5 Lektion 6 Lektion 7 Lektion 8 Lektion 9 Lektion 10 Lektion 11 Lektion 12

Mehr

Empfänger. Alle Empfänger, die sich für Ihre(n) Newsletter angemeldet haben, werden in der Empfängerverwaltung erfasst.

Empfänger. Alle Empfänger, die sich für Ihre(n) Newsletter angemeldet haben, werden in der Empfängerverwaltung erfasst. Empfänger Alle Empfänger, die sich für Ihre(n) Newsletter angemeldet haben, werden in der Empfängerverwaltung erfasst. Für eine größere Flexibilität in der Handhabung der Empfänger erfolgt der Versand

Mehr

Axel Tüting Version 1.1 zeit für das wesentliche TUTORIAL: SCHNELBAUSTEINE

Axel Tüting Version 1.1 zeit für das wesentliche TUTORIAL: SCHNELBAUSTEINE 2014 www.time4mambo.de Axel Tüting Version 1.1 zeit für das wesentliche TUTORIAL: SCHNELBAUSTEINE Inhalt Schnellbausteine... 3 Eigene Schnellbausteine... 4 Die verschiedenen Steuerelemente... 8 www.time4mambo.de

Mehr

Inhaltsverzeichnis/Impressum

Inhaltsverzeichnis/Impressum Inhaltsverzeichnis/Impressum Formulare und Steuerelemente Kombinationsfeld mit Bearbeitungsfunktion 2 Steuerelemente an Datenblatt anpassen 11 TreeView: Neue Elemente anlegen 17 Sicherheit Frontend/Backend

Mehr

Stapelbearbeitung. Urheberrechtlich geschützt Stollfuß Medien GmbH & Co. KG

Stapelbearbeitung. Urheberrechtlich geschützt Stollfuß Medien GmbH & Co. KG Urheberrechtlich geschützt Stollfuß Medien GmbH & Co. KG Inhaltsverzeichnis Aufruf der... 3 Praxisoptionen... 3 Ausgabeoptionen... 4 Auswahl der Auswertungen... 5 Vorlagen für die Stapelauswahl... 7 Nachbearbeitung

Mehr

Access Grundlagen für Anwender. Sabine Spieß, Andrea Weikert. 1. Ausgabe, 1. Aktualisierung, September Trainermedienpaket ACC2010_TMP

Access Grundlagen für Anwender. Sabine Spieß, Andrea Weikert. 1. Ausgabe, 1. Aktualisierung, September Trainermedienpaket ACC2010_TMP Sabine Spieß, Andrea Weikert Access 2010 Grundlagen für Anwender 1. Ausgabe, 1. Aktualisierung, September 2012 Trainermedienpaket ACC2010_TMP 3 Access 2010 - Grundlagen für Anwender 3 Daten in Formularen

Mehr

Microsoft Access Abfragen: Informationen anzeigen und sortieren

Microsoft Access Abfragen: Informationen anzeigen und sortieren Microsoft Access Abfragen: Informationen anzeigen und sortieren Alle Kunden Die Namen der Mitarbeiter und deren E-Mail-Adresse Bestellungen, nach dem Datum sortiert Anja Aue 16.11.16 Abfragen Zusammenstellung

Mehr

Access [basics] Lookup-Daten per Formular verwalten. Beispieldatenbank. Ausgangsformular. Formular zum Bearbeiten von Lookup- Daten

Access [basics] Lookup-Daten per Formular verwalten. Beispieldatenbank. Ausgangsformular. Formular zum Bearbeiten von Lookup- Daten Lookup-Daten per Formular verwalten Wenn Sie Daten wie Anreden, Funktionen, Abteilungen et cetera in Lookup-Tabellen speichern und diese Daten in weiteren Tabellen per Nachschlagefeld verfügbar machen,

Mehr

ACCESS IM UNTERNEHMEN GOOGLE MAPS. In diesem Heft: EINFACH FILTERN S MIT VORLAGE DYNAMISCHER ADRESSBLOCK SEITE 14 SEITE 53 SEITE 24

ACCESS IM UNTERNEHMEN GOOGLE MAPS. In diesem Heft: EINFACH FILTERN  S MIT VORLAGE DYNAMISCHER ADRESSBLOCK SEITE 14 SEITE 53 SEITE 24 Ausgabe 03/2017 GOOGLE MAPS Zeigen Sie Adressdaten mit Google Maps direkt im Access-Formular an (ab S. 37). In diesem Heft: EINFACH FILTERN Erweitern Sie Formulare um eine einfache Funktion zum Speichern

Mehr

Analysen mit Pivot-Tabellen durchführen

Analysen mit Pivot-Tabellen durchführen Analysen mit Pivot-Tabellen durchführen Pivot-Tabellen auch PivotTables genannt erlauben es, die Daten in einer Excel-Tabelle in Form einer zusammenfassenden Kreuztabelle zu analysieren. Beispielsweise

Mehr

Bei einer Neuerfassung von Daten sind die Felder, welche zwingend erfasst werden müssen, mit einem roten Band versehen.

Bei einer Neuerfassung von Daten sind die Felder, welche zwingend erfasst werden müssen, mit einem roten Band versehen. Updates sage200 Version 2013 und 2014 NEUERFASSUNG Bei einer Neuerfassung von Daten sind die Felder, welche zwingend erfasst werden müssen, mit einem roten Band versehen. DATUMFELDER Durch Drücken der

Mehr

Das Grundlagenbuch zu FileMaker Pro 7- Datenbanken erfolgreich anlegen und verwalten

Das Grundlagenbuch zu FileMaker Pro 7- Datenbanken erfolgreich anlegen und verwalten Das Grundlagenbuch zu FileMaker Pro 7- Datenbanken erfolgreich anlegen und verwalten SMART BOOKS Inhaltsverzeichnis..««... Vorwort 13 Kapitel 1 - Einführung 17 Crashkurs: FileMaker Pro 7 anwenden 19 Eine

Mehr

ZUGRIFFSBERECHTIGUNG IM TERMINBUCH

ZUGRIFFSBERECHTIGUNG IM TERMINBUCH ZUGRIFFSBERECHTIGUNG IM TERMINBUCH Pionier der Zahnarzt-Software. Seit 986. Einleitung DAMPSOFT GmbH Seite 2/3 Sehr geehrtes Praxis-Team, mit Zugriffsberechtigungen auf die Terminbücher in Ihrem DS-Win-Termin

Mehr