08.03.2013 Aufrufe

Access 2010 Grundlagen für Entwickler - Addison-Wesley

Access 2010 Grundlagen für Entwickler - Addison-Wesley

Access 2010 Grundlagen für Entwickler - Addison-Wesley

MEHR ANZEIGEN
WENIGER ANZEIGEN

Erfolgreiche ePaper selbst erstellen

Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.

3<br />

Formulare<br />

Formulare sind das wichtigste Element der Benutzer oberfläche von <strong>Access</strong>-An wen dungen.<br />

Sie sorgen da<strong>für</strong>, dass der Benutzer bei der Da ten eingabe nicht mit ellen langen<br />

Tabellen oder Ab fragen mit Tausenden von Da ten sät zen han tieren muss, sondern die<br />

Daten übersichtlich dar gestellt bekommt – je nach Anforderung als Liste oder Detailansicht.<br />

Mit Kombinationsfeldern, Listenfeldern und Un ter for mu la ren lassen sich auch die<br />

Daten ver knüpf ter Tabellen problemlos darstellen.<br />

Der große Vorteil von <strong>Access</strong>-Formularen gegen über denen von Visual Basic oder den<br />

.NET-Pro grammiersprachen liegt in den speziellen Funk tio nen <strong>für</strong> die Verwendung mit<br />

Tabellen und Ab fragen als Datensatzquelle. Diese Tech ni ken sind mit da<strong>für</strong> verantwort-


Kapitel 3 Formulare<br />

lich, dass <strong>Access</strong> heute das am weitesten verbreitete Datenbank-Ma na ge ment system<br />

<strong>für</strong> Windows-Rechner ist. Abgerundet wird dies durch die Möglichkeit, For mulare und<br />

die enthaltenen Steuerelemente zu programmieren und dabei die zahlreichen Er eignisse<br />

zu nutzen.<br />

In diesem Kapitel erfahren Sie zunächst, welche Neue rungen <strong>Access</strong> <strong>2010</strong> in Zu sam menhang<br />

mit For mularen mitbringt, und erhalten eine kleine Einführung in Formulare. Dann<br />

geht es ans Eingemachte: Sie lernen, wie Sie Daten auf Basis einzelner oder verknüpfter<br />

Tabellen in Abfragen darstellen können und wie Sie VBA zur Realisierung wichtiger<br />

Funktionen wie etwa zur Suche von Da ten sät zen verwenden.<br />

Das folgende Kapitel geht dann genauer auf die verschiedenen Steuerelemente ein, die<br />

Sie in Formularen einsetzen können. Dort geht es nicht nur um die Stan dard steuer elemen<br />

te von <strong>Access</strong>, sondern auch um Steuerelemente wie das ListView-, TreeView- oder<br />

ImageList-Steuerelement.<br />

Da alle in diesem Kapitel beschriebenen Funktionen mehr oder weniger den Einsatz<br />

von VBA und Ereigniseigenschaften von Formularen und Steuerelementen benötigen,<br />

finden Sie zunächst eine Beschreibung der wichtigsten Ereignisse und ihre Abfolge <strong>für</strong><br />

das Formular selbst und einige Steuerelemente.<br />

164<br />

BEISPIELDATENBANK<br />

Die Beispieldatei zu diesem Kapitel finden Sie unter dem Namen Formulare.accdb<br />

auf www.acciu.de/aeb<strong>2010</strong>.<br />

3.1 Formulare in <strong>Access</strong> <strong>2010</strong><br />

Microsoft hat sich eine Menge Features einfallen lassen, die das Erstellen von For mula<br />

ren <strong>für</strong> Einsteiger vereinfachen. Es gibt aber auch Neuerungen, die dem alteingesessenen<br />

<strong>Access</strong>-<strong>Entwickler</strong> das Leben erleichtern. Auch hier haben wir die Neuerungen<br />

seit <strong>Access</strong> 2007 mit berücksichtigt, damit Umsteiger von <strong>Access</strong> 2003 und älter sich<br />

direkt zurechtfinden. Beim Gang durch das Entwerfen von For mu la ren lernen Sie alles<br />

Neue kennen.<br />

3.1.1 Anlegen eines Formulars<br />

Das schnelle Anlegen von Formularen erfolgt in <strong>Access</strong> <strong>2010</strong> mit dem passenden Ribbon-Befehl<br />

auf Basis des aktuell markierten Tabellen- oder Abfrage-Objekts. Die Ribbon-Einträge<br />

befinden sich sämtlich im Bereich Erstellen|Formulare. Die einfachste<br />

Variante mit der Bezeichnung Formular erstellt automatisch ein einfaches Formular


Formulare in <strong>Access</strong> <strong>2010</strong><br />

in der Ansicht Einzelnes Formular. Dabei berücksichtigt diese Funktion eventuelle Unter<br />

da ten blätter der zugrunde liegenden Tabelle/Abfrage und legt da<strong>für</strong> passende<br />

Unterformulare auf Basis der verknüpften Ta bel len an.<br />

Mit dem Eintrag Erstellen|Formulare|Geteiltes Formular legen Sie ein geteiltes Formular<br />

an, das gleichzeitig eine Liste der Datensätze der zugrunde liegenden Tabelle oder Abfra<br />

ge und die Detailansicht des aktuell ausgewählten Datensatzes anzeigt. Mehr zu geteilten<br />

Formularen erfahren Sie weiter unten in »Geteilte Formulare« ab Seite 172.<br />

Weitere Ribbon-Schaltflächen verheißen das schnelle Anlegen der gängigen Ansichten.<br />

Wer's manuell mag, der klickt direkt auf den Ribbon-Eintrag Erstellen|Formulare|Formu<br />

lar entwurf und legt selbst Hand an.<br />

Gebundene und ungebundene Formulare<br />

Üblicherweise verwendet man in <strong>Access</strong> Formulare, um Daten aus den zugrunde liegenden<br />

Tabellen anzuzeigen, und das bedeutet, dass man das Formular an eine Tabelle<br />

oder Abfrage bindet. Dazu stellt man die Eigenschaft Datensatzquelle entweder auf den<br />

Namen einer Tabelle, den Namen einer Abfrage oder einen passenden SQL-Ausdruck<br />

ein.<br />

Natürlich kann es auch ungebundene Formulare geben – etwa, um den Benutzer mit<br />

einem Startdialog einige nützliche Hinweise mit auf den Weg zu geben, dem Benutzer<br />

statt eines Ribbons ein Übersichtsformular zum Steuern der Anwendung zu bieten oder<br />

Informationen abzufragen – beispielsweise Such- oder Filterkriterien <strong>für</strong> die Anzeige<br />

von Daten in Formularen oder Berichten. Solche Formulare unterscheiden sich lediglich<br />

durch das Leerlassen der Eigenschaft Datensatzquelle von gebundenen Formularen.<br />

Gebundene Felder über die Feldliste anlegen<br />

Wenn Sie sich entschieden haben, ein Formular von Grund auf selbst anzulegen (also<br />

ohne eine der Funktionen zum automatischen Einfügen etwa von Feldern oder der<br />

Daten satzquelle), gehen Sie folgendermaßen vor: Nach dem Auswählen der Daten satzquelle<br />

können Sie die darin enthaltenen Felder auf dem Formular platzieren. Wie Sie<br />

diese anordnen, hängt im Wesentlichen davon ab, wie Sie die Daten darstellen möchten<br />

und welchen Zweck das Formular hat. Grundsätzlich geschieht das aber immer gleich:<br />

» Aktivieren Sie die Entwurfs- oder die Layoutansicht des Formulars.<br />

» Klicken Sie auf den Ribbon-Eintrag Entwurf|Tools|Vorhandene Felder hinzufügen, um<br />

die Feldliste anzuzeigen.<br />

» Ziehen Sie die benötigten Felder aus der Feldliste (auch per Mehrfachauswahl) auf<br />

den gewünschten Bereich des Formulars. Welche Bereiche es gibt und wozu diese<br />

dienen, erfahren Sie weiter unten.<br />

165


Kapitel 3 Formulare<br />

Die Feldliste zur Auswahl der anzuzeigenden Felder hat sich im Vergleich zu älteren<br />

<strong>Access</strong>-Versionen stark verändert. Zwar zeigt sie noch die Felder der ausgewählten Datensatzquelle<br />

an, aber anders als gewohnt (siehe Abbildung 3.1): Die eigentliche Feldliste<br />

im oberen Bereich enthält nicht nur die Felder der Datensatzquelle, sondern auch<br />

noch die Angabe der passenden Tabelle. Im mittleren Bereich zeigt <strong>Access</strong> direkt mit<br />

der Datensatzquelle verknüpfte Tabellen und ihre Felder an (falls Sie im Beziehungen-<br />

Fenster entsprechende Beziehungen angelegt oder den Nachschlageassistenten damit<br />

betraut haben) und im unteren Bereich die übrigen Tabellen. Falls die Feldliste nur die<br />

Felder der aktuellen Datensatzquelle anzeigt, können Sie die Ansicht mit einem Klick<br />

auf die Schaltfläche Alle Tabellen anzeigen erweitern.<br />

Abbildung 3.1: Auswahl der im Formular anzuzeigenden Felder<br />

Bei Feldern aus verknüpften Tabellen fügt <strong>Access</strong> diese Tabellen einfach der Da ten satzquelle<br />

hinzu, indem es den passenden SQL-Ausdruck in der Eigenschaft Datensatzquelle<br />

anpasst, bei nicht verknüpften Tabellen ist noch ein wenig Handarbeit notwendig: Sie<br />

müssen dann in einem Dialog angeben, über welches Feld ein Bezug hergestellt werden<br />

soll (siehe Abbildung 3.2).<br />

Prinzipiell stellen Sie mit diesem Dialog nachträglich eine Verknüpfung her (wenn auch<br />

nur auf Datensatzquellen-Ebene), die in der zugrunde liegenden Abfrage gespeichert<br />

wird. Das ist ein interessantes Werkzeug, wenn man mal eben ein Feld nachreichen<br />

möchte, das man beim Anlegen der Datensatzquelle vergessen hat. Die Frage ist, ob<br />

dies bei Tabellen, die noch nicht verknüpft sind, sinnvoll ist – und gerade dazu ist der<br />

Dialog wohl vorgesehen.<br />

166


Formulare in <strong>Access</strong> <strong>2010</strong><br />

Das Hinzufügen von Feldern aus bereits verknüpften Tabellen hingegen kann eine echte<br />

Erleichterung sein: Wie oft passiert es, dass man Felder etwa aus Lookup-Tabellen wie<br />

einer Anrede-Tabelle beim Zusammenstellen der Datensatzquelle außer Acht lässt und<br />

diese sonst nachträglich anpassen müsste. Das geht nun schon wesentlich schneller,<br />

indem man einfach das passende Feld aus dem mittleren Bereich der Feldliste in den<br />

Entwurf zieht.<br />

Abbildung 3.2: Nachträgliches Hinzufügen von Feldern aus nicht verknüpften Tabellen<br />

Felder über Steuerelemente anlegen<br />

Der zweite Weg, ein gebundenes Feld anzulegen, ist ein wenig aufwändiger. Er ist sinnvoll,<br />

wenn Sie kein Textfeld <strong>für</strong> die Anzeige des Feldinhaltes benötigen, sondern auf ein<br />

an deres Steuerelement zurückgreifen möchten. Bei Kombinations- und Listenfeldern<br />

kann man dem vorbeugen, indem man die Nachschlage-Eigenschaften des zugrunde<br />

liegenden Feldes entsprechend vorbereitet (mehr dazu unter »Beziehungen herstellen«<br />

ab Seite 54 und folgende).<br />

Auch <strong>für</strong> Boolean-Felder legt <strong>Access</strong> au to ma tisch ein Kontrollkästchen an. Vielleicht<br />

möchten Sie aber auch einmal eine Op tions grup pe anlegen, um die Werte eines<br />

Zahlenfeldes abzubilden, oder Sie benötigen ein Kom bi na tions- oder Listenfeld, ohne<br />

dass Sie die Nachschlage-Eigenschaften des Ta bellenfelds ent sprechend eingestellt<br />

haben. Dann klicken Sie einfach auf die passende Schalt fläche im Ribbon-Bereich<br />

Entwurf|Steuerelemente und ziehen dann das Feld, dessen Inhalt das Steuerelement<br />

anzeigen soll, in den Formularentwurf. Die Eigenschaft Steuerelementinhalt wird dann<br />

gleich auf das entsprechende Feld eingestellt.<br />

Formularbereiche<br />

Der meistverwendete Formularbereich ist vermutlich der Detailbereich. Er enthält in<br />

der Regel die an die Datensatzquelle gebundenen Steuerelemente – eben jene Felder,<br />

die Sie aus der Feldliste auf das Formular ziehen können. Die hier enthaltenen Felder<br />

zeigt <strong>Access</strong> in allen Ansichten an – in manchen sogar nur diese Felder und keine<br />

Steuerelemente der anderen Bereiche.<br />

167


Kapitel 3 Formulare<br />

Formulare können außerdem einen Formularkopf und einen Formularfuß aufweisen.<br />

Der Formularkopf ist beispielsweise gut <strong>für</strong> die Anzeige von Überschriften, berechneten<br />

Werten wie dem Systemdatum oder Steuerelementen wie Schaltflächen geeignet.<br />

Der Formularfuß enthält oft Ergebnisse von Berechnungen wie etwa der Anzahl der<br />

im Detailbereich angezeigten Datensätze oder einer Rechnungssumme. Dort können<br />

Sie auch – wie in vielen Windows-Anwendungen – die Schaltflächen zum Steuern des<br />

Formulars unterbringen. Viele Benutzer sind es gewohnt, dort Schaltflächen mit Beschrif<br />

tungen wie OK oder Abbrechen zu finden.<br />

Die Bereiche Seitenkopf und Seitenfuß sind nur interessant, wenn Sie Formulare ausdrucken<br />

wollen. Für das Ausdrucken von Daten sind unter <strong>Access</strong> aber Berichte verantwortlich;<br />

sie bieten auch wesentlich bessere Möglichkeiten zur optimalen Darstellung<br />

der Daten. Daher behandelt dieses Buch diese Bereiche an dieser Stelle nicht weiter.<br />

Das Ein- und Ausblenden der verschiedenen Formularbereiche erledigen Sie mit den<br />

Kontextmenüeinträgen Seitenkopf/­fuß und Formularkopf/­fuß.<br />

3.1.2 Formularansichten<br />

Bei einem Formular, das der Eingabe von Daten dient, werden Sie vermutlich nur jeweils<br />

einen einzigen Datensatz anzeigen. Sie stellen dann die Eigenschaft Standardansicht auf<br />

Einzelnes Formular ein.<br />

Das ist nicht nur die Basis zur Eingabe, sondern auch zur Anzeige von Daten im Detail<br />

sowie zum Bearbeiten von Datensätzen. Generell können Sie die Ansicht eines For mulars<br />

im Ribbon unter Start|Ansichten oder über das Kontextmenü der Titelleiste eines<br />

For mulars oder des Formularhintergrunds einstellen.<br />

Entwurfsansicht<br />

In der Entwurfsansicht eines Formulars passen Sie das Formular an: Sie fügen Steuerele<br />

mente hinzu, stellen Eigenschaften wie Höhe, Breite oder Farben ein und ordnen die<br />

Ele mente wie gewünscht an. Es würde den Rahmen dieses Kapitels sprengen, wenn<br />

es alle Einzelheiten in Zusammenhang mit der Entwurfsansicht beschreiben sollte.<br />

Deshalb beschränkt es sich auf die Neuheiten seit <strong>Access</strong> 2007 (<strong>für</strong> alle, die von<br />

<strong>Access</strong> 2003 oder älter zu <strong>Access</strong> <strong>2010</strong> wechseln), die sich vor allem auf die verbesserte<br />

Ausrichtung von Steuerelementen beziehen (siehe »Hilfreiche Funktionen <strong>für</strong> den<br />

Formularentwurf« ab Seite 173).<br />

Wenn Ihnen die <strong>Grundlagen</strong> beim Erstellen von Formularen mit der Entwurfsansicht<br />

nicht geläufig sind, liefert die Onlinehilfe von <strong>Access</strong> allerdings gutes Material: Geben<br />

Sie dort als Suchbegriff einfach »Formulare« ein und Sie erhalten eine umfangreiche<br />

Liste mit allen möglichen Themen rund um die Erstellung von Formularen.<br />

168


Layoutansicht<br />

Formulare in <strong>Access</strong> <strong>2010</strong><br />

Die Layoutansicht ist ein neues Feature, das mit <strong>Access</strong> 2007 eingeführt wurde. Es stellt<br />

eine Art Zwischenstufe zwischen der Entwurfsansicht und der Formularansicht dar. In<br />

dieser Ansicht zeigt ein Formular die zugrunde liegenden Daten an und lässt sich gleichzeitig<br />

wie in der Entwurfsansicht bearbeiten. Der wichtigste Vorteil der Layoutansicht<br />

ist, dass Sie Größe, Position und Formatierung der Steuerelemente auf die enthaltenen<br />

Daten abstimmen können. Bisher war dazu ein stetiges Wechseln zwischen Entwurfs-<br />

und Formularansicht notwendig.<br />

Der Wert der Eigenschaft Layoutansicht zulassen (VBA: LayoutView) entscheidet, ob<br />

Sie ein Formular grundsätzlich in der Layoutansicht anzeigen können. Wenn diese<br />

Voraussetzung geschaffen ist, können Sie sie problemlos einsetzen, um wie in<br />

Abbildung 3.3 die Breite von Steuerelementen in der Endlosansicht eines Formulars<br />

den vorhandenen Inhalten anzupassen.<br />

Abbildung 3.3: Anpassen der Breite von Steuerelementen in der Layoutansicht<br />

Im Gegensatz zu <strong>Access</strong> 2007 können Sie nun einige Entwurfsaufgaben mehr in der<br />

Layoutansicht erledigen – so lassen sich etwa Steuerelemente über die Steu er ele ment-<br />

Schaltflächen im Ribbon-Bereich Entwurf|Steuerelemente hinzufügen. Sie können nun<br />

auch Steuer ele men te duplizieren oder mit den Cursor-Tasten in Kombination mit der<br />

Um schalt- und der Strg-Taste fein positionieren beziehungsweise ihre Größe anpassen.<br />

Dies ist nun auch nötig, denn in Webdatenbanken steht nur noch die Layoutansicht<br />

<strong>für</strong> den Entwurf von Formularen und Berichten zur Verfügung (mehr dazu unter<br />

»Webdatenbanken« ab Seite 603).<br />

Datenblattansicht<br />

Die Datenblattansicht zeigt die im Detailbereich befindlichen gebundenen Felder genau<br />

wie in der von Tabellen und Abfragen bekannten Datenblattansicht an. Steuerele<br />

mente im Kopf- und Fußbereich stellt die Datenblattansicht nicht dar. Aus dem<br />

Detailbereich fallen auch einige Steuerelementtypen weg. Angezeigt werden nur Text-<br />

169


Kapitel 3 Formulare<br />

fel der, Kombinationsfelder, Listenfelder (als Kombinationsfeld), Optionsfelder und Kontrollkästchen,<br />

die nicht in Optionsgruppen organisiert sind, Optionsgruppen (allerdings<br />

nur als Textfeld, nicht mit Optionsfeldern), Umschaltflächen (als Wert) und einige andere<br />

Steuerelemente. Bezeichnungsfelder, die an eines der genannten Steuerelemente<br />

gebunden sind, zeigt die Datenblattansicht als Spaltenkopf an.<br />

In der Praxis werden Sie die Datenblattansicht wohl nie <strong>für</strong> eigenständige Formulare<br />

einsetzen, sondern nur als Unterformular – auch, wenn Sie eigentlich nur Daten in der<br />

Datenblattansicht anzeigen möchten. Der Grund ist ganz einfach: Die Datenblattansicht<br />

ist sehr egoistisch und lässt keinen Platz <strong>für</strong> andere Steuerelemente. Wenn Sie eine<br />

solche Ansicht etwa mit einem kleinen Suchfeld oder mit Schaltflächen zum Schließen<br />

oder Abbrechen versehen möchten, müssen Sie eine Art »Rahmen-Formular« verwenden<br />

und das Formular in der Datenblattansicht dort als Unterformular einbetten. Wie<br />

das aussieht, sehen Sie in Abbildung 3.4. Weitere Informationen zu dieser Ansicht finden<br />

Sie unter »Einfache Daten in der Übersicht als Datenblatt« ab Seite 205.<br />

Abbildung 3.4: Ein Formular in der Datenblattansicht mit Formular-Rahmen zum Hinzufügen von<br />

Schaltflächen<br />

Die Datenblattansicht kommt auch oft zum Einsatz, wenn es um die Darstellung von<br />

1:n- oder m:n-Beziehungen geht. Dabei zeigt dann ein Hauptformular die Da ten der<br />

Detailtabelle und das Unterformular die Daten der Mastertabelle in der Da ten blattan<br />

sicht an. Ein Beispiel sehen Sie in Abbildung 3.5, weitere Informationen zu dieser<br />

Dar stellung finden Sie in »1:n-Beziehung per Unterformular und Datenblattansicht« ab<br />

Seite 214.<br />

Einfaches Formular<br />

Diese Ansicht ist <strong>für</strong> die Anzeige der Details eines Datensatzes geeignet. Sie zeigt jeweils<br />

einen einzigen Datensatz an. Im Gegensatz zur Datenblattansicht können Sie die<br />

Steuer elemente ganz nach Ihren Wünschen auf die Bereiche des Formulars aufteilen<br />

und – noch wichtiger – Sie können alle Steuerelemente einsetzen, die <strong>Access</strong> hergibt.<br />

Be sonders interessant sind dabei Listenfelder und Unterformulare, mit denen Sie die<br />

struk turierte Anzeige von Daten aus mehreren verknüpften Tabellen anzeigen können.<br />

170


Abbildung 3.5: Für Unterformulare bietet sich oft die Datenblattansicht an<br />

Endlosformular<br />

Formulare in <strong>Access</strong> <strong>2010</strong><br />

Endlosformulare sind prinzipiell einfache Formulare, von denen Sie aber mehrere<br />

gleichzeitig (und zwar untereinander) anzeigen können. Damit können Sie einige<br />

Nachteile der Datenblattansicht ausmerzen: So lassen sich auch hier beliebige<br />

Steuerelemente anzeigen. Eine Ausnahme sind Unterformulare: Diese können Sie nicht<br />

in der Endlosansicht eines Formulars einsetzen. Um mit einem Endlosformular ein<br />

Formular in der Datenblattansicht nachzubauen, brauchen Sie nur die Steuerelemente<br />

genauso wie in der Datenblattansicht anzuordnen, passende Beschriftungsfelder im<br />

Formularkopf zu platzieren und die Höhe des De tail be reichs entsprechend zu verkleinern.<br />

Das kann etwa so wie in Abbildung 3.6 aussehen (weitere Informationen zu dieser<br />

Ansicht finden Sie unter »Einfache Daten in der Übersicht mit Endlosformularen« ab<br />

Seite 201). Eine solche Endlosansicht können Sie natürlich auch als Unterformular einsetzen,<br />

um eine 1:n- oder m:n-Beziehung darzustellen.<br />

Abbildung 3.6: Ein Endlosformular als Ersatz <strong>für</strong> die Datenblattansicht<br />

PivotTable- und PivotChart-Ansicht<br />

Diese beiden Ansichten bietet <strong>Access</strong> seit der Version XP an. Sie bieten eine recht gute<br />

Entwurfsansicht, mit der sich schnell ansprechende Ergebnisse erzielen lassen. Aller-<br />

171


Kapitel 3 Formulare<br />

dings sind diese beiden Ansichten in Zusammenhang mit größeren Datenmengen recht<br />

lang sam. Da das Interesse seitens der <strong>Access</strong>-<strong>Entwickler</strong> an diesen Ansichten relativ<br />

gering ist, geht dieses Buch nicht näher darauf ein.<br />

3.1.3 Geteilte Formulare<br />

Seit <strong>Access</strong> 2007 gibt es einige Eigenschaften, mit denen Sie Formulare zur Anzeige<br />

von Daten in Listen- und Detailansicht aufteilen können. Sicher haben Sie schon einmal<br />

ein Unterformular oder ein Listenfeld in einem Formular verwendet, um eine<br />

Über sicht über Datensätze – etwa von Mitarbeiterdaten – anzuzeigen und darüber die<br />

Aus wahl eines Datensatzes zur Anzeige der Detaildaten in einem anderen Bereich des<br />

Formulars einzublenden.<br />

Hierzu ist VBA-Programmierung notwendig, und Microsoft hat mit den sogenannten<br />

»Split Forms« eine Möglichkeit geschaffen, dies durch einfaches Anpassen einiger<br />

Eigenschaften zu erreichen. (Natürlich können Sie dies auch per Assistenten durchführen,<br />

aber diese kleinen Helfershelfer will dieses Buch ja beiseite lassen ...)<br />

Grundvoraussetzung <strong>für</strong> das Teilen eines Formulars ist das Einstellen der Eigenschaft<br />

Standardansicht auf den neuen Wert Geteiltes Formular (siehe Abbildung 3.7).<br />

Abbildung 3.7: Mit der richtigen Einstellung <strong>für</strong> die Eigenschaft Standardansicht erreichen Sie die<br />

Anzeige eines geteilten Formulars<br />

Haben Sie diese Eigenschaft eingestellt, brauchen Sie eigentlich nur noch eine passende<br />

Datensatzquelle auszuwählen und die Felder in der gewünschten Reihenfolge<br />

anzuordnen. Das kann dann, in einem sehr einfachen Fall, wie in Abbildung 3.8 aussehen.<br />

Ein Wechseln in die Formularansicht sorgt schließlich <strong>für</strong> die Anzeige des geteilten Formu<br />

lars (siehe Abbildung 3.9). Mit einem Klick auf einen der Datensätze in der Hälfte mit<br />

der Datenblattansicht zeigen Sie den jeweils angeklickten Datensatz im Detailbereich<br />

des Formulars an.<br />

172


Formulare in <strong>Access</strong> <strong>2010</strong><br />

Abbildung 3.8: Dieser Entwurf soll den Detailbereich eines geteilten Formulars ausmachen<br />

Abbildung 3.9: Die geteilte Ansicht eines Formulars mit Datenblatt und Detailansicht<br />

Natürlich können Sie <strong>für</strong> geteilte Formulare einige Einstellungen vornehmen. Welche<br />

dies sind, zeigt Tabelle 3.4.<br />

Weitere Möglichkeiten bietet das Kontextmenü des Datenblatt-Bereichs des geteilten<br />

Formulars: Hier haben Sie alle Möglichkeiten, die Sie auch sonst in der Datenblattansicht<br />

haben.<br />

Die Formatierung des Datenblattbereichs können Sie mit den Steuerelementen im<br />

Ribbon unter Start|Schriftart anpassen – vorher müssen Sie allerdings den Fokus auf<br />

das Datenblatt setzen.<br />

Die passenden Eigenschaften unter VBA heißen DatasheetAlter nateBackColor, Datasheet<br />

BackColor, DatasheetBorderLineStyle, DatasheetCellsEffect, Datasheet Column­<br />

Header UnderlineStyle, DatasheetFontHeight, DatasheetFontItalic, Data sheet Font Name,<br />

Data sheetFontUnderline, DatasheetFontWeight, DatasheetForeColor, Data sheet Grid lines­<br />

Be ha viour und DatasheetGridlinesColor – <strong>für</strong> weitere Informationen siehe Onlinehilfe.<br />

3.1.4 Hilfreiche Funktionen <strong>für</strong> den Formularentwurf<br />

Seit <strong>Access</strong> 2007 gibt es einige neue Funktionen zum Entwerfen von Formularen, die mit<br />

<strong>Access</strong> <strong>2010</strong> noch erweitert wurden.<br />

173


Kapitel 3 Formulare<br />

Eigenschaft<br />

(deutsch)<br />

Größe des geteilten<br />

Formulars<br />

Orientierung des<br />

geteilten Formulars<br />

Teilerleiste des<br />

geteilten Formulars<br />

Datenblatt des<br />

geteilten Formulars<br />

Drucken des geteilten<br />

Formulars<br />

Position der Teilerleiste<br />

speichern<br />

174<br />

Eigenschaft<br />

(VBA)<br />

Beschreibung<br />

SplitFormSize Legt die Höhe des Detailbereichs des<br />

Formulars fest. Einheit in VBA: Twips<br />

(567 Twips entsprechen 1 cm).<br />

SplitFormOrientation Gibt die Position des Datenblatts an.<br />

Mögliche Werte:<br />

0 (acDatasheetOnTop, oben, Standard)<br />

1 (acDatasheetOnBottom, unten)<br />

2 (acDatasheetOnLeft, links)<br />

3 (acDatasheetOnRight, rechts)<br />

SplitFormSplitterBar Gibt an, ob der Benutzer die Größe der<br />

Bereiche mit einer Teilerleiste einstellen kann.<br />

Boolean.<br />

SplitFormDatasheet Gibt an, ob der Benutzer Daten im Datenblatt<br />

bearbeiten darf. Mögliche Werte:<br />

0 (acDatasheetAllowEdits, editieren erlaubt)<br />

1 (acDatasheetReadOnly, schreibgeschützt)<br />

SplitFormPrinting Gibt an, welcher Bereich des Formulars gedruckt<br />

wird. Mögliche Werte:<br />

0 (acFormOnly, Detailansicht)<br />

1 (acGridOnly, Datenblatt)<br />

SplitFormSplitterBarSave Gibt an, ob die Position der Teilerleiste beim<br />

Schließen gespeichert wird. Boolean.<br />

Tabelle 3.4: Eigenschaften zum Anpassen der geteilten Formularansicht<br />

Automatische Layouts<br />

Wenn Sie Steuerelemente und ihre Beschriftungsfelder in Tabellenform anordnen<br />

möchten, können Sie einfach alle betroffenen Elemente markieren und dann einen der<br />

Ribbon-Einträge Anordnen|Tabelle|Tabelle oder Anordnen|Tabelle|Gestapelt auswählen.<br />

Diese Funktionen stehen in der Entwurfs- und der Layoutansicht zur Ver fü gung. <strong>Access</strong><br />

ordnet die Steuerelemente dann wie in Abbildung 3.10 (Ausrichtung: Ta belle) oder wie in<br />

Abbildung 3.11 (Ausrichtung: Gestapelt) an.<br />

Abbildung 3.10: Felder mit tabellarischer Ausrichtung


Abbildung 3.11: Felder in der »gestapelten« Ausrichtung<br />

Formulare in <strong>Access</strong> <strong>2010</strong><br />

Es handelt sich hierbei aber nicht nur um das automatische Anordnen, denn <strong>Access</strong> fügt<br />

gleichzeitig eine Art Rahmen hinzu, der die Positionen der enthaltenen Steuerelemente<br />

wahrt. Außerdem bietet dieser Rahmen die Möglichkeit, dass Sie die Reihenfolge der<br />

Steuer elemente neu sortieren, ohne sich um die Wahrung der Abstände und der Po sition<br />

kümmern zu müssen. Markieren Sie dazu einfach das zu verschiebende Feld und<br />

ziehen Sie es an die gewünschte Stelle. Eine orange Linie zeigt dabei an, wo <strong>Access</strong> das<br />

Steuerelement platziert.<br />

Bei der tabellarischen Ausrichtung übernimmt <strong>Access</strong> automatisch die Aufgabe, die<br />

Feld bezeichnungen als Spaltenköpfe in den Formularkopf zu kopieren. Wenn Sie einzelne<br />

Elemente einer Gruppe von gemeinsam ausgerichteten Steuer ele men ten individuell<br />

anpassen möchten, müssen Sie mit Einschränkungen leben: In der tabellarischen<br />

Ausrichtung können Sie nur die Breite und in der gestapelten Ausrichtung nur die Höhe<br />

der einzelnen Steuerelemente anpassen.<br />

Das ist aber nicht schlimm: Wenn Sie die Steuerelemente mit einer der Ausrichten-<br />

Funk tionen positioniert haben, können Sie den Layoutrahmen wieder entfernen, indem<br />

Sie diesen markieren und den Ribbon-Eintrag Anordnen|Tabelle|Entfernen betätigen.<br />

Danach können Sie manuell Hand an die Steuerelemente anlegen, um etwa die Breite<br />

des PLZ-Felds in Abbildung 3.11 zu verringern.<br />

Den Layoutrahmen markieren Sie im Übrigen mit einem Klick auf das kleine Kreuz,<br />

das nach einem Klick auf eines der enthaltenen Steuerelemente erscheint (siehe<br />

Abbildung 3.12).<br />

Sie können dem Layout auch nachträglich einzelne Steuerelemente hinzufügen oder<br />

Steuer elemente daraus entfernen. Zum Hinzufügen ziehen Sie einfach das passende<br />

Steuerelement an die gewünschte Stelle im Layout, zum Entfernen markieren Sie das<br />

175


Kapitel 3 Formulare<br />

betroffene Steuerelement und klicken anschließend auf die Ribbon-Schalt flä che Anordnen|Tabelle|Layout<br />

entfernen. Nach dem Entfernen verbleibt das ent fernte Steuerele<br />

ment an Ort und Stelle und die daneben oder darunter befindlichen Steuer ele men te<br />

rü cken an dessen Platz. Sie müssen auf einem Layout entfernte Steuer ele mente also<br />

noch manuell verschieben, damit diese nicht mit den Steuer ele men ten des Layouts<br />

über lappen.<br />

Abbildung 3.12: Mit dem Kreuz links oben markieren Sie das komplette Layout<br />

Die Layoutansicht sollte dem <strong>Entwickler</strong> einer Datenbank vorbehalten sein. Sie sollten<br />

daher entweder die Eigenschaft Layoutansicht zulassen <strong>für</strong> die gewünschten Formulare<br />

auf Nein einstellen oder die Ansicht <strong>für</strong> die komplette Datenbank deaktivieren, indem Sie<br />

in den <strong>Access</strong>-Optionen die Eigenschaft Aktuelle Datenbank|Anwendungsoptionen|Lay outan<br />

sicht aktivieren auf Ja einstellen. In VBA können Sie dieses Feature mit der Eigenschaft<br />

AllowLayoutView des Formulars aktivieren und deaktivieren.<br />

Mit <strong>Access</strong> <strong>2010</strong> gibt es einige Verbesserungen: So können Sie nun das Raster weiter<br />

verfeinern. Sie können Zellen horizontal oder vertikal aufteilen, um zusätzliche<br />

Steuerelemente einfügen zu können (etwa eine Schaltfläche), und diese wieder verbinden.<br />

Jede Zelle darf aber nur ein Steuerelement enthalten.<br />

Wenn Sie Zellen aufteilen, fügt <strong>Access</strong> in den entstandenen Leerräumen automatisch<br />

das neue Steuerelement EmptyCell als Platzhalter ein. Dies ist vor allem dem Layout von<br />

Webformularen und -berichten geschuldet.<br />

Die notwendigen Befehle finden Sie im Kontextmenü des jeweiligen Elements (siehe<br />

Abbildung 3.13). Außerdem finden Sie hier die Möglichkeit, komplette Zeilen und Spalten<br />

einzufügen (Kontextmenüeinträge Einfügen|Links einfügen, Einfügen|Rechts einfügen,<br />

Einfügen|Oben einfügen und Einfügen|Unten einfügen).<br />

176


Abbildung 3.13: Aufteilen und verbinden der Zellen eines Rasters<br />

Formulare in <strong>Access</strong> <strong>2010</strong><br />

Diese Aktionen können Sie auch mit der RunCommand-Methode durchführen, und zwar<br />

mit den folgenden Konstanten:<br />

» acCmdLayoutInsertRowAbove<br />

» acCmdLayoutInsertRowBelow<br />

» acCmdLayoutInsertColumnLeft<br />

» acCmdLayoutInsertColumnRight<br />

» acCmdLayoutMergeCells<br />

» acCmdLayoutSplitColumnCell<br />

» acCmdLayoutSplitRowCell<br />

Das komplette Layout markieren Sie mit RunCommand acCmdSelectEntireLayout.<br />

Berechnungen in der Datenblattansicht<br />

In Tabellen, Abfragen und Formularen steht seit <strong>Access</strong> 2007 <strong>für</strong> die Datenblattansicht<br />

eine neue Funk tion zum Durchführen von Berechnungen und zur Anzeige ihrer Ergebnisse<br />

bereit. Da der Benutzer Ihrer Datenbank keinen Kontakt mit der Datenblattansicht<br />

der Tabellen und Abfragen haben soll, ist dies ein Fall <strong>für</strong> das Formulare-Kapitel. Wie<br />

weiter oben er wähnt, erscheinen Daten in der Datenblattansicht meist in Form von<br />

Unterformularen in der Datenblattansicht – ein Formular in der Datenblattansicht allein<br />

macht wenig Sinn, da man beispielsweise keine zusätzlichen Steuerelemente<br />

wie Schaltflächen dort un terbringen kann. Ob nun in einem Formular oder in einem<br />

Unterformular: Sie können einem Daten blatt eine Zeile mit Berechnungen hinzufü-<br />

177


Kapitel 3 Formulare<br />

gen, indem Sie es in der Datenblatt an sicht markieren und im Ribbon die Schaltfläche<br />

Start|Datensätze|Summen anklicken. Schon erscheint die gewünschte Zeile in der<br />

Tabelle und Sie müssen nur noch das Feld und die Berechnungsmethode festlegen (siehe<br />

Abbildung 3.14). Praktischerweise bleibt die Berechnungszeile am unteren Rand des<br />

Datenblatts »kleben« und wird nicht nach unten weggeschoben, wenn das Datenblatt<br />

mehr Datensätze enthält, als es gleichzeitig anzeigen kann.<br />

Abbildung 3.14: Auswahl einer Berechnungsart <strong>für</strong> das Feld Betrag<br />

<strong>Access</strong> stellt je nach dem Datentyp des Feldes unterschiedliche Funktionen zur Ver fügung.<br />

Bei Zahlen stehen naturgemäß mehr Varianten bereit als <strong>für</strong> Textfelder – hier gibt<br />

es lediglich die Möglichkeit, die Anzahl der Einträge auszugeben. Wie <strong>für</strong> viele neue Features<br />

seit <strong>Access</strong> 2007 gilt: Probieren Sie es einfach aus, es geht ganz leicht.<br />

3.1.5 Farben und Schriftarten per Design festlegen<br />

Mit den sogenannten Designs können Sie das Aussehen von Formularen und Berichten<br />

sowie der enthaltenen Steuerelemente einfach anpassen. Die da<strong>für</strong> verantwortlichen<br />

Elemente der Benutzeroberfläche finden Sie im Formular- beziehungsweise Berichtsentwurf<br />

im Ribbon-Tab Entwurf (siehe Abbildung 3.15).<br />

Abbildung 3.15: Aufruf der Funktionen zum Festlegen von Designs<br />

178


Dies geschieht im Falle der Farben durch folgende Struktur:<br />

Formulare in <strong>Access</strong> <strong>2010</strong><br />

» Bei den Farben gibt es eine Zuordnung von Farbcode zu Platzhaltern wie beispielsweise<br />

Text/Hintergrund ­ dunkel 1 (siehe Abbildung 3.16).<br />

» In den Farbeigenschaften eines Formulars, Berichts oder Steuerelements legen Sie<br />

entweder direkt einen Farbcode fest (also ein entsprechender Zahlenwert) oder Sie<br />

geben einen der Platzhalter aus Abbildung 3.16 an, aufzurufen über den Ribbon-<br />

Eintrag Designs|Farben|Neue Designfarben erstellen. Letzteres ist die Voraussetzung<br />

<strong>für</strong> die zentrale Anpassung der Farben über den Dialog aus der Abbildung.<br />

Abbildung 3.16: Zuordnung von Farben zu Platzhaltern<br />

Sie können nun beliebig viele eigene Zusammenstellungen der Designfarben erstellen<br />

und speichern. Anschließend klicken Sie mit der rechten Maustaste auf einen der<br />

Einträge in der Liste der Farbschemata und legen fest, ob dieser auf alle Formulare<br />

beziehungsweise Berichte angewendet werden soll oder nur auf das aktuell geöffnete.<br />

Sie können hier auch eigene Schemata löschen oder bearbeiten (siehe Abbildung 3.17).<br />

Die Platzhalter aus Abbildung 3.16 tauchen im Dialog zum Einstellen der Farbe eines<br />

Elements der Benutzeroberfläche in etwas veränderter Form wieder auf (siehe<br />

Abbildung 3.18).<br />

Verändert deshalb, weil es beispielsweise statt Text/Hintergrund ­ dunkel 1 einen Eintrag<br />

Schwarz, Text 1 gibt. Die einzelnen Schemafarben können Sie noch in helleren oder<br />

dunkleren Tönen einsetzen – die entsprechenden Schaltflächen befinden sich unterhalb<br />

der Hauptfarben des Schemas.<br />

179


Kapitel 3 Formulare<br />

Abbildung 3.17: Festlegen eines Farbschemas<br />

Abbildung 3.18: Auswählen der Platzhalter des Farbschemas <strong>für</strong> ein Steuerelement<br />

Wenn Sie einem Element eine Designfarbe zuweisen, legen Sie damit drei Eigenschaften<br />

fest:<br />

» Index der Designfarbe (BorderThemeColorIndex), siehe unten<br />

» Abdunkelung (BorderShade): Wert zwischen 1 und 100, der die Abdunkelung des<br />

Elements festlegt (100 entspricht der Originalfarbe)<br />

180


Formulare in <strong>Access</strong> <strong>2010</strong><br />

» Aufhellung (BorderTint): Wert zwischen 1 und 100, der die Aufhellung des Elements<br />

festlegt (100 entspricht der Originalfarbe)<br />

Die folgenden Zahlenwerte entsprechen dem Index <strong>für</strong> die Designfarben aus Abbildung 3.16:<br />

» 0 (Text 1)<br />

» 1 (Hintergrund 1)<br />

» 2 (Text 2)<br />

» 3 (Hintergrund 2)<br />

» 4 (Akzent 1)<br />

» 5 (Akzent 2)<br />

» 6 (Akzent 3)<br />

» 7 (Akzent 4)<br />

» 8 (Akzent 5)<br />

» 9 (Akzent 6)<br />

» 10 (Hyperlink)<br />

» 11 (Besuchter Hyperlink)<br />

Diese Informationen sind einzeln nur per VBA zugänglich. Im Eigenschaftsfenster wählen<br />

Sie nur einen Eintrag des Dialogs wie Schwarz, Text 1, Heller 25% aus.<br />

Farben statisch gestalten<br />

Wenn die Farben nicht durch den Wechsel des Designs beeinflusst werden sollen, wählen<br />

Sie einfach eine der Standardfarben aus dem Dialog aus Abbildung 3.18 aus oder<br />

legen manuell einen entsprechenden Zahlenwert fest.<br />

Design oder nicht Design?<br />

Mit der Eigenschaft Design verwenden (VBA: UseTheme) können Sie <strong>für</strong> Navi ga tionsschalt<br />

flächen, Navigationssteuerelemente, Schaltflächen, Registerkarten-Steuer ele -<br />

mente und Umschaltflächen festlegen, ob Sie das angegebene Design verwenden wollen<br />

oder nicht.<br />

Deshalb enthalten diese Steuerelemente <strong>für</strong> alle Elemente, deren Farbe eingestellt<br />

werden kann, vier Eigenschaften.<br />

Hat Design verwenden den Wert Nein, wird die in BackColor angegebene Farbe verwendet,<br />

sonst die in den übrigen drei Ei gen schaften festgelegte:<br />

181


Kapitel 3 Formulare<br />

» ...Color<br />

» ...Shade<br />

» ...ThemeColorIndex<br />

» ...Tint<br />

Die drei Pünktchen werden dabei <strong>für</strong> die verschiedenen Eigenschaften durch Back-,<br />

Border-, Fore-, Gridline-, Hover-, HoverFore-, Pressed- und PressedFore- ersetzt. Steht<br />

Design verwenden <strong>für</strong> ein Steuerelement auf Nein, kommen auch die Effekte etwa <strong>für</strong><br />

den Farbverlauf nicht zum Tragen.<br />

Design-Schriften<br />

Für die Steuerelemente mit Beschriftung wie etwa Bezeichnungsfelder oder Schaltflächen<br />

oder mit Texten wie das Textfeld können Sie auch die Schriftart per Design festlegen.<br />

Wenn Sie im Ribbon unter Entwurf|Designs|Schriftarten einen Eintrag auswählen,<br />

wirkt sich dies direkt auf alle Texte im aktuell geöffneten Formular aus. Sie legen damit<br />

allerdings nur die Schriftart fest, andere Einstellungen wie Schriftgröße et cetera bleiben<br />

unberührt.<br />

Genau genommen können Sie sogar zwei Schriftarten pro Design festlegen. Klicken Sie<br />

im Ribbon auf Entwurf|Designs|Schriftarten und wählen dann ganz unten den Eintrag<br />

Neue Designschriftart erstellen... aus, erscheint der Dialog aus Abbildung 3.19.<br />

Hier wählen Sie zwei Schriftarten aus, die Sie später auf die Steuerelemente des<br />

Formulars verteilen können. Standardmäßig wird beim Neuanlegen von Steuerelementen<br />

oder beim Zuweisen dieses Designs die Textkörper-Schriftart verwendet.<br />

Nur wenn Sie aus dem Ribbon den Eintrag Entwurf|Kopfzeile/Fußzeile|Titel auswählen,<br />

wird ein Bezeichnungsfeld mit der Überschriften-Schriftart erstellt.<br />

Abbildung 3.19: Neue Design-Schriftarten festlegen<br />

Sie können die Steuerelemente jedoch komfortabel über das Eigenschaftsfenster mit<br />

den beiden Design-Schriftarten bestücken (siehe Abbildung 3.20). Beachten Sie, dass<br />

nur die mit dem Zusatz (Kopfbereich) beziehungsweise (Detailbereich) versehenen<br />

182


Formulare in <strong>Access</strong> <strong>2010</strong><br />

Schrift arten die aktuellen Design-Schriftarten sind, die beim Wechseln des Designs<br />

durch andere Schriftarten ersetzt werden. Wenn Sie etwa die Schriftart Arial ohne Zusatz<br />

auswählen, bleibt diese Schriftart auch beim Design-Wechsel erhalten.<br />

Abbildung 3.20: Einfache Auswahl der beiden Design-Schriftarten<br />

Per VBA können Sie übrigens prüfen, ob ein Element der Benutzeroberfläche eine der<br />

beiden Designschriftarten verwendet oder nicht: Die Eigenschaft ThemeFontIndex liefert<br />

einen der folgenden Werte zurück:<br />

» 0: Es wird die Designschriftart <strong>für</strong> den Kopfbereich verwendet.<br />

» 1: Es wird die Designschriftart <strong>für</strong> den Detailbereich verwendet.<br />

» ­1: Es wird keine Designschriftart verwendet.<br />

Farben und Schriften zusammenführen<br />

Wenn Sie mit den aktuell ausgewählten Farben und Schriftarten zufrieden sind, können<br />

Sie beide kombiniert speichern. Dies erledigen Sie mit einem Klick auf den Ribbon-<br />

Eintrag Entwurf|Designs|Designs und durch anschließendes Auswählen des Eintrags<br />

Aktuelles Design speichern.<br />

Die Informationen werden im Vorlagen-Verzeichnis von Office im Unterordner Document<br />

Themes als Datei mit der Endung .thmx gespeichert (unter Win dows 7 beispielsweise im<br />

Verzeichnis C:\Users\Andre Minhorst\AppData\Roaming\Micro soft\Templates\Document<br />

Themes).<br />

Wenn Sie eine mit einem Design ausgestattete Datenbank weitergeben, brauchen Sie die<br />

.thmx-Datei übrigens nicht mit auf den Zielrechner zu übertragen – alle Eigenschaften<br />

sind natürlich in der Datenbank gespeichert. Sie können das Design dann lediglich nicht<br />

auf neue Formulare oder Berichte anwenden.<br />

183


Kapitel 3 Formulare<br />

3.1.6 Sonstige Neuerungen<br />

Es gibt noch einige weitere kleine Neuheiten, die eine Erwähnung wert sind.<br />

Alternierender Hintergrund in Datenblättern<br />

<strong>Access</strong> <strong>2010</strong> bietet die Möglichkeit, die Datenblattansicht von Tabellen, Abfragen und<br />

For mularen mit einer alternierenden Hintergrundfarbe zu versehen. Eine <strong>Access</strong>-weite<br />

Op tion finden Sie in den <strong>Access</strong>-Optionen unter Datenblatt|Standardfarben|Alternative Hin ­<br />

ter grund far be. Den Effekt können Sie sich in zahlreichen Abbildungen dieses Kapitels<br />

an sehen. Wenn Sie eine individuelle Färbung <strong>für</strong> einzelne Objekte vornehmen möchten,<br />

ver wenden Sie in der Datenblattansicht den Ribbon-Eintrag Start|Schriftart|Alternative<br />

Füllung/Hintergrundfarbe. Alternativ können Sie dies auch mit VBA erreichen; die passende<br />

Eigenschaft heißt DatasheetAlternateBackColor.<br />

Steuerelemente mit Abstand<br />

Mit den folgenden vier Eigenschaften stellen Sie den Abstand von Steuerelementen zur<br />

nächsten Gitternetzlinie in einem der Layouts Tabelle oder Geschachtelt fest. Die Ei genschaf<br />

ten sind <strong>für</strong> fast alle Steuerelemente verfügbar:<br />

» Textabstand oben (TopPadding)<br />

» Textabstand unten (BottomPadding)<br />

» Textabstand links (LeftPadding)<br />

» Textabstand rechts (RightPadding)<br />

Steuerelemente verankern<br />

Mit dem Ribbon-Eintrag Anordnen|Position|Anker können Sie Steuerelemente mit dem<br />

Seitenrand des Formulars verankern. Alternativ stellen Sie die Eigenschaften Ho rizon<br />

taler Anker (HorizonalAnchor) und Vertikaler Anker (VerticalAnchor) des betroffenen<br />

Steuer elements ein. Die Standardeinstellungen sind Links <strong>für</strong> den horizontalen Anker<br />

und Oben <strong>für</strong> den vertikalen Anker. Das bedeutet, dass sich das Steuerelement beim<br />

Ver größern oder Verkleinern des Formulars nicht ändert.<br />

Wie sich die unterschiedlichen Einstellungen auswirken, probieren Sie am besten selbst<br />

aus – und zwar in der Layoutansicht, die da<strong>für</strong> perfekt geeignet ist. Einen guten Eindruck<br />

vermittelt das Beispiel in Abbildung 3.21.<br />

Beschriftung der Navigationsleiste<br />

Links von der Navigationsleiste von Formularen können Sie ab <strong>Access</strong> 2007 einen Text<br />

an zeigen. Diesen legen Sie mit der Eigenschaft Navigationsbeschriftung fest (in VBA: Na­<br />

184


Formulare in <strong>Access</strong> <strong>2010</strong><br />

vi gationCaption). Abbildung 3.22 zeigt ein Beispiel <strong>für</strong> eine auf diese Weise beschriftete<br />

Na vi gationsleiste. Da die Länge begrenzt und die Anzeige unauffällig ist, scheint der<br />

Nut zen dieser Funktion eingeschränkt zu sein.<br />

Abbildung 3.21: Verankerte Elemente eines Formulars<br />

Abbildung 3.22: Die Beschriftung der Navigationsleiste darf nicht sehr lang sein<br />

Wenn Sie den Benutzer mit der Beschriftung unbedingt auf die Navigationselemente<br />

aufmerksam machen möchten, stellen Sie <strong>für</strong> die Eigenschaft Zeitgeberintervall des<br />

Formulars den Wert 500 ein und hinterlegen <strong>für</strong> die Ereigniseigenschaft Bei Zeitgeber<br />

die folgende Routine:<br />

Private Sub Form_Timer()<br />

Static bol As Boolean<br />

bol = Not bol<br />

If bol Then<br />

Else<br />

End If<br />

End Sub<br />

Me.NavigationCaption = "Hier navigieren!->"<br />

Me.NavigationCaption = "->Hier navigieren!"<br />

Listing 3.1: Wechselnde Navigationsleistenbeschriftungen per Zeitgeber<br />

185


Kapitel 3 Formulare<br />

Dies lässt die Beschriftung »Hier navigieren« hin- und herspringen. Beachten Sie, dass<br />

die verwendeten Zeichen immer die gleiche Breite einnehmen, sonst springt auch die<br />

eigentliche Navigationsleiste hin und her. (Kleiner Tipp: Nehmen Sie nicht alles ernst,<br />

was in diesem Buch geschrieben steht.)<br />

Formular-Ribbon<br />

Sie können <strong>für</strong> jedes Formular eine der in der Datenbank gespeicherten Ribbon-Defi<br />

ni ti onen festlegen. Damit können Sie beispielsweise da<strong>für</strong> sorgen, dass bei Ak ti vierung<br />

ei nes Formulars nur formularspezifische Ribbon-Elemente angezeigt werden,<br />

oder ein fach das Ribbon um ein spezielles Tab-Element <strong>für</strong> das Formular erweitern.<br />

Sie legen das Ribbon mit der Formular-Eigenschaft Name des Menübands (unter VBA:<br />

RibbonName) fest.<br />

Dies funktioniert nicht bei Popup- und geteilten Formularen. Weitere In formationen<br />

zum Thema Ribbons finden Sie unter »Ribbon« ab Seite 679.<br />

3.1.7 Formularvorlage<br />

Nicht neu, aber möglicherweise nicht jedem bekannt sind die folgenden beiden Features,<br />

die das Erstellen von Formularen vereinfachen.<br />

Mit der Zeit werden Sie sich einen gewissen Stil angewöhnen, was das Aussehen von<br />

For mu laren angeht. Vielleicht verwenden Sie eine bestimmte Hintergrundfarbe <strong>für</strong><br />

Kopf-, Fuß- und Detailbereich oder stellen weitere Eigenschaften immer auf die gleichen<br />

Werte ein.<br />

Wenn Sie solche Standardeinstellungen öfter benötigen, können Sie ein For mular mit<br />

all den gewünschten Eigenschaften anlegen und es unter dem For mu lar na men Normal<br />

speichern. Von nun an besitzen alle neu erstellten Formulare standardmäßig diese<br />

Eigenschaften. Erst wenn Sie das Formular Normal entfernen oder umbenennen, verwendet<br />

<strong>Access</strong> wieder die ursprünglichen Standardeinstellungen beim Anlegen von<br />

Formularen.<br />

Das Gleiche gilt übrigens auch <strong>für</strong> Berichte, auch hier verwenden Sie standardmäßig den<br />

Berichtsnamen Normal <strong>für</strong> die Vorlage. Wenn Sie einen anderen Formular- beziehungsweise<br />

Berichtsnamen als Normal einsetzen möchten, können Sie dies in den <strong>Access</strong>-<br />

Optionen unter Objekt­Designer|Formular/Berichte|Formularvorlage/Berichtsvorlage einstel<br />

len.<br />

Beim Öffnen filtern und sortieren<br />

Mit den beiden Eigenschaften Beim Laden filtern (VBA: FilterOnLoad) und Beim Laden<br />

sortieren (VBA: OrderByOnLoad) legen Sie fest, ob eine in den Eigenschaften Filter (Filter)<br />

186


Formulare öffnen<br />

oder Sortiert nach (OrderBy) angegebene Filterung beziehungsweise Sortierung direkt<br />

beim Laden des Formulars ausgeführt werden soll.<br />

An Bildschirmgröße anpassen<br />

Mit der gleichnamigen Eigenschaft bestimmen Sie, ob Formulare, die zu groß <strong>für</strong> die<br />

aktuelle <strong>Access</strong>-Fensterbreite sind, dem verfügbaren Platz angepasst werden sollen.<br />

Unter VBA lautet der Name dieser Eigenschaft FitToScreen – obwohl dies streng genommen<br />

keine Anpassung an den Bildschirm, sondern an den freien MDI-Bereich von<br />

<strong>Access</strong> ist.<br />

Keine Entwurfsänderungen in der Formularansicht<br />

Die Eigenschaft Entwurfsänderungen zulassen (AllowDesignChanges) entfällt. Sie bewirkte<br />

in älteren Versionen, dass Formulare nur in der Entwurfsansicht das Eigenschaftenfenster<br />

angezeigt haben. Seit <strong>Access</strong> 2007 können Sie das Eigenschaftenfenster grundsätzlich<br />

nur noch in der Entwurfs- und der Layoutansicht einblenden.<br />

3.1.8 Berichte in Unterformularen<br />

Mit <strong>Access</strong> <strong>2010</strong> können Unterformulare erstmals Berichte anzeigen. Dazu legen Sie<br />

einfach den Berichtsnamen als Wert <strong>für</strong> die Eigenschaft Herkunftsobjekt fest.<br />

3.2 Formulare öffnen<br />

Formulare öffnen Sie in der Regel mit der DoCmd.OpenForm-Anweisung. Es geht zwar<br />

auch über das Instanzieren des entsprechenden Formularobjekts und anschließendes<br />

Einstellen der Eigenschaft Visible auf den Wert True, das ist aber eher Spezialfällen wie<br />

dem Öffnen mehrerer Instanzen des gleichen Formulars vor behalten.<br />

Da die DoCmd.OpenForm-Anweisung allgemein bekannt sein dürfte (ansonsten liefert<br />

die Onlinehilfe einen guten Überblick), soll sie hier nicht im Detail beschrieben werden.<br />

Im Hinblick auf die nachfolgenden Beispiele sei nur erwähnt, dass die dortigen Aufrufe<br />

»benannte Parameter« verwenden. Das bedeutet, dass die optionalen Parameter nicht<br />

in der vorgegebenen Reihenfolge durch Kommata getrennt an die Anweisung angefügt<br />

werden, sondern unter Angabe des jeweiligen Parameternamens:<br />

DoCmd.OpenForm "frmKontakteDetailansicht", DataMode:=acFormAdd, _<br />

WindowMode:=acDialog<br />

Mit einer herkömmlichen Parameterliste hätte diese Anweisung so ausgesehen:<br />

DoCmd.OpenForm "frmKontakteDetailansicht", , , , acFormAdd, acDialog<br />

187


Kapitel 3 Formulare<br />

3.3 Ereignisse in Formularen und Steuerelementen<br />

Formulare lassen sich wie auch Berichte bis zu einem gewissen Grad ohne den Einsatz<br />

von Ereigniseigenschaften und VBA verwenden. Sobald Sie aber auch nur eine OK-<br />

Schaltfläche hinzufügen möchten, ist es so weit: Die Programmierung der ersten<br />

Ereignisprozedur steht an. Ereignisse stellen die Schnittstelle zwischen Ober flächenelementen<br />

und der VBA-Programmierung derselben bereit. In diesem Abschnitt erfahren<br />

Sie, welche der zahlreichen Ereignisse von Formularen und der enthaltenen<br />

Steuerelemente oft zum Einsatz kommen und wie Sie diese optimal einsetzen – dazu<br />

gehört auch, dass Sie sich mit der Reihenfolge der Ereignisse vertraut machen.<br />

3.3.1 Ereignisse in Formularen<br />

Formulare bieten eine unüberschaubare Menge Ereignisse. Seit <strong>Access</strong> 2007 sind keine<br />

mehr hinzugekommen, lediglich die neuen Steuerelemente bringen einige neue<br />

Ereignisse mit. Weitere Informationen zu diesen Steuerelementen finden Sie im Kapitel<br />

»Steuerelemente« ab Seite 265.<br />

Anlegen eines Ereignisses<br />

Abbildung 3.23 zeigt einen Teil der Ereigniseigenschaften von Formularen.<br />

Abbildung 3.23: Übersicht der Ereigniseigenschaften eines Formulars<br />

188


Ereignisse in Formularen und Steuerelementen<br />

Zum Anlegen eines Ereignisses klicken Sie doppelt in das entsprechende Textfeld, sodass<br />

dieses den Wert [Ereignisprozedur] erhält, und betätigen dann die Schaltfläche mit<br />

den drei Punkten (…) wie in Abbildung 3.24. Wenn Sie es noch einfacher haben möchten,<br />

aktivieren Sie in den <strong>Access</strong>-Optionen im Bereich Objekt­Designer|Formulare/Berichte<br />

den Eintrag Immer Ereignisprozeduren verwenden.<br />

Dann müssen Sie nur noch auf die beim Aktivieren des Feldes erscheinende Schaltfläche<br />

klicken, um die Ereignisprozedur anzulegen. Wenn Sie dies beispielsweise mit<br />

der ersten aufgeführten Ereigniseigenschaft Beim An zei gen machen, öffnet sich der<br />

VBA-Editor und zeigt direkt den Rumpf der gewünschten Pro zedur an:<br />

Private Sub Form_Current()<br />

End Sub<br />

Listing 3.2: Leerer Rumpf einer frisch angelegten Ereignisprozedur<br />

Abbildung 3.24: Anlegen einer Ereignisprozedur<br />

Neben den Ereignisprozeduren können Sie auch <strong>Access</strong>-Makros und VBA-Funktionen<br />

per Ereignis auslösen. Dazu tragen Sie einfach den Namen des <strong>Access</strong>-Makros oder der<br />

Funktion ein. <strong>Access</strong>-Makros bleiben in diesem Buch weitgehend außen vor; der Einsatz<br />

von Funktionen kann jedoch durchaus sinnvoll sein. So können Sie beispielsweise <strong>für</strong><br />

OK-Schaltflächen eine globale Funktion schreiben, die eine Anweisung zum Schließen<br />

des Formulars enthält, oder eine Funktion im For mu lar modul anlegen, die durch mehrere<br />

Ereignisse des gleichen Formulars ausgelöst wird.<br />

Ereigniseigenschaft und Ereignisprozedur — ein unzertrennliches Paar<br />

Damit ein Ereignis durch die entsprechende Aktion – also etwa Betätigen einer Schaltflä<br />

che – ausgelöst wird, muss die Ereigniseigenschaft den Eintrag [Ereignisprozedur] ent-<br />

189


Kapitel 3 Formulare<br />

halten und eine Ereignisprozedur mit dem <strong>für</strong> dieses Ereignis vorgesehenen Namen im<br />

Klassenmodul des Formulars oder des Berichts vorliegen – beispielsweise cmdOK_Click.<br />

3.3.2 Abfolge und Bedeutung der Ereignisse beim Öffnen und<br />

Schließen eines Formulars<br />

Besonders wichtig beim Umgang mit Ereigniseigenschaften ist die Reihenfolge ihrer<br />

Abarbeitung und der Zusammenhang mit den im Hintergrund ausgelösten internen<br />

Pro zessen wie Laden der angezeigten Daten, Übergeben von Öffnungsargumenten und<br />

der gleichen in gebundenen Formularen. Selbst wenn Sie eine <strong>Access</strong>-Anwendung völlig<br />

ohne Dokumentation programmieren müs sen, können Sie sich hier selbst weiterhelfen:<br />

Schreiben Sie einfach <strong>für</strong> alle Er eig nis eigenschaften, die Sie interessieren, eine kleine<br />

Prozedur, die den jeweiligen Er eig nis namen ausgibt. Wenn Sie dann das Formular öffnen<br />

oder die gewünschte Aktion durch führen, können Sie anschließend oder währenddessen<br />

beobachten, in welcher Rei hen folge die Ereignisse ablaufen. Wenn Sie beispielsweise herausfinden,<br />

welche Sequenz von Ereignissen beim einfachen Öffnen und Schließen eines<br />

Formulars abläuft, legen Sie die Ereignisprozeduren aus folgendem Listing an:<br />

190<br />

Private Sub Form_Activate()<br />

End Sub<br />

Debug.Print "Beim Aktivieren"<br />

Private Sub Form_Close()<br />

End Sub<br />

Debug.Print "Beim Schließen"<br />

Private Sub Form_Current()<br />

End Sub<br />

Debug.Print "Beim Anzeigen"<br />

Private Sub Form_Deactivate()<br />

End Sub<br />

Debug.Print "Beim Deaktivieren"<br />

Private Sub Form_Load()<br />

End Sub<br />

Debug.Print "Beim Laden"<br />

Private Sub Form_Open(Cancel As Integer)<br />

End Sub<br />

Debug.Print "Beim Öffnen"


Private Sub Form_Resize()<br />

End Sub<br />

Debug.Print "Bei Größenänderung"<br />

Private Sub Form_Unload(Cancel As Integer)<br />

End Sub<br />

Debug.Print "Beim Entladen"<br />

Ereignisse in Formularen und Steuerelementen<br />

Listing 3.3: Ausgabe von Meldungen beim Auslösen unterschiedlicher Ereignisprozeduren<br />

Als Ergebnis erhalten Sie beim Öffnen des Formulars folgende Abfolge:<br />

» Beim Öffnen: Tritt beim Öffnen ein. Bietet die Möglichkeit, das Öffnen abzubrechen,<br />

wenn beispielsweise keine Daten vorhanden sind – diese werden dementsprechend<br />

bereits vor dem Beim Öffnen-Ereignis eingelesen.<br />

» Beim Laden: Tritt ein, wenn das Formular einschließlich Steuerelementen geöffnet,<br />

aber noch nicht sichtbar ist. Eignet sich <strong>für</strong> Aktionen wie Setzen von Standardwerten<br />

oder Werten von Steuer ele men ten.<br />

» Bei Größenänderung: Wird beim Öffnen und bei der Änderung der Größe eines<br />

For mu lars ausgelöst. Kann zum Anpassen der Größe von Steuerelementen<br />

an die Größe des For mu lar s verwendet werden – falls Sie nicht zufällig von den<br />

Möglichkeiten rund um die Verankerung von Steuerelementen Gebrauch machen.<br />

» Beim Aktivieren: Wird ausgelöst, wenn das Formular den Fokus erhält.<br />

» Beim Anzeigen: Wird beim Anzeigen, bei jedem Datensatzwechsel und beim Ak tu alisieren<br />

ausgelöst.<br />

Beim Schließen sieht der Ablauf so aus:<br />

» Beim Entladen: Wird durch einen Klick auf eine Schließen-Schaltfläche oder die<br />

DoCmd.Close-Anweisung ausgelöst, findet aber vor dem eigentlichen Schließen statt.<br />

Der Schließen-Vorgang kann an dieser Stelle durch Setzen des Cancel-Parameters<br />

auf den Wert True unterbrochen werden.<br />

» Beim Deaktivieren: Wird ausgelöst, wenn das Formular den Fokus verliert.<br />

» Beim Schließen: Wird im Moment des Schließens ausgelöst.<br />

3.3.3 Abfolge und Bedeutung der Ereignisse beim Bearbeiten<br />

von Datensätzen<br />

Das Bearbeiten eines Datensatzes beginnt mit der ersten Änderung an einem der Felder<br />

und endet mit dem Speichervorgang des Datensatzes.<br />

191


Kapitel 3 Formulare<br />

Ändern von Feldinhalten<br />

Änderungen von Datensätzen beginnen mit dem Ändern des Inhalts mindestens eines<br />

Fel des. Dies löst eines oder mehrere der folgenden Ereignisse aus:<br />

» Vor Eingabe: Wird vor der Eingabe des ersten Zeichens ausgelöst, aber nur in Ver bindung<br />

mit neuen Datensätzen. Kann abgebrochen werden.<br />

» Bei Geändert: Wird beim ersten Eingeben oder Löschen eines Zeichens in einem der<br />

Da tensätze ausgelöst. Kann abgebrochen werden.<br />

Speichern der Änderungen<br />

Wird die Änderung gespeichert, werden in der Regel die folgenden drei Ereignisse ausgelöst.<br />

Das erste Ereignis Vor Aktualisierung kann den Ablauf allerdings abbrechen, etwa<br />

wenn eine Validierung fehlschlägt.<br />

» Vor Aktualisierung: Wird nach dem Auslösen des Speicherns, aber vor dem eigentlichen<br />

Speichervorgang ausgelöst. Ist ein gängiger Platz <strong>für</strong> die Durchführung von<br />

Validierungen. Folgendes Beispiel zeigt, wie Sie die Aktualisierung abbrechen, wenn<br />

be stimmte Daten nicht vorhanden sind (weitere Informationen zum Validieren siehe<br />

»Validieren vor dem Speichern« ab Seite 254):<br />

192<br />

If IsNull(Me!Telefon) And IsNull(Me!EMail) Then<br />

End If<br />

MsgBox "Bitte geben Sie eine Telefonnummer oder eine " _<br />

& "E-Mail-Adresse ein.", vbExclamation + vbOKOnly, _<br />

"Fehlende Daten"<br />

Me!Telefon.SetFocus<br />

Cancel = True<br />

» Nach Aktualisierung: Wird nach dem Speichern des Datensatzes ausgelöst.<br />

» Nach Eingabe: Wird nach dem Speichern eines neuen Datensatzes ausgelöst; gilt<br />

nicht <strong>für</strong> bestehende Datensätze.<br />

» Beim Anzeigen: Wird beim Wechseln beziehungsweise Speichern des Datensatzes<br />

auf ge rufen.<br />

Abbruch des Änderungsvorgangs<br />

Neben dem Speichern bietet sich nach Änderungen an Feldern eines Datensatzes auch<br />

die Möglichkeit, den Vorgang abzubrechen und die vorgenommenen Änderungen am<br />

aktuellen Datensatz zu verwerfen. Dies löst das Bei Rückgängig-Ereignis des Formulars<br />

aus. Das Ereignis kann durch Setzen des Cancel-Parameters auf den Wert True abgebrochen<br />

werden.


Löschen eines Datensatzes<br />

Ereignisse von Steuerelementen<br />

Das Löschen von Datensätzen ist keine triviale Geschichte. Wenn Sie auf eine Löschen-<br />

Schaltfläche klicken, ist der Datensatz noch lange nicht gelöscht.<br />

Zunächst wird die Anzeige aktualisiert, die auf einem temporären, zwischengespeicherten<br />

Datenbestand basiert, und dann noch abgewartet, ob die Ereignisse Bei<br />

Löschbestätigung oder Nach Löschbestätigung Änderungen bringen. Erst dann überträgt<br />

<strong>Access</strong> die Änderungen in die Tabellen der Datenbank.<br />

» Beim Löschen: Wird beim Aufrufen des Löschvorgangs und unmittelbar vor dem<br />

Löschen ausgelöst. Wenn mehrere Datensätze gleichzeitig gelöscht werden, wird<br />

dieses Ereignis <strong>für</strong> jeden Datensatz einmal aufgerufen.<br />

» Beim Anzeigen: Wird nach dem Löschen, aber vor dem Übernehmen der Änderungen<br />

in die Tabelle ausgelöst.<br />

» Bei Löschbestätigung: Wird nur ausgelöst, wenn <strong>Access</strong> eine Bestätigung der Datensatzänderung<br />

anzeigt – diese Option lässt sich im Dialog aus Abbildung 3.25 einstellen.<br />

Die Bestätigungsmeldung kann unterbunden und durch eine eigene Meldung<br />

ersetzt werden. Beispiel:<br />

If MsgBox("Möchten Sie den Datensatz wirklich löschen?", _<br />

End If<br />

vbExclamation + vbOKCancel, _<br />

"Löschbestätigung") = vbCancel Then<br />

Cancel = True<br />

Response = acDataErrContinue<br />

» Nach Löschbestätigung: Wird nur ausgelöst, wenn eine angezeigte Bestätigung einer<br />

Datensatzänderung auch bestätigt wurde beziehungsweise wenn der Löschvorgang<br />

nach einer benutzerdefinierten Meldung im Ereignis Bei Löschbestätigung nicht abgebrochen<br />

wurde.<br />

3.4 Ereignisse von Steuerelementen<br />

Neben dem Formular selbst lösen auch die Steuerelemente Ereignisse aus. Nachfolgend<br />

finden Sie einige wichtige Ereigniseigenschaften von Steuerelementen.<br />

Unterformulare<br />

Wenn Sie mit Unterformularen arbeiten, sollten Sie wissen, in welcher Reihenfolge die<br />

Ereignisse beim Öffnen und Schließen von Unterformularen in Bezug auf die Ereignisse<br />

des Hauptformulars ausgelöst werden.<br />

193


Kapitel 3 Formulare<br />

Abbildung 3.25: Aktivieren der Anzeige einer Meldung beim Ändern von Datensätzen<br />

Die Abfolge sieht folgendermaßen aus:<br />

» Unterformular Beim Öffnen<br />

» Unterformular Beim Laden<br />

» Unterformular Bei Größenänderung<br />

» Unterformular Beim Anzeigen<br />

» Hauptformular Beim Öffnen<br />

» Hauptformular Beim Laden<br />

» Hauptformular Bei Größenänderung<br />

» Hauptformular Bei Aktivierung<br />

» Hauptformular Beim Anzeigen<br />

Das Unterformular wird in der Tat komplett vor dem Hauptformular geladen. Lediglich<br />

das Filtern der Datensätze des Unterformulars in Abhängigkeit von den <strong>für</strong> das Hauptfor<br />

mular vorgesehenen Datensätzen erfolgt noch vor dem Beim Öffnen-Ereignis des Unter<br />

formulars.<br />

Beim Schließen des Formulars wird zuerst das Hauptformular und dann das Unter formu<br />

lar geschlossen:<br />

194


» Hauptformular Beim Entladen<br />

» Hauptformular Bei Deaktivierung<br />

» Hauptformular Beim Schließen<br />

» Unterformular Beim Entladen<br />

» Unterformular Beim Schließen<br />

Textfelder<br />

Ereignisse von Steuerelementen<br />

Beim Setzen der Einfügemarke in ein Textfeld, beim Ändern des Inhalts und beim anschließenden<br />

Verlassen treten die folgenden Ereignisse auf:<br />

» Beim Hingehen: Beim Eintreten in das Feld<br />

» Bei Fokuserhalt<br />

» Bei Geändert: Beim Ändern des ersten Zeichens. Stellt die Eigenschaft Dirty auf den<br />

Wert True ein.<br />

» Bei Änderung: Beim Ändern jedes Zeichens<br />

» Vor Aktualisierung: Bei jeder Aktion, die zum Verlassen des Feldes führt. Dieses<br />

Ereignis bietet die Möglichkeit, feldbezogene Validierungen durchzuführen und die<br />

Aktualisierung abzubrechen. Beispiel:<br />

If IsNumeric(Left(Me.Projekt, 1)) Then<br />

End If<br />

MsgBox "Der Projektname darf nicht mit einer Zahl " _<br />

& "beginnen.", vbOKOnly + vbExclamation, _<br />

"Fehlerhafte Eingabe"<br />

Cancel = True<br />

» Nach Aktualisierung: Nach dem Ereignis Vor Aktualsisierung, wenn dieses nicht abgebrochen<br />

wurde. Bietet die Möglichkeit, den eingegebenen Wert weiter zu verwerten<br />

oder zu ändern.<br />

» Beim Verlassen: Nach Abarbeitung der Ereignisse Vor Aktualisierung und Nach<br />

Aktualisierung<br />

» Bei Fokusverlust: Beim Setzen des Fokus auf ein anderes Steuerelement<br />

In Zusammenhang mit diesen Ereignissen sind drei Eigenschaften von Textfeldern wichtig:<br />

Value, OldValue und Text. OldValue enthält während des ganzen Änderungsvorgangs<br />

den vorherigen Wert des Feldes. Text enthält den aktuell im Textfeld angezeigten Wert<br />

und Value den alten Wert, bis es mit dem Auslösen des Ereignisses Vor Aktualisierung<br />

195


Kapitel 3 Formulare<br />

den aktuellen Inhalt des Feldes beziehungsweise der Eigenschaft Text zugewiesen bekommt.<br />

Kombinationsfelder<br />

Das Auswählen eines Eintrags und das anschließende Verlassen eines Kombinations feldes<br />

löst die folgenden Ereignisse aus (wenn dieses noch nicht den Fokus hat).<br />

In runden Klammern finden Sie Ereignisse, die nur bei der manuellen Eingabe von<br />

Zeichen ausgeführt werden, in eckigen Klammern die Ereignisse, die nur bei manueller<br />

Eingabe nicht vorhandener Listeneinträge ausgelöst werden.<br />

» Beim Hingehen<br />

» Bei Fokuserhalt<br />

» Bei Änderung: Beim Auswählen eines Eintrags oder bei der ersten Eingabe eines<br />

Zeichens<br />

» (Bei Geändert: Bei der manuellen Eingabe von Zeichen)<br />

» [Bei nicht in Liste: Nach der manuellen Eingabe eines Wertes, der nicht in der Liste<br />

enthalten ist. Hier können Sie eine Prozedur hinterlegen, die nicht in der Liste enthaltene<br />

Einträge in der zugrunde liegenden Datensatzherkunft anlegt und den neuen<br />

Wert im Kombinationsfeld festlegt. In diesem Fall folgen die übrigen Ereignisse,<br />

sonst wird der Änderungsvorgang unterbrochen.]<br />

» Vor Aktualisierung<br />

» Nach Aktualisierung<br />

» Beim Klicken<br />

» Bei Geändert<br />

» Beim Verlassen<br />

» Bei Fokusverlust<br />

Weitere Steuerelemente<br />

Die übrigen Steuerelemente wie Listenfelder, Kontrollkästchen oder Optionsgruppen<br />

haben je nach Typ unterschiedliche Ereignisse. Nachdem Sie erfahren haben, wie Sie<br />

die Abfolge der Ereignisse von Formularen und Steuerelementen ermitteln, soll an dieser<br />

Stelle nicht weiter auf die Ereignisse der noch nicht besprochenen Steuerelemente<br />

und ihre Abfolge eingegangen werden. Diesbezügliche Unklarheiten lassen sich leicht<br />

experimentell beseitigen, indem Sie die betroffenen Ereignisse mit entsprechenden<br />

Prozeduren ausstatten.<br />

196


Abbildung verschiedener Beziehungsarten<br />

3.5 Abbildung verschiedener Beziehungsarten<br />

Die vorhandene Fachliteratur beschränkt sich weitgehend auf die Darstellung von Daten<br />

aus einzelnen oder aus per 1:n-Beziehung verknüpften Tabellen. Daher erhalten Sie in<br />

diesem Abschnitt des Kapitels einen Überblick über die Realisierung der verschiedenen<br />

Beziehungsarten in Formularen.<br />

3.5.1 Einfache Daten in der Detailansicht<br />

Die Daten aus einzelnen Tabellen oder einfachen Abfragen lassen sich mit wenigen<br />

Schritten in einem Formular darstellen. Üblicherweise sind hier zwei Darstellungen gefragt:<br />

Detailansichten oder Übersichtslisten.<br />

Mit »einfachen Abfragen« sind hier Abfragen gemeint, deren Daten quasi wie eine<br />

Tabelle gehandhabt werden – also etwa Abfragen, die zwei per 1:1-Beziehung verknüpfte<br />

Tabellen darstellen oder die eine einzelne Tabelle mit einem oder mehreren Lookup-<br />

Feldern beinhalten.<br />

Detailansichten dienen dazu, die sich oft über viele Felder erstreckenden Daten in einer<br />

Form anzuzeigen, die die Daten einerseits vernünftig strukturiert und andererseits eine<br />

komfortable Bearbeitung ermöglicht.<br />

Übersichtslisten dienen selten dazu, die Daten kompletter Tabellen anzuzeigen – es sei<br />

denn, diese enthalten nicht besonders viele Felder.<br />

Wenn der Benutzer scrollen muss, um alle Felder eines Formulars sehen zu können, ist<br />

die Menge der angezeigten Felder zu groß.<br />

Meist dienen solche Übersichtslisten dazu, wichtige Informationen zu den angezeigten<br />

Datensätzen sowie die Anzeige einer Detailansicht zu bieten.<br />

Übersichtlisten gibt es in drei Formen: als Endlosformular, Datenblattansicht oder in<br />

geteilten Formularen.<br />

Detailansicht einfacher Daten in Formularen<br />

Die Erstellung einer Detailansicht einfacher Daten läuft in folgenden Schritten ab:<br />

» Anlegen eines neuen, leeren Formulars<br />

» Festlegen der Datensatzquelle im Eigenschaftsfenster<br />

» Einfügen der benötigten Felder (Ribboneintrag Entwurf|Tools|Vorhandene Felder hinzufügen,<br />

siehe Abbildung 3.26)<br />

» Anpassen und Ausrichten der Felder und Anpassen der Beschriftungen<br />

197


Kapitel 3 Formulare<br />

Das ist im Prinzip einfach. Wichtig ist lediglich, dass Sie beim Anlegen der Datensatzquelle<br />

in Schritt 2 darauf achten, dass die Datensatzquelle nur die Felder enthält, die Sie auch<br />

wirklich benötigen.<br />

Abbildung 3.26: Das Hinzufügen der Einträge der Feldliste in das Formular kann durch Ziehen<br />

mit der Maus erfolgen<br />

Muss man in Detailansichten blättern können?<br />

Die oben erstellte Detailansicht enthält einige Elemente, die die Navigation in den<br />

Datensätzen des Formulars erleichtern:<br />

» Bildlaufleisten<br />

» Datensatzmarkierer<br />

» Navigationsschaltflächen<br />

Das Formular aus Abbildung 3.27 enthält all diese Elemente. Die Frage ist nun: Welche<br />

davon benötigen Sie in einer Detailansicht?<br />

Was zu einer anderen Frage führt: Was macht der Benutzer eigentlich mit dieser<br />

Detailansicht?<br />

Nun, er soll die Details eines Datensatzes betrachten und gegebenenfalls die enthaltenen<br />

Daten ändern beziehungs weise neue Datensätze anlegen oder bestehende löschen<br />

können.<br />

Soll er mit der Na vi gationsleiste arbeiten, um innerhalb der Datensätze zu navigieren?<br />

Seit <strong>Access</strong> 2007 vielleicht: Immerhin befindet sich dort ein Suchen-Feld.<br />

198


Abbildung verschiedener Beziehungsarten<br />

Optimal wäre es trotzdem, wenn der Benutzer die oben genannten Elemente gar nicht<br />

be nötigt, sondern mit anderen Mitteln zu einem gesuchten oder einem neuen Datensatz<br />

ge langt – immerhin ist nicht jeder Benutzer mit diesen Steuerelementen vertraut und<br />

in tuitiv zu bedienen sind sie auch nicht unbedingt.<br />

Abbildung 3.27: Die Formularansicht des Detailformulars<br />

Angenommen, der Benutzer benötigt diese zusätzlichen Elemente gar nicht: Dann zeigen<br />

Sie diese doch gar nicht an.<br />

Drei Mausklicks im Eigenschaftsfenster der Entwurfsansicht des Formulars, einer noch,<br />

wenn das Formular beim Öffnen zentriert angezeigt werden soll, was immer Sinn macht,<br />

und noch eine Beschriftung hinzufügen – blitzschnell sieht das Eigenschaftsfenster wie<br />

in Abbildung 3.28 und das Formular wie in Abbildung 3.29 aus.<br />

Abbildung 3.28: Eigenschaften eines Formulars …<br />

199


Kapitel 3 Formulare<br />

Abbildung 3.29: … ohne unnötigen Schnickschnack (frmKontakteDetailansicht)<br />

Navigation und Aktion in Detailformularen<br />

Nun fehlen noch die Möglichkeiten zum Auswählen eines Datensatzes, zum Anlegen<br />

eines neuen und Löschen des bestehenden Datensatzes. Nur: Müssen diese Elemente<br />

auf das Detailformular? Das ist sicher Geschmackssache, aber Sie sollten sich <strong>für</strong> eine<br />

der folgenden beiden Möglichkeiten entscheiden: Entweder Sie verwenden nur eine OK­<br />

und eine Abbrechen-Schaltfläche oder Sie fügen neben einer Möglichkeit zur Auswahl<br />

von Datensätzen auch noch je eine Schaltfläche zum Löschen und zum Anlegen von<br />

Datensätzen hinzu.<br />

Nachfolgend finden Sie eine Beschreibung der ersten Variante. Die Schaltflächen zum<br />

Auswählen, Anlegen und Löschen eines Datensatzes werden in Zusammenhang mit den<br />

unten beschriebenen Übersichtsformularen in der Endlos- und der Datenblattansicht<br />

beschrieben.<br />

OK- und Abbrechen-Schaltflächen<br />

Diese beiden Schaltflächen lösen jeweils Prozeduren mit nur einer Zeile VBA-Code aus.<br />

Legen Sie die beiden Schaltflächen an, stellen Sie die Eigenschaft Beschriftung auf die<br />

Werte OK und Abbrechen und die Eigenschaft Name auf die Werte cmdOK und cmdAbbrechen<br />

ein. Legen Sie dann <strong>für</strong> beide je eine Prozedur <strong>für</strong> die Ereigniseigenschaft Beim<br />

Klicken an (siehe Abbildung 3.30):<br />

200<br />

Private Sub cmdOK_Click()<br />

End Sub<br />

DoCmd.Close acForm, Me.Name<br />

Private Sub cmdAbbrechen_Click()<br />

End Sub<br />

Me.Undo<br />

DoCmd.Close acForm, Me.Name<br />

Listing 3.4: Schließen mit und ohne Übernahme der Änderungen


Abbildung verschiedener Beziehungsarten<br />

Die OK-Schaltfläche sorgt in der Regel nur da<strong>für</strong>, dass ein Formular geschlossen wird.<br />

Es gibt aber auch Ausnahmen: Wenn ein Formular geöffnet wurde, um Daten zu ermitteln,<br />

die in der aufrufenden Routine weiter verarbeitet werden sollen, müssen diese<br />

vor dem Schließen natürlich erst abgefragt werden. Wie dies funktioniert, erfahren Sie<br />

weiter unten in »Von Formular zu Formular« ab Seite 235.<br />

Abbildung 3.30: Anlegen der Beim Klicken-Ereigniseigenschaft <strong>für</strong> eine Schaltfläche (frmDetailansicht)<br />

3.5.2 Einfache Daten in der Übersicht mit Endlosformularen<br />

Ein Formular zur Anzeige der Übersicht von Datensätzen enthält die gleiche Daten satzquelle<br />

wie das Formular zur Anzeige der Detailansicht, in der Regel jedoch mit weniger<br />

Feldern. Für Tabellen, die so wenige Felder enthalten, dass diese leicht neben einander<br />

angezeigt werden können, braucht prinzipiell gar keine Detailansicht erstellt zu werden.<br />

Die Felder sind meist wie in Abbildung 3.31 nebeneinander angeordnet. In der Abbildung<br />

finden Sie direkt die Elemente zum Steuern von Aktionen wie Neu anlegen, Löschen und<br />

Bearbeiten von Datensätzen.<br />

Abbildung 3.31: Entwurfsansicht eines Übersichtsformulars (frmKontakteEndlosformular)<br />

201


Kapitel 3 Formulare<br />

Die OK-Schaltfläche dient wie die Schaltfläche des zuvor beschriebenen Formulars lediglich<br />

dem Schließen des Formulars.<br />

Die Neu-Schaltfläche soll das oben beschriebene Formular zur detaillierten Ansicht<br />

eines Datensatzes öffnen und einen leeren Datensatz anzeigen. Da<strong>für</strong> ist die folgende<br />

Ereigniseigenschaft verantwortlich, die durch das Beim Klicken-Ereignis der Schaltfläche<br />

ausgelöst wird.<br />

Formular mit leerem Datensatz öffnen<br />

Die Routine verwendet die OpenForm-Methode des DoCmd-Objekts zum Öffnen des<br />

For mu lars frmKontakteDetailansicht. Der Parameter DataMode erhält dabei den Wert<br />

acForm Add, damit das Formular beim Öffnen direkt einen leeren Datensatz anzeigt.<br />

Formular als modalen Dialog öffnen<br />

Der Wert des Parameters WindowMode liefert die Voraussetzung da<strong>für</strong>, dass das aufrufende<br />

Formular – die Übersicht – den angezeigten Datenbestand direkt nach dem<br />

Eingeben des neuen Datensatzes und Schließen des Detailformulars aktualisieren kann<br />

(siehe Abbildung 3.32). Durch den Wert acDialog wird das Formular frmKontakteDetailansicht<br />

als modaler Dialog geöffnet, was bedeutet, dass innerhalb der <strong>Access</strong>-Anwendung<br />

nichts mehr geht, solange dieses Formular geöffnet ist – selbst der aufrufende Code<br />

wird währenddessen angehalten. Erst wenn das Formular den Fokus verliert, also geschlossen<br />

oder unsichtbar gemacht wird, läuft die aufrufende Routine weiter. Dadurch<br />

kann die Übersicht direkt nach dem Schließen der Detailansicht aktualisiert werden.<br />

202<br />

Private Sub cmdNeu_Click()<br />

End Sub<br />

DoCmd.OpenForm "frmKontakteDetailansicht", DataMode:=acFormAdd, _<br />

WindowMode:=acDialog<br />

Me.Requery<br />

Listing 3.5: Aufrufen des Detailformulars zum Anlegen eines neuen Datensatzes<br />

Löschen von Datensätzen<br />

Die nächste Schaltfläche dient dem Löschen des aktuell markierten Datensatzes. Die<br />

ein fachste Variante des benötigten Codes sieht wie folgt aus:<br />

Private Sub cmdLoeschen_Click()<br />

End Sub<br />

On Error Resume Next<br />

DoCmd.RunCommand acCmdDeleteRecord<br />

Listing 3.6: Löschen eines Datensatzes


Abbildung verschiedener Beziehungsarten<br />

Abbildung 3.32: Öffnen eines Eingabeformulars vom Übersichtsformular aus (frmKontakte­<br />

Endlos formular, frmKontakteDetailansicht)<br />

Die Routine verwendet die RunCommand-Methode des DoCmd-Objekts mit dem Pa rame<br />

ter acCmdDeleteRecord, um den aktuell markierten Datensatz zu löschen.<br />

Da hier eigent lich nur der Fehler auftreten kann, dass kein Datensatz markiert ist, verhindern<br />

Sie diesen mit der On Error Resume Next-Anweisung im Vorhinein.<br />

Alternativ können Sie den ak tuellen Datensatz im Übrigen auch mit folgender Anweisung<br />

löschen:<br />

Me.Recordset.Delete<br />

Etwas weniger rudimentär gestalten Sie das Abfangen dieses Fehlers, wenn Sie die On<br />

Error Resume Next-Anweisung durch folgenden Code ersetzen. Dieser prüft, ob aktuell<br />

kein Datensatz ausgewählt ist, und bricht in diesem Fall mit einer entsprechenden<br />

Meldung ab:<br />

If IsNull(Me!KontaktID) Then<br />

End If<br />

MsgBox "Bitte wählen Sie zunächst einen Datensatz aus."<br />

Exit Sub<br />

Sollte die Anzeige von Warnmeldungen bei Datensatzänderungen aktiviert sein (siehe<br />

Abbildung 3.25), erscheint hier allerdings eine unschöne Meldung (siehe Abbildung 3.33).<br />

Abbildung 3.33: <strong>Access</strong>-Meldung beim Löschen eines Datensatzes<br />

203


Kapitel 3 Formulare<br />

Wenn Sie diese Meldung durch eine eigene Meldung ersetzen möchten, verwenden Sie<br />

die folgende Prozedur:<br />

204<br />

Private Sub cmdLoeschen_Click()<br />

On Error Resume Next<br />

DoCmd.SetWarnings False<br />

If MsgBox("Möchten Sie den Kontakt '" & Me!Vorname & " " _<br />

End If<br />

End Sub<br />

& Me!Nachname & "' wirklich löschen?", vbYesNo + vbExclamation, _<br />

"Löschbestätigung") = vbYes Then<br />

DoCmd.RunCommand acCmdDeleteRecord<br />

DoCmd.SetWarnings True<br />

Listing 3.7: Löschen eines Datensatzes mit benutzerdefinierter Warnmeldung<br />

Die SetWarnings-Methode des DoCmd-Objekts deaktiviert dabei die Anzeige der eingebauten<br />

Meldung und aktiviert diese anschließend wieder.<br />

Anzeigen der Details zu einem Datensatz<br />

Die letzte Schaltfläche des Übersichtsformulars dient dem Anzeigen der Detailansicht<br />

des ausgewählten Datensatzes. Die dadurch ausgelöste Routine prüft zunächst, ob ein<br />

Datensatz markiert ist.<br />

Falls ja, öffnet sie das Detailformular wiederum mit der DoCmd.OpenForm-Methode. In<br />

diesem Fall kommt <strong>für</strong> den Parameter DataMode jedoch der Wert acFormEdit zum Zuge<br />

und der anzuzeigende Datensatz wird mit dem Parameter WhereCondition festgelegt.<br />

Dieser erwartet als Wert – wie der Name schon sagt – eine Bedingung, die den anzuzeigenden<br />

Datensatz festlegt. Hier lautet die Bedingung, dass der Wert des Feldes<br />

KontaktID dem des ausgewählten Datensatzes im Übersichtsformular entsprechen<br />

muss.<br />

Private Sub cmdBearbeiten_Click()<br />

If IsNull(Me!KontaktID) Then<br />

End If<br />

End Sub<br />

MsgBox "Bitte wählen Sie zunächst einen Datensatz aus."<br />

Exit Sub<br />

DoCmd.OpenForm "frmKontakteDetailansicht", DataMode:=acFormEdit, _<br />

WindowMode:=acDialog, WhereCondition:="KontaktID = " & Me!KontaktID<br />

Listing 3.8: Öffnen eines Datensatzes in der Detailansicht


Abbildung verschiedener Beziehungsarten<br />

3.5.3 Einfache Daten in der Übersicht als Datenblatt<br />

Neben der Endlosansicht ist auch die Datenblattansicht von Formularen <strong>für</strong> die Ver wendung<br />

als Übersichtsformular geeignet. Die Datenblattansicht hat den Vorteil, dass der<br />

Be nutzer dort selbst die Spaltenbreite einstellen kann – das ist bei Endlosformularen<br />

mit unter ein Problem, wenn wenig Platz vorhanden oder die maximale Länge der anzuzeigenden<br />

Zeichenketten nicht bekannt ist. Auch die Performance ist in dieser Ansicht<br />

besser. Allerdings hat auch das Endlosformular Vorteile. Warum das so ist, erfahren Sie,<br />

wenn Sie das Formular frmKontakteUebersicht einmal in der Datenblattansicht anzeigen<br />

(siehe Abbildung 3.34). Das Datenblatt ist zwar als Übersicht akzeptabel, aber Kopf- und<br />

Fußbereich des Formulars sind völlig verschwunden. Tatsache ist: Beide können in der<br />

Datenblattansicht schlicht nicht verwendet werden.<br />

Abbildung 3.34: Übersichtsformular in der Datenblattansicht (sfmKontakteDatenblattansicht)<br />

Datenblattansicht im Unterformular<br />

Wer dennoch ein Formular in der Datenblattansicht verwenden möchte, setzt dies als<br />

Un terformular in ein Hauptformular ein, das die Navigationselemente zum Anlegen, Löschen<br />

und Bearbeiten der Einträge bereitstellt. Das Unterformular enthält einfach nur<br />

die Felder, die in der Datenblattansicht angezeigt werden sollen (siehe Abbildung 3.35).<br />

Abbildung 3.35: Der Entwurf eines Formulars in der Datenblattansicht braucht keine besonderen<br />

optischen Ansprüche zu erfüllen.<br />

Es gibt lediglich drei Punkte, auf die Sie achten sollten:<br />

» Stellen Sie die Eigenschaft Standardansicht auf Datenblatt ein.<br />

205


Kapitel 3 Formulare<br />

» Versehen Sie die Bezeichnungsfelder der einzelnen Steuerelemente mit ordentlichen<br />

Beschriftungen, da diese später als Feldüberschriften dienen.<br />

» Sorgen Sie <strong>für</strong> die richtige Aktivierreihenfolge, da diese festlegt, in welcher Rei henfol<br />

ge die einzelnen Felder später angezeigt werden (Dialog öffnen über den Eintrag<br />

Aktivierreihenfolge des Steuerelement-Kontextmenüs).<br />

Hauptformular als Container <strong>für</strong> ein Unterformular in der<br />

Datenblattansicht<br />

Bereiten Sie dann das Hauptformular vor. Dazu legen Sie ein neues, leeres Formular an<br />

und kopieren die bereits erstellten Schaltflächen des Formulars frmKontakteEndlosformu<br />

lar in den Detailbereich des neuen Formulars.<br />

Lassen Sie dabei ein wenig Platz nach oben. Ziehen Sie dann das neue Unterformular<br />

aus dem Navigationsbereich in den De tail bereich des Formulars (siehe Abbildung 3.36).<br />

Abbildung 3.36: Ziehen des Unterformulars aus dem Navigationsbereich in den Detailbereich des<br />

neuen Hauptformulars<br />

Anpassen der Datenblattansicht eines Formulars<br />

Das Erste, was Ihnen beim Blick auf die Formularansicht auffallen wird, ist, dass standardmäßig<br />

fast alle Steuerelemente in Formularen in Schriftgröße 11 dargestellt werden,<br />

das Unterformular in der Datenblattansicht aber mit Schriftgröße 10 aufwartet.<br />

Außerdem ist die Breite der Felder des Unterformulars natürlich nicht den Inhalten angepasst.<br />

Beides holen Sie nach – allerdings nicht in der Entwurfsansicht, sondern in<br />

der Formularansicht des Unterformulars. Dieses müssen Sie da<strong>für</strong> allerdings noch ein-<br />

206


Abbildung verschiedener Beziehungsarten<br />

mal separat öffnen. Wenn Tabellen, Abfragen und Formulare in der Datenblattansicht<br />

grundsätzlich in einer anderen Schriftgröße oder in anderem Layout geöffnet werden<br />

sollen, können Sie die gewünschten Einstellungen in den <strong>Access</strong>-Optionen im Bereich<br />

Datenblatt anpassen. Dort finden Sie auch die neue Option zum Einstellen einer alternativen<br />

Hintergrundfarbe.<br />

Wenn Sie die dortigen Einstellungen je Formular individuell vergeben möchten, können<br />

Sie dies entweder mit dem Ribbon-Eintrag Start|Textformatierung|Alternative Zeilenfarbe<br />

im Entwurf oder Datenblatt|Formatierung|Alternative Zeilenfarbe in der Datenblattansicht<br />

beziehungsweise per VBA durchführen.<br />

Dazu hinterlegen Sie die folgende Prozedur <strong>für</strong> die Er eig nis ei gen schaft Beim Laden:<br />

Private Sub Form_Load()<br />

End Sub<br />

Me.DatasheetAlternateBackColor = &HDDDDFF<br />

Listing 3.9: Individuelles Anpassen der alternativen Hintergrundfarbe eines Formulars in der<br />

Datenblattansicht<br />

Die Farbe können Sie statt im hexadezimalen Format auch mit der RGB-Funktion angeben<br />

– zum Beispiel so:<br />

Me.DatasheetAlternateBackColor=RGB(255,240,204)<br />

Wenn Sie die Farbe des aktuell angezeigten Datenblatts – unabhängig davon, ob es sich<br />

um eine Tabelle, Abfrage oder ein Formular handelt – ändern möchten, können Sie diesen<br />

Aufruf verwenden (beispielsweise im Direktfenster):<br />

Screen.ActiveDatasheet.DatasheetAlternateBackColor=RGB(255,240,204)<br />

Offensichtlich lässt sich die alternierende Farbe jedoch nicht auf eine der Design-<br />

Farben einstellen – ein Wert wie 1 oder 2 wird als Schwarz interpretiert. Die übrigen<br />

Einstellungen des Optionen-Dialogs können Sie ebenfalls auf diese Weise vor nehmen.<br />

Die Namen der passenden Eigenschaften beginnen alle mit »Datasheet...«. Die Spaltenbreiten<br />

bändigen Sie durch Ziehen des vertikalen Trennstriches zwischen zwei Spalten<br />

und die Schriftgröße passen Sie in einem Dialog an, den Sie per Kon text me nü der<br />

Titelleiste des Formulars öffnen. Solche Änderungen speichert der Benutzer etwa<br />

mit Strg + S dauerhaft. Nachdem Sie im Hauptformular auch noch die Bildlaufleisten,<br />

Datensatzmarkierer, Na vi gationsschaltflächen und Trennlinien deaktiviert haben, sieht<br />

das Formular schon viel besser aus (siehe Abbildung 3.37).<br />

Bezug auf Steuerelemente im Unterformular<br />

Nun fehlen noch die Funktionen der Schaltflächen. Da sich die betroffenen Daten nun<br />

in einem Unterformular befinden, können Sie nicht ohne Weiteres die Prozeduren aus<br />

207


Kapitel 3 Formulare<br />

dem Formular frmKontakteEndlosformular einsetzen – aber fast. Die Prozedur hinter der<br />

Schaltfläche OK können Sie komplett übernehmen.<br />

Abbildung 3.37: Datenblattansicht als Übersicht (frmKontakteDatenblattansicht)<br />

Die Prozeduren zum Anlegen, Löschen und Bearbeiten des aktuell markierten<br />

Datensatzes sehen nun folgendermaßen aus:<br />

208<br />

Private Sub cmdBearbeiten_Click()<br />

If IsNull(Me!sfmKontakteDatenblatt!KontaktID) Then<br />

End If<br />

End Sub<br />

MsgBox "Bitte wählen Sie zunächst einen Datensatz aus."<br />

Exit Sub<br />

DoCmd.OpenForm "frmKontakteDetailansicht", DataMode:=acFormEdit, _<br />

WindowMode:=acDialog, WhereCondition:="KontaktID = " _<br />

& Me!sfmKontakteDatenblatt!KontaktID<br />

Me.Requery<br />

Private Sub cmdLoeschen_Click()<br />

If IsNull(Me!sfmKontakteDatenblatt.Form!KontaktID) Then<br />

End If<br />

MsgBox "Bitte wählen Sie zunächst einen Datensatz aus."<br />

Exit Sub<br />

DoCmd.SetWarnings False<br />

If MsgBox("Möchten Sie den Kontakt '" _<br />

End If<br />

End Sub<br />

& Me!sfmKontakteDatenblatt!Vorname & " " _<br />

& Me!sfmKontakteDatenblatt!Nachname & "' wirklich löschen?", _<br />

vbYesNo + vbExclamation, "Löschbestätigung") = vbYes Then<br />

Me!sfmKontakteDatenblatt.SetFocus<br />

DoCmd.RunCommand acCmdDeleteRecord<br />

DoCmd.SetWarnings True


Private Sub cmdNeu_Click()<br />

End Sub<br />

Abbildung verschiedener Beziehungsarten<br />

DoCmd.OpenForm "frmKontakteDetailansicht", DataMode:=acFormAdd, _<br />

WindowMode:=acDialog<br />

Me!sfmKontakteDatenblatt.Requery<br />

Listing 3.10: Änderungen an den Prozeduren zum Löschen und Bearbeiten von Datensätzen im<br />

Detailformular<br />

Die Änderungen gegenüber den Prozeduren des Formulars mit der Endlosansicht sind<br />

fett gedruckt. Dabei wird jeweils auf die im Unterformular befindlichen Steuerelemente<br />

Bezug genommen, die im Formular frmKontakteEndlosformular alle im gleichen<br />

Formular wie die Schaltflächen angesiedelt waren. Bei den Steuerelementen wird statt<br />

Me!KontaktID etwa Me!sfmKontakteDatenblatt!Kontakt ID verwendet. Stattdessen könnte<br />

man auch ausführlicher schreiben:<br />

oder<br />

Me!sfmKontakteDatenblatt.Form!KontaktID<br />

Me.Controls("sfmKontakteDatenblatt").Form.Controls("KontaktID")<br />

Vorteil Datenblattansicht<br />

Ein klarer Vorteil der hier konstruierten erweiterten Datenblattansicht gegenüber der<br />

Endlosansicht ist, dass nach dem Bearbeiten von Daten im Detailformular und der<br />

Aktualisierung der überarbeiteten Daten im Übersichtsformular nicht der Datensatz<br />

ge wechselt wird. Im Formular frmKontakteEndlosformular springt der Datensatzzeiger<br />

nach dem Aktualisieren mit der Requery-Methode immer wieder auf den ersten Da tensatz.<br />

Das ist in der Datenblattansicht nicht der Fall. Hier ist darüber hinaus nur eine<br />

Ak tualisierung per Requery nach dem Anlegen eines neuen Datensatzes erforderlich,<br />

im Detailformular vorgenommene Änderungen werden automatisch übernommen. Das<br />

Gleiche gilt natürlich auch <strong>für</strong> die Datenblattansicht.<br />

3.5.4 Daten in der Übersicht als Listenfeld<br />

Die dritte Möglichkeit der Darstellung von Daten in der Übersicht ist die Verwendung<br />

eines Listenfeldes. Wie Sie bemerkt haben werden, kann man mit den beiden zuvor beschriebenen<br />

Varianten auch bereits in der Übersicht Daten verändern, wenn man nicht<br />

gerade die einzelnen Steuerelemente sperrt oder die Eigenschaft RecordsetTyp des<br />

Formulars auf Snapshot einstellt. Hier bringt das Listenfeld Vorteile: Es ist keine direkte<br />

Bearbeitung möglich und außerdem scheint das Markieren zu bearbeitender oder zu<br />

löschender Datensätze intuitiver zu sein. Davon abgesehen kann man den Doppelklick<br />

<strong>für</strong> die schnelle Anzeige des Detailformulars auslegen.<br />

209


Kapitel 3 Formulare<br />

Mit den folgenden Schritten haben Sie rasch ein Listenfeld als Übersicht erstellt:<br />

» Legen Sie ein Listenfeld in einem leeren Formular an und vergrößern Sie es auf etwa<br />

12 cm Breite.<br />

» Stellen Sie die Eigenschaft Name auf lstKontakte ein.<br />

» Legen Sie als Datensatzherkunft eine Abfrage an, die auf der Tabelle tblKontakte basiert<br />

und nur die wichtigsten Felder KontaktID, Nachname, Vorname, Telefonnummer<br />

und EMail enthält. Gegebenenfalls können Sie auch eine Sortierung festlegen – etwa<br />

nach dem Nachnamen.<br />

» Stellen Sie die Eigenschaften Spaltenanzahl und Spaltenbreiten auf die Werte 5 und<br />

0cm;2cm:2cm;3cm ein. Dadurch wird die gebundene Spalte KontaktID ausgeblendet<br />

(Primärschlüsselfelder soll man, wie oben bereits erläutert, nur anzeigen, wenn<br />

diese geschäftliche Informationen enthalten). Das Feld muss aber dennoch in der<br />

Abfrage und auch im Listenfeld enthalten sein, da sonst kein Bezug zum entsprechenden<br />

Datensatz hergestellt werden kann. Die weiteren Spalten werden in der<br />

entsprechenden Breite angezeigt und die letzte Spalte füllt den restlichen Platz<br />

aus.<br />

» Je nach Geschmack stellen Sie die Eigenschaft Spaltenüberschriften auf Ja ein oder<br />

legen selbst Beschriftungsfelder oberhalb des Listenfeldes an. Bedenken Sie, dass<br />

die Spaltenüberschriften – falls angezeigt – eine Zeile belegen und damit der Index<br />

des ersten angezeigten Datensatzes nicht 0, sondern 1 ist.<br />

Wenn Sie noch die obligatorischen Schaltflächen wie in den beiden vorherigen Bei spie len<br />

hinzufügen und die Eigenschaften Bildlaufleisten, Datensatzmarkierer, Na vi ga tions schaltflächen<br />

und Trennlinien auf Nein einstellen, sieht das Formular wie in Abbildung 3.38 aus.<br />

Abbildung 3.38: Übersicht per Listenfeld (frmKontakteListenfeld)<br />

Nun fehlen noch die dem Listenfeld angepassten Prozeduren zum Anlegen, Löschen<br />

und Bearbeiten des aktuell ausgewählten Datensatzes. Hierbei ist zu beachten, dass<br />

nach Änderungen immer die Requery-Methode des Listenfeld-Steuerelements ausgeführt<br />

werden muss.<br />

210


Abbildung verschiedener Beziehungsarten<br />

Um den Wert des Feldes KontaktID <strong>für</strong> den aktuellen Listenfeldeintrag zu ermitteln, lesen<br />

Sie einfach den Wert des Listenfeldes aus. Das Feld KontaktID ist das gebundene<br />

Feld der Datensatzherkunft und dadurch mit dem Wert des Listenfeldes identisch. Eine<br />

weitere Änderung ist zum Löschen des markierten Datensatzes notwendig: Mit DoCmd.<br />

RunCommand acCmdDeleteRecord ist einem Eintrag im Listenfeld nicht bei zu kom men,<br />

hier müssen Sie direkt mit einer DELETE-Anweisung auf die zugrunde liegende Ta belle<br />

zugreifen.<br />

Private Sub cmdBearbeiten_Click()<br />

If IsNull(Me!lstKontakte) Then<br />

End If<br />

End Sub<br />

MsgBox "Bitte wählen Sie zunächst einen Datensatz aus."<br />

Exit Sub<br />

DoCmd.OpenForm "frmKontakteDetailansicht", DataMode:=acFormEdit, _<br />

WindowMode:=acDialog, WhereCondition:="KontaktID = " _<br />

& Me!lstKontakte<br />

Me!lstKontakte.Requery<br />

Private Sub cmdLoeschen_Click()<br />

If IsNull(Me!lstKontakte) Then<br />

End If<br />

MsgBox "Bitte wählen Sie zunächst einen Datensatz aus."<br />

Exit Sub<br />

DoCmd.SetWarnings False<br />

If MsgBox("Möchten Sie den Kontakt '" & Me!lstKontakte & " " _<br />

End If<br />

End Sub<br />

& Me!lstKontakte & "' wirklich löschen?", _<br />

vbYesNo + vbExclamation, "Löschbestätigung") = vbYes Then<br />

CurrentDb.Execute "DELETE FROM tblKontakte WHERE KontaktID = " _<br />

& Me!lstKontakte<br />

Me!lstKontakte.Requery<br />

DoCmd.SetWarnings True<br />

Private Sub cmdNeu_Click()<br />

End Sub<br />

DoCmd.OpenForm "frmKontakteDetailansicht", DataMode:=acFormAdd, _<br />

WindowMode:=acDialog<br />

Me!lstKontakte.Requery<br />

Listing 3.11: Anlegen, löschen und bearbeiten von Datensätzen eines Listenfeldes<br />

211


Kapitel 3 Formulare<br />

3.5.5 1:1-Beziehungen<br />

Die Daten aus 1:1-Beziehungen lassen sich genau wie die oben beschriebenen »einfachen<br />

Daten« in Formularen abbilden. Sie können eine Abfrage über die an der 1:1-Beziehung<br />

beteiligten Tabellen erstellen und genau die gleichen Formulare wie oben verwenden. Sie<br />

können aber auch beispielsweise im Übersichtsformular nur die Daten der einen Seite der<br />

Beziehung anzeigen und die Details erst im Detailformular offenbaren.<br />

Einen kleinen Trick müssen Sie anwenden, wenn Sie sicherstellen wollen, dass auch in<br />

der Erweiterungstabelle zu jedem Datensatz der Haupttabelle ein passender Datensatz<br />

angelegt wird – auch ohne dass der Benutzer explizit Daten <strong>für</strong> die Erweiterungstabelle<br />

eingibt. Dazu legen Sie eine kleine Prozedur an, die durch die Ereigniseigenschaft Vor<br />

Aktualisierung des Formulars ausgelöst wird. Dieses Ereignis stellt einfach den Wert des<br />

Verknüpfungsfeldes der Erweiterungstabelle auf den Wert des Primärschlüsselfeldes der<br />

Haupttabelle ein:<br />

212<br />

Private Sub Form_BeforeUpdate(Cancel As Integer)<br />

End Sub<br />

Me!tblAngestellte_PersonID = Me!tblPersonen_PersonID<br />

Listing 3.12: Synchronisieren von Haupt- und Erweiterungstabelle im Formular<br />

Das Beispielformular finden Sie nicht in der Datenbank zu diesem Kapitel, sondern in<br />

der Beispieldatenbank Abfragen.accdb unter dem Formularnamen frmAngestellte.<br />

3.5.6 n:1-Beziehungen<br />

n:1-Beziehungen bestehen aus einer Tabelle, von der ein Feld in Form einer anderen<br />

Tabelle ausgelagert wurde – das einfachste Beispiel ist wohl das Feld Anrede in einer<br />

Kontakt-, Adress- oder Mitarbeitertabelle. Die Darstellung und Auswahl der Daten aus<br />

der Mastertabelle erfolgt im Formular über ein Kombinationsfeld.<br />

Wer gute Vorarbeit leistet und per Nachschlage-Assistent oder von Hand bereits beim<br />

Da tenentwurf da<strong>für</strong> sorgt, dass die Daten aus verknüpften Tabellen in einem Kom bina<br />

tionsfeld ausgewählt werden können, hat beim Erstellen eines Formulars <strong>für</strong> diese<br />

Be ziehungsart leichtes Spiel: Das Kombinationsfeld wird dann automatisch an ge legt<br />

(siehe Abbildung 3.39). Wie Sie eine Tabelle mit einem Kombinationsfeld zur Dar stellung<br />

einer n:1-Beziehung erstellen, erfahren Sie in »n:1-Beziehungen oder Lookup-<br />

Beziehungen« ab Seite 91.<br />

Sollte es aus irgendwelchen Gründen nicht gewünscht sein, dass das Kombinationsfeld<br />

direkt in der Tabelle angelegt wird, müssen Sie selbst Hand anlegen. In diesem Fall<br />

zeigt das Formular nach dem Hinzufügen der Felder aus der Feldliste zunächst ein herkömmliches<br />

Textfeld statt eines Kombinationsfeldes an.


Abbildung 3.39: Darstellung einer n:1-Beziehung im Formular<br />

Abbildung verschiedener Beziehungsarten<br />

Wenn Sie wissen, aus welcher Datei das Fremdschlüsselfeld seine Daten beziehen soll,<br />

gehen Sie folgendermaßen vor:<br />

» Konvertieren Sie das Textfeld in ein Kombinationsfeld, indem Sie aus dessen Kontext<br />

menü den Eintrag Ändern zu|Kombinationsfeld auswählen.<br />

» Stellen Sie die Eigenschaft Datensatzherkunft des frisch gebackenen Kombinationsfel<br />

des auf die Tabelle tblAnreden ein.<br />

» Legen Sie <strong>für</strong> die Eigenschaften Spaltenanzahl und Spaltenbreiten die Werte 2 und 0<br />

fest. Es sollen beide Felder der Tabelle enthalten sein, aber nur die zweite mit den<br />

Anreden selbst angezeigt werden.<br />

Das Kombinationsfeld zeigt nun die vorhandenen Einträge wie in Abbildung 3.39 an.<br />

3.5.7 1:n-Beziehungen<br />

1:n-Beziehungen trifft man während des <strong>Entwickler</strong>lebens vermutlich am häufigsten an,<br />

vor allem, wenn man n:1-Beziehungen auch darunter einstuft.<br />

Ein klassisches Beispiel <strong>für</strong> eine 1:n-Beziehung sind Projekte und Kunden, wobei die<br />

Projekte in der Detailtabelle und die Kunden in der Mastertabelle gespeichert werden<br />

(siehe Abbildung 3.40).<br />

Abbildung 3.40: 1:n-Beziehung zwischen Projekten und Kunden<br />

213


Kapitel 3 Formulare<br />

Die Gestaltung des Formulars <strong>für</strong> die Anzeige der Projekte entspricht vom Aufbau her<br />

dem Formular aus Abbildung 3.40 – es enthält die Daten zum jeweiligen Projekt und der<br />

Kunde kann mit einem Kombinationsfeld ausgewählt werden.<br />

Interessanter ist die Darstellung eines Kunden und seiner Projekte. Der Kunde wird<br />

dabei wie in »Einfache Daten in der Detailansicht« ab Seite 197 an gezeigt. Zusätzlich<br />

sollen aber nun die Projekte des Kunden im Überblick dargestellt werden – und dazu<br />

gibt es wiederum verschiedene Möglichkeiten. Auch diese Mög lich kei ten kennen Sie bereits<br />

– wenn auch in vereinfachter Form – aus den Ab schnitten »Einfache Daten in der<br />

Übersicht mit Endlosformularen« ab Seite 201, »Einfache Daten in der Übersicht als<br />

Datenblatt« ab Seite 205, »Daten in der Übersicht als Listenfeld« ab Seite 209 und<br />

»Geteilte Formulare« ab Seite 172.<br />

Dabei müssen Sie in diesem Fall sowohl das Endlosformular als auch die Datenblattansicht<br />

in ein Unterformular ausgliedern; lediglich das Listenfeld können Sie direkt in<br />

das Kunden-Formular integrieren.<br />

Verknüpfte Daten direkt bearbeiten oder nicht?<br />

Welche Variante Sie verwenden, hängt zunächst einmal davon ab, ob Sie die verknüpften<br />

Daten – also die Projekte – direkt im Kundenformular bearbeiten möchten oder nicht.<br />

Falls ja, verwenden Sie ein Unterformular mit den Projekten in einem Endlosformular<br />

oder der Datenblattansicht, ansonsten entscheiden Sie sich <strong>für</strong> ein Unterformular mit<br />

dem Recordset-Typ Snapshot oder ein Listenfeld.<br />

Da die Unterformular-Varianten technisch ähnlich sind und beide in einfacher Form bereits<br />

ausführlich erläutert wurden, soll hier nur eine der beiden Möglichkeiten beschrieben<br />

werden – die etwas einfacher zu realisierende Datenblattansicht.<br />

3.5.8 1:n-Beziehung per Unterformular und Datenblattansicht<br />

Das Unterformular zur Anzeige der Projekte in der Datenblattansicht ist genauso wie<br />

das aus »Einfache Daten in der Übersicht als Datenblatt« ab Seite 205 aufgebaut. Es<br />

basiert allerdings auf einer Abfrage, die alle Felder der Tabelle tblProjekte enthält und<br />

diese zusätzlich nach dem Startdatum sortiert (siehe Abbildung 3.41).<br />

Vorbereiten des Unterformulars<br />

Das Unterformular selbst enthält lediglich die drei Felder Projekt, Startdatum und Enddatum.<br />

Die anderen Felder werden zwar <strong>für</strong> die Funktionalität benötigt, sollen aber nicht<br />

angezeigt werden – und die Datenblattansicht zeigt immer alle im Entwurf enthal tenen<br />

Felder an. Daran ändert auch das Einstellen der Sichtbar-Eigenschaft auf den Wert Nein<br />

nichts. Die einzige Möglichkeit, ein Feld »unsichtbar« zu machen, ist das Verkleinern<br />

der Spaltenbreite auf den Wert 0. Dies erreichen Sie mit dem Kontextmenü-Ein trag<br />

214


Abbildung verschiedener Beziehungsarten<br />

Format|Spalten ausblenden des Spaltenkopfes oder mit den VBA-Eigenschaften Co lumn­<br />

Width und ColumnHidden.<br />

Abbildung 3.41: Datensatzquelle des Unterformulars zur Anzeige der Projekte eines Kunden<br />

(sfm Pro jekte)<br />

Vergessen Sie nicht, die Eigenschaft Standardansicht auf Datenblatt einzustellen und<br />

die Spaltenbreiten und Schriftgröße Ihren Bedürfnissen anzupassen. Speichern Sie das<br />

Formular unter dem Namen sfmProjekte (siehe Abbildung 3.42).<br />

Abbildung 3.42: Das Unterformular zur Anzeige der Projekte in der Entwurfsansicht<br />

Erstellen des Hauptformulars<br />

Das Hauptformular basiert auf der Tabelle tblKunden und zeigt alle darin enthaltenen<br />

Felder an. Ordnen Sie die Felder wie in Abbildung 3.43. Anschließend fügen Sie das<br />

Unterformular in das Hauptformular ein, indem Sie dieses aus dem Navigationsbereich<br />

in den Entwurf des Hauptformulars ziehen. Damit das Unterformular nur die Projekte<br />

des im Hauptformular angezeigten Kunden ausgibt, müssen zwei Eigenschaften des<br />

Unterformularsteuerelements entsprechend eingestellt werden, was beim Einfügen<br />

des Unterformulars normalerweise automatisch geschieht – zumindest wenn schon<br />

eine Beziehung zwischen den beiden Tabellen angelegt wurde.<br />

215


Kapitel 3 Formulare<br />

Abbildung 3.43: Haupt- und Unterformular zur Anzeige von Kunden und Projekten<br />

Die beiden Eigenschaften heißen Verknüpfen von und Verknüpfen nach und erwarten den<br />

Namen des Fremdschlüsselfeldes der Datensatzquelle des Unterformulars und den<br />

Namen des Primärschlüsselfeldes der Datensatzquelle des Hauptformulars. Diese beiden<br />

Felder haben üblicherweise den gleichen Namen – in diesem Fall KundeID.<br />

Die Verknüpfungsfelder müssen nicht zwangsläufig mit dem Primär schlüssel feld<br />

des Hauptformulars und dem Fremdschlüsselfeld des Unterformulars gefüllt werden.<br />

Es gibt auch Fälle, in denen beispielsweise der Wert eines Kombinationsfeldes<br />

im Hauptformular zur Auswahl der passenden Datensätze im Unterformular herangezogen<br />

wird – hier würde man einfach den Namen des Kombinationsfeldes als Wert<br />

der Eigenschaft Verknüpfen nach verwenden. Um die Eigenschaften einzusehen oder anzupassen,<br />

markieren Sie das Unterfor mu lar steuerelement – dieses ist keinesfalls mit<br />

dem Unterformular selbst zu verwechseln! Am einfachsten markieren Sie es, wenn Sie<br />

erst ein anderes Steuerelement des Haupt for mulars markieren und dann einfach auf<br />

das Unterformular klicken.<br />

Wenn das Ei gen schafts fenster im Register Daten die beiden Eigenschaften anzeigt, liegen<br />

Sie richtig. Wenn Sie das Formular nun in der Formularansicht öffnen, sieht dieses<br />

etwa wie in Abbildung 3.44 aus. Beim Wechseln zu einem anderen Datensatz des<br />

Hauptformulars zeigt das Unterformular automatisch die passenden Projekte an.<br />

Anlegen, bearbeiten und löschen im Detailformular<br />

Eine Projekttabelle wird in der Regel nicht mit derart wenig Feldern auskommen. Sie<br />

werden also normalerweise, wie unter »Einfache Daten in der Übersicht als Datenblatt«<br />

ab Seite 205, ein Detailformular zur Anzeige der Projekte benötigen. Um dieses anzuzeigen,<br />

neue Projekte anzulegen oder Projekte zu löschen, können Sie die ebenfalls in<br />

dem genannten Abschnitt vorgestellten Schaltflächen einschließlich VBA-Code <strong>für</strong> das<br />

Formular frmKunden anpassen.<br />

216


Abbildung verschiedener Beziehungsarten<br />

Abbildung 3.44: Das fertige Formular zur Anzeige von Kunden und Projekten (frmKunden)<br />

Die Prozeduren zum Löschen eines Datensatzes und <strong>für</strong> die Anzeige zum Bearbeiten<br />

sind prinzipiell mit den oben beschriebenen identisch; bei Bedarf finden Sie diese im<br />

Klassenmodul des Formulars frmKunden.<br />

Eine wichtige Neuerung liefert das Neuanlegen per Detailformular. In der Routine, die<br />

durch das Anklicken der Schaltfläche cmdNeu ausgelöst wird, enthält die DoCmd.Open-<br />

Anweisung einen bislang nicht verwendeten Parameter. Mit OpenArgs kann man einen<br />

einzelnen Wert, auch Öffnungsparameter genannt, an das aufgerufene Formular übergeben.<br />

In diesem Fall handelt es sich dabei um den Wert des Feldes KundeID.<br />

Private Sub cmdNeu_Click()<br />

End Sub<br />

DoCmd.OpenForm "frmProjekteDetailansicht", DataMode:=acFormAdd, _<br />

WindowMode:=acDialog, OpenArgs:=Me!KundeID<br />

Me.Requery<br />

Listing 3.13: Aufruf des Detailformulars mit Öffnungsargument<br />

Der Hintergrund ist, dass neue Datensätze im Detailformular zur Anzeige der Projekte<br />

direkt mit dem richtigen Kunden ausgestattet werden sollen (siehe Abbildung 3.45).<br />

Damit das Detailformular den Öffnungsparameter auch auswertet, legen Sie dort <strong>für</strong><br />

die Ereigniseigenschaft Beim Öffnen die folgende Prozedur an. Die Prozedur prüft, ob<br />

ein Öffnungsparameter übergeben wurde (das ist notwendig, da das Formular etwa zum<br />

Bearbeiten auch ohne Öffnungsparameter geöffnet werden soll – das Fehlen des Öffnungs<br />

arguments würde sonst einen Fehler auslösen) und weist diesen dann der Ei genschaft<br />

DefaultValue des Feldes KundeID zu.<br />

Dadurch wird der entsprechende Eintrag im Kombinationsfeld zur Anzeige der verknüpften<br />

Werte voreingestellt.<br />

217


Kapitel 3 Formulare<br />

218<br />

Private Sub Form_Open(Cancel As Integer)<br />

If Not IsNull(Me.OpenArgs) Then<br />

End If<br />

End Sub<br />

Me!KundeID.DefaultValue = Me.OpenArgs<br />

Listing 3.14: Auswerten des Öffnungsarguments beim Öffnen eines Formulars<br />

Abbildung 3.45: Das Kunden-Formular und die Detailansicht <strong>für</strong> Projekte mit einem neuen<br />

Daten satz (frmKunden, frmProjekteDetailansicht)<br />

3.5.9 1:n-Beziehung per Listenfeld<br />

Wenn Sie ohnehin ein Formular <strong>für</strong> die Anzeige der Details der verknüpften Datensätze<br />

des Hauptformulars verwenden, müssen beziehungsweise sollten Sie im Hauptformular<br />

nicht die Möglichkeit zur Bearbeitung der verknüpften Daten bieten. In diesem Fall reicht<br />

ein Listenfeld zu deren Anzeige aus; außerdem bietet es die Möglichkeit, Datensätze<br />

zur Bearbeitung und zum Löschen auszuwählen – alles wie bereits unter »Daten in der<br />

Übersicht als Listenfeld« ab Seite 209 beschrieben.<br />

Eine Besonderheit gibt es jedoch: Die im Listenfeld angezeigten Daten hängen nun von<br />

dem im Hauptformular angezeigten Datensatz ab. Dementsprechend muss bei einem<br />

Wechsel des Hauptformulars auch der Inhalt des Listenfeldes aktualisiert werden (siehe<br />

Abbildung 3.46).<br />

Damit das Listenfeld jeweils die Projekte zu dem im Hauptformular angezeigten Kunden<br />

ausgibt, legen Sie <strong>für</strong> die Ereigniseigenschaft Beim Anzeigen die folgende Routine an.<br />

Diese stellt einen SQL-Ausdruck mit einer Abfrage zusammen, die alle anzuzeigenden<br />

Felder der Tabelle tblProjekte ausgibt und dabei nur jene Datensätze berücksichtigt,<br />

deren Feld KundeID mit dem Wert des Formularfeldes KundeID übereinstimmt. Diesen


Abbildung verschiedener Beziehungsarten<br />

SQL-Ausdruck weist die Routine dann der Eigenschaft Datensatzherkunft (RowSource)<br />

des Listenfeldes zu:<br />

Private Sub Form_Current()<br />

End Sub<br />

Dim strSQL As String<br />

strSQL = "SELECT ProjektID, Projekt, Startdatum, Enddatum " _<br />

& "FROM tblProjekte WHERE KundeID = " & Me!KundeID<br />

Me!lstProjekte.RowSource = strSQL<br />

Listing 3.15: Synchronisieren des Listenfeldinhalts mit dem Hauptformular<br />

Abbildung 3.46: Darstellung einer 1:n-Beziehung per Listenfeld (frmKundenListenfeld)<br />

Projekt anzeigen per Doppelklick<br />

Wenn Sie dem Benutzer zusätzlichen Komfort bieten möchten, legen Sie <strong>für</strong> das<br />

Listenfeld eine Prozedur an, die beim Doppelklick auf das Listenfeld den aktuell markierten<br />

Eintrag im Detailformular anzeigt. Dazu verwenden Sie die Ereigniseigenschaft<br />

Beim Doppelklicken:<br />

Private Sub lstProjekte_DblClick(Cancel As Integer)<br />

If IsNull(Me!lstProjekte) Then<br />

End If<br />

End Sub<br />

MsgBox "Bitte wählen Sie zunächst einen Datensatz aus."<br />

Exit Sub<br />

DoCmd.OpenForm "frmProjekteDetailansicht", DataMode:=acFormEdit, _<br />

WindowMode:=acDialog, WhereCondition:="ProjektID = " & Me!lstProjekte<br />

Me!lstProjekte.Requery<br />

Listing 3.16: Anzeigen der Detailansicht eines Projekts<br />

219


Kapitel 3 Formulare<br />

Die Routine hat exakt den gleichen Inhalt wie die Ereignisprozedur, die durch einen Klick<br />

auf die Schaltfläche Bearbeiten ausgelöst wird. Den enthaltenen Code extrahieren Sie am<br />

besten in eine separate neue Routine, die Sie von beiden Ereignisprozeduren aufrufen.<br />

Wie das aussieht, können Sie dem Klassenmodul des Formulars frmKunden Listenfeld in<br />

der Beispieldatenbank Formulare.accdb unter www.acciu.de/aeb<strong>2010</strong> entnehmen.<br />

3.5.10 m:n-Beziehungen in Haupt- und Unterformular<br />

Wie bereits erwähnt, bieten Listenfelder zur Verwaltung von Daten aus m:n-Beziehungen<br />

nicht den Komfort, dass man die im Listenfeld angezeigten Daten direkt bearbeiten<br />

kann. Da<strong>für</strong> ist eine andere Lösung wesentlich sinnvoller: Die Darstellung einer m:n-<br />

Beziehung in einer Kombination aus Formular und Unterformular.<br />

Dabei zeigt das Hauptformular die Daten der einen Seite der Beziehung an, während das<br />

Unterformular die Daten der Verknüpfungstabelle und der anderen Seite der Be zie hung<br />

enthält.<br />

Das bekannteste Beispiel <strong>für</strong> eine solche Darstellung ist vermutlich das Formular Bestel<br />

lun gen aus der Nordwind-Datenbank (siehe Abbildung 3.47).<br />

Abbildung 3.47: Beispiel <strong>für</strong> die Verwaltung von Daten in einer m:n-Beziehung<br />

Zu Demonstrationszwecken reicht eine einfache m:n-Beziehung mit den notwendigsten<br />

Feldern wie in Abbildung 3.48. Die beiden Tabellen tblProjekte und tblMitarbeiter werden<br />

über die Verknüpfungstabelle tblProjektzeiten miteinander verbunden. Interessant wird<br />

220


Abbildung verschiedener Beziehungsarten<br />

dieses Beispiel durch die Tatsache, dass auch die Verknüpfungstabelle einige Felder<br />

enthält – in diesem Fall sogar die wichtigsten.<br />

Die Verknüpfungstabelle dient dem Speichern der Zeiten, die die Mitarbeiter mit den jeweiligen<br />

Projekten verbracht haben. Um diese Informationen nicht nur quantitativ, sondern<br />

auch qualitativ auswerten zu können, wird nicht nur die Zeit, sondern auch das<br />

Datum der Tätigkeit gespeichert.<br />

Für die Beziehungen legen Sie referentielle Integrität fest, da so sichergestellt ist, dass<br />

<strong>für</strong> jeden Eintrag in die Tabelle tblProjektzeiten ein Projekt und ein Mitarbeiter ausgewählt<br />

werden.<br />

Wer dieses Beispiel ausbauen möchte, wird natürlich die Projekt- und die Mitarbeitertabelle<br />

noch um einige Felder erweitern. Aber auch die Verknüpfungstabelle tbl Projekt<br />

zeiten kann noch weitere wichtige Informationen speichern: etwa eine Kurz beschrei<br />

bung der jeweiligen Tätigkeit und eine Tätigkeitsart wie Konzeption, Pro grammie<br />

rung oder Test.<br />

Abbildung 3.48: Datenmodell der Beispieltabellen<br />

Hauptformular der m:n-Beziehung<br />

Bevor Sie sich der Erstellung des Hauptformulars zuwenden, müssen Sie bei m:n-Beziehungen<br />

jeweils zunächst überlegen, von welcher der beiden Tabellen Sie nur je einen<br />

Datensatz im Hauptformular anzeigen möchten und welche Tabelle die Datensätze <strong>für</strong><br />

das Unterformular liefert.<br />

Im vorliegenden Fall sind beide Varianten interessant: Man könnte sich sowohl zu jedem<br />

Projekt die Zeiten ansehen, die die einzelnen Mitarbeiter damit verbracht haben,<br />

andererseits ist es vielleicht nicht ganz uninteressant, womit der eine oder andere<br />

Mitarbeiter seine Zeit verbringt.<br />

Wichtiger <strong>für</strong> die Auswertung von Projekten ist sicher die erste Variante. Das Hauptfor<br />

mular zeigt also die Daten der Tabelle tblProjekte an, die dementsprechend auch als<br />

Datensatzquelle des Formulars dient. Wenn Sie aus der Feldliste die beiden Fel der Projekt<br />

ID und Projekt in den Detailbereich des Formulars ziehen, ist der erste Teil der Arbeit<br />

be reits erledigt (siehe Abbildung 3.49).<br />

221


Kapitel 3 Formulare<br />

Abbildung 3.49: Hauptformular der m:n-Beziehung (frmProjektzeiten)<br />

Als Nächstes fügen Sie nun noch das Unterformular zur Anzeige der Mitarbeiter und der<br />

jeweiligen Projektzeiten hinzu.<br />

Unterformular der m:n-Beziehung<br />

Das Unterformular soll anzeigen, welcher Mitarbeiter an welchem Datum wie viele<br />

Stunden mit einem Projekt verbracht hat. Dazu legen Sie <strong>für</strong> das Unterformular die<br />

Abfrage aus Abbildung 3.50 als Datensatzquelle an.<br />

Abbildung 3.50: Datensatzquelle des Unterformulars der m:n-Beziehung<br />

Der Aufbau dieser Abfrage ist <strong>für</strong> alle Unterformulare von Formularen zur Darstellung<br />

von m:n-Beziehungen gleich. Die Abfrage enthält jeweils die Verknüpfungstabelle und<br />

die Tabelle, deren Daten nicht im Hauptformular angezeigt werden – in diesem Fall also<br />

die Tabellen tblProjektzeiten und tblMitarbeiter. Auch <strong>für</strong> die Felder gibt es feste Regeln.<br />

Sie benötigen immer folgende Felder:<br />

» Fremdschlüsselfeld der Verknüpfungstabelle zur Tabelle, die im Hauptformular angezeigt<br />

wird (hier ProjektID).<br />

» Fremdschlüsselfeld der Verknüpfungstabelle zur anderen Tabelle der m:n-Beziehung<br />

(hier MitarbeiterID). Dieses Feld wird in der Regel als Kombinationsfeld ausgeführt,<br />

damit ein Datensatz dieser Tabelle ausgewählt werden kann.<br />

222


Abbildung verschiedener Beziehungsarten<br />

» Alle sonstigen Felder der Verknüpfungstabelle und der im Unterformular anzuzeigenden<br />

Tabelle, die im Unterformular angezeigt werden sollen (hier die Felder<br />

Datum und Zeit der Tabelle tblProjektzeiten und Telefon der Tabelle tblMitarbeiter).<br />

Im Unterformular stellen Sie dann die Eigenschaft Datensatzherkunft auf die soeben<br />

erstellte Abfrage ein. Im Detailbereich des Unterformulars finden nicht alle Felder der<br />

zugrunde liegenden Abfrage Platz: Das Fremdschlüsselfeld, das auf den entsprechenden<br />

Datensatz des Hauptformulars verweist, muss nicht angezeigt werden.<br />

Lediglich das Feld zur Auswahl des Mitarbeiters sowie einige weitere Felder sollen später<br />

sichtbar sein (siehe Abbildung 3.51).<br />

Abbildung 3.51: Das Unterformular der m:n-Beziehung in der Entwurfsansicht<br />

Datenblatt- oder Endlosansicht?<br />

Da das Unterformular mehrere Datensätze anzeigen soll, stehen die Datenblatt- und<br />

die Endlosansicht zur Verfügung. Wenn Sie keine besonderen Ansprüche an das Layout<br />

der Steuerelemente im Unterformular haben und außer Text- und Kombinationsfeldern<br />

keine Steuerelemente benötigen, sind Sie mit der Datenblattansicht gut bedient. In<br />

diesem Beispiel ist das der Fall; stellen Sie daher die Eigenschaft Standardansicht auf<br />

Datenblattansicht ein.<br />

Das Unterformular sieht in der Datenblattansicht nun wie in Abbildung 3.52 aus. Wenn<br />

Sie bereits Daten in die Tabelle tblMitarbeiter eingegeben haben oder die Tabellen und<br />

Formulare aus der Beispieldatenbank verwenden, können Sie das Unterformular bereits<br />

testen. Die Auswahl eines Mitarbeiters per Kombinationsfeld sorgt automatisch <strong>für</strong><br />

das Füllen der übrigen Felder, die Sie aus der Tabelle tblMitarbeiter in das Unterformular<br />

übernommen haben.<br />

Fehlt noch eine Eingabe in die beiden Felder Datum und Zeit und das Speichern eines<br />

neuen Datensatzes. Letzteres schlägt wegen der Regeln zur referentiellen Integrität<br />

freilich fehl, da noch kein Wert <strong>für</strong> das Feld ProjektID der Abfrage qryProjektzeiten angegeben<br />

wurde – was ja auch gar nicht geht, da das Feld überhaupt nicht im Formular<br />

angezeigt wird.<br />

223


Kapitel 3 Formulare<br />

Abbildung 3.52: Das Unterformular in der Datenblattansicht<br />

Wie sollen Sie also Datensätze im Unterformular anlegen, wenn Sie gar keinen Zugriff<br />

auf eines der wichtigsten Felder haben? Ganz einfach: Sie automatisieren die Eingabe<br />

der Werte <strong>für</strong> dieses Feld. Das Unterformular ist ja eigentlich <strong>für</strong> den Einsatz im Hauptfor<br />

mular frmProjektzeiten gedacht, in das Sie es nun einfügen können.<br />

Nachdem Sie das Unterformular in das Hauptformular eingefügt haben, werfen Sie einen<br />

Blick auf die Eigenschaften Verknüpfen von und Verknüpfen nach (siehe Abbildung 3.53).<br />

Dort sollte <strong>Access</strong> beim Einfügen des Unterformulars automatisch jeweils den Wert<br />

ProjektID eingetragen haben. Diese Einstellung gewährleistet Folgendes:<br />

» Das Feld ProjektID wird bei neuen Datensätzen im Unterformular automatisch mit<br />

dem entsprechenden Wert des Hauptformulars gefüllt.<br />

» Das Unterformular zeigt nur Datensätze an, deren ProjektID dem entsprechenden<br />

Wert des Hauptformulars entspricht.<br />

Abbildung 3.53: Verknüpfen von Haupt- und Unterformular<br />

Ein Wechsel in die Formularansicht demonstriert die Funktion des Formulars. Sie können<br />

nun einen neuen Datensatz im Hauptformular anlegen und im Unterformular die<br />

gewünschten Projektzeiten eintragen. Dazu wählen Sie dort einfach den Mitarbeiter<br />

224


Abbildung verschiedener Beziehungsarten<br />

aus und geben Datum und Zeit ein – um den Inhalt des Feldes ProjektID kümmert sich<br />

<strong>Access</strong> selbst (siehe Abbildung 3.54).<br />

Abbildung 3.54: m:n-Beziehung im Einsatz<br />

3.5.11 m:n-Beziehungen per Listenfeld<br />

Die Verwaltung von Daten aus m:n-Beziehungen erfolgt verhältnismäßig selten mit Hilfe<br />

von Listenfeldern. Der Grund ist, dass Listenfelder keine direkte Bearbeitung der angezeigten<br />

Daten ermöglichen. Daher sind Listenfelder eher zum übersichtlichen Zu wei sen<br />

von Datensätzen der n-Seite zu den Datensätzen der m-Seite einer Beziehung ein setzbar.<br />

Einsatzzwecke <strong>für</strong> die Anwendung von Listenfeldern in Zusammenhang mit m:n-Bezie<br />

hungen gibt es genug: Die Verwaltung von Verteilerlisten, die das Zuordnen von<br />

Empfängern zu einer Publikation ermöglichen, Benutzer und Benutzergruppen (wie im<br />

obigen Beispiel) oder Fahrzeuge und Sonderausstattungen.<br />

Das folgende Formular soll anhand des Beispiels der Fahrzeuge und Aus stat tungs merkmale<br />

hergeleitet werden. Zum Nachvollziehen benötigen Sie drei Tabellen: Die beiden<br />

Tab ellen tblFahrzeuge und tblAusstattungen werden über die Tabelle tblFahrzeugeAusstattungen<br />

miteinander verknüpft (siehe Abbildung 3.55). Für die beiden Beziehungen<br />

legen Sie jeweils referentielle Integrität mit Löschweitergabe fest.<br />

Abbildung 3.55: Datenmodell der Beispieltabellen<br />

225


Kapitel 3 Formulare<br />

Das Formular zur Verwaltung der enthaltenen Daten besitzt als Datensatzquelle die<br />

Tabelle tblFahrzeuge und enthält die folgenden Steuerelemente (siehe Abbildung 3.56):<br />

» FahrzeugID und Fahrzeug: Textfelder, gebunden an die Datensatzquelle des For mu lars.<br />

» lstVorhandeneAusstattung: Zeigt alle Datensätze der Tabelle tblAusstattungen an, die<br />

über die Tabelle tblFahrzeugeAusstattungen mit der Tabelle tblFahrzeuge verknüpft sind.<br />

» lstNichtVorhandeneAusstattung: Zeigt alle Datensätze der Tabelle tblAusstattungen<br />

an, die das Listenfeld lstVorhandeneAusstattung nicht anzeigt.<br />

» cmdEntfernen: Verschiebt das aktuell im linken Listenfeld markierte Aus stat tungsmerk<br />

mal in das rechte Listenfeld.<br />

» cmdAlleEntfernen: Verschiebt alle Einträge des linken Listenfeldes in das rechte Listen<br />

feld.<br />

» cmdHinzufuegen: Verschiebt das aktuell im rechten Listenfeld markierte Aus stattungs<br />

merk mal in das linke Listenfeld.<br />

» cmdAlleHinzufuegen: Verschiebt alle Einträge des rechten Listenfeldes in das linke<br />

Lis tenfeld.<br />

Zusätzlich zu den bereits erwähnten Funktionen soll ein Doppelklick auf einen Eintrag<br />

eines der Listenfelder den betroffenen Eintrag in das jeweils andere Listenfeld verschieben.<br />

Abbildung 3.56: Entwurfsansicht des Formulars zum Festlegen der Ausstattungsmerkmale eines<br />

Fahrzeugs<br />

Datensatzherkunft der Listenfelder<br />

Die Beschreibung der Steuerelemente des Formulars hat den Inhalt der beiden Lis tenfel<br />

der bereits scharf umrissen. Da der Inhalt des rechten Listenfeldes vom Inhalt des<br />

linken Lis tenfeldes abhängt, beginnen Sie mit dem linken Listenfeld.<br />

226


Abbildung verschiedener Beziehungsarten<br />

Es soll alle Datensätze der Tabelle tblAusstattungen enthalten, die über die Tabelle<br />

tblFahrzeugeAusstattungen mit der Tabelle tblFahrzeuge verknüpft sind. Das aktuell<br />

angezeigte Fahr zeug lässt sich über das Feld FahrzeugID identifizieren. Da die<br />

Verknüpfungstabelle den entsprechenden Wert bereits enthält, besteht die Abfrage <strong>für</strong><br />

die Datensatzher kunft aus den beiden Tabellen tblFahrzeugeAusstattungen und tblAusstattungen<br />

(siehe Abbildung 3.57).<br />

Das Listenfeld soll die Bezeichnung des Ausstattungsmerkmals anzeigen, aber das<br />

Feld AusstattungID als gebundene Spalte verwenden; das Feld FahrzeugID ist nur als<br />

Kriterium vorgesehen und muss gar nicht angezeigt werden.<br />

Damit das Listenfeld die erste Spalte mit dem Feld AusstattungID ausblendet, stellen<br />

Sie die Eigenschaften Spaltenanzahl und Spaltenbreite auf die Werte 2 beziehungsweise<br />

0cm ein.<br />

Abbildung 3.57: Datensatzherkunft des Listenfeldes lstVorhandeneAusstattung (frm Fahr zeu ge)<br />

Wenn Sie die Tabellen aus der Beispieldatenbank verwenden oder bereits selbst Beispiel<br />

da ten eingegeben haben, können Sie nun ausprobieren, ob das Listenfeld die gewünschten<br />

Daten anzeigt. Beim ersten angezeigten Fahrzeug sollte dies funktionieren,<br />

beim Blät tern zum nächsten Fahrzeug verändert sich der Inhalt des Listenfeldes allerdings<br />

nicht. Das liegt daran, dass seine Datensatzherkunft nicht aktualisiert wird.<br />

Legen Sie also die folgende Prozedur an, die durch das Ereignis Beim Anzeigen des<br />

Formulars ausgelöst wird:<br />

Private Sub Form_Current()<br />

End Sub<br />

'Aktualisieren des linken Listenfeldes<br />

Me!lstVorhandeneAusstattung.Requery<br />

Listing 3.17: Diese Prozedur sorgt <strong>für</strong> die Aktualisierung des Listenfeldes beim Datensatz wechsel<br />

227


Kapitel 3 Formulare<br />

Legen Sie nun die Abfrage an, die als Datensatzherkunft <strong>für</strong> das zweite Listenfeld dient.<br />

Das Listenfeld soll alle Ausstattungen anzeigen, die nicht zu einem Fahrzeug gehören<br />

und die nicht über die Tabelle tblFahrzeugeAusstattungen mit der Tabelle tblFahrzeuge<br />

verknüpft sind. Das sind alle Datensätze der Tabelle tblAusstattungen, die nicht im linken<br />

Listenfeld angezeigt werden – also formulieren Sie die Abfrage auch einfach so. Diese<br />

enthält zunächst lediglich die beiden Felder der Tabelle tblAusstattungen. Den Bezug zu<br />

der Abfrage, die als Datensatzherkunft des linken Listenfeldes dient, erstellen Sie über<br />

das Kriterium <strong>für</strong> das Feld AusstattungID: Dort schließen Sie über das Schlüsselwort<br />

NOT IN alle Ausstattungen der Abfrage des linken Listenfeldes aus. Allerdings müssen<br />

Sie die dortige Abfrage noch ein wenig bearbeiten, da die <strong>für</strong> IN-Bedingungen verwendeten<br />

Unterabfragen nur ein Feld ausgeben dürfen. Den in Abbildung 3.58 nicht komplett<br />

zu erkennenden Ausdruck <strong>für</strong> die Bedingung finden Sie hier:<br />

228<br />

Nicht In (SELECT tblAusstattungen.AusstattungID FROM tblAusstattungen<br />

INNER JOIN tblFahrzeugeAusstattungen ON tblAusstattungen.AusstattungID = tblFahrzeuge-<br />

Ausstattungen.AusstattungID WHERE tblFahrzeugeAusstattungen.FahrzeugID=[Forms]![frmFahr<br />

zeuge]![FahrzeugID])<br />

Abbildung 3.58: Datensatzherkunft des Listenfeldes der nicht vorhandenen Ausstattungs merkmale<br />

(frmFahrzeuge)<br />

Nun zeigen die beiden Listenfelder bereits die gewünschten Daten an (siehe<br />

Abbildung 3.59). Als Nächstes bringen Sie ein wenig Leben in die Schaltflächen und<br />

Lis ten fel der, um das Formular zum Hinzufügen und Entfernen von Datensätzen in der<br />

Tabelle tbl FahrzeugeAusstattungen verwenden zu können.<br />

Damit auch das rechte Listenfeld beim Datensatzwechsel aktualisiert wird, ergänzen<br />

Sie die Ereignisprozedur Form_Current wie im folgenden Listing:<br />

Private Sub Form_Current()<br />

'Aktualisieren des linken Listenfeldes<br />

Me!lstVorhandeneAusstattung.Requery<br />

'Aktualisieren des rechten Listenfeldes


End Sub<br />

Me!lstNichtVorhandeneAusstattung.Requery<br />

Listing 3.18: Aktualisieren der Listenfelder<br />

Abbildung verschiedener Beziehungsarten<br />

Abbildung 3.59: Die Listenfelder zeigen bereits die richtigen Daten an (frmFahrzeuge)<br />

Hinzufügen eines Ausstattungsmerkmals<br />

Das Formular bietet zwei Möglichkeiten zum Hinzufügen eines einzelnen Aus stat tungsmerkmals<br />

zu einem Fahrzeug: per Doppelklick auf den gewünschten Eintrag im Listen<br />

feld lstNichtVorhanden oder durch Markieren des Eintrags im selben Listenfeld und<br />

an schließendes Betätigen der Schaltfläche cmdHinzufuegen.<br />

Da in beiden Varianten die gleiche Aktion ausgelöst werden soll, legen Sie diese in einer<br />

separaten Prozedur an, die von der jeweiligen Ereignisprozedur der beiden Steuer elemen<br />

te aus aufgerufen wird.<br />

Für das Anlegen des neuen Datensatzes in der Tabelle tblFahrzeugeAusstattungen müssen<br />

Sie die zukünftigen Werte der Felder FahrzeugID und AusstattungID kennen. Diese<br />

er mitteln Sie in den beiden Ereignisprozeduren Beim Klicken der Schaltfläche cmdHinzufuegen<br />

und Beim Doppelklicken des Listenfeldes lstNichtVorhanden und rufen von dort<br />

aus die Prozedur Hinzufuegen auf. Sollte einer der beiden Werte nicht vorhanden sein,<br />

was der Fall ist, wenn entweder kein hinzuzufügender Eintrag des Listenfeldes markiert<br />

ist oder das Formular einen neu angelegten Fahrzeug-Datensatz enthält, wird die<br />

Hinzufuegen-Prozedur nicht aufgerufen.<br />

Private Sub cmdHinzufuegen_Click()<br />

If Not IsNull(Me!FahrzeugID) And _<br />

End If<br />

End Sub<br />

Not IsNull(Me!lstNichtVorhandeneAusstattung) Then<br />

Hinzufuegen Me!FahrzeugID, Me!lstNichtVorhandeneAusstattung<br />

229


Kapitel 3 Formulare<br />

230<br />

Private Sub lstNichtVorhandeneAusstattung_DblClick(Cancel As Integer)<br />

If Not IsNull(Me!FahrzeugID) And _<br />

End If<br />

End Sub<br />

Not IsNull(Me!lstNichtVorhandeneAusstattung) Then<br />

Hinzufuegen Me!FahrzeugID, Me!lstNichtVorhandeneAusstattung<br />

Listing 3.19: Aufrufen der Prozedur zum Anlegen eines neuen Datensatzes in der Tabelle tbl Fahrzeuge<br />

Ausstattungen<br />

Die Prozedur Hinzufuegen erwartet als Parameter die Fahrzeug-ID und die Ausstattungs-<br />

ID des zu erstellenden Datensatzes. Nach der Durchführung der Aktionsabfrage aktualisiert<br />

die Prozedur die beiden Listenfelder.<br />

Private Sub Hinzufuegen(lngFahrzeugID As Long, _<br />

End Sub<br />

lngAusstattungID As Long)<br />

Dim db As DAO.Database<br />

Dim strSQL As String<br />

Set db = CurrentDb<br />

'Zusammen der Anfügeabfrage<br />

strSQL = "INSERT INTO tblFahrzeugeAusstattungen" _<br />

& "(FahrzeugID, AusstattungID) VALUES(" _<br />

& lngFahrzeugID & ", " & lngAusstattungID & ")"<br />

'Ausführen der Anfügeabfrage<br />

db.Execute strSQL<br />

'Aktualisieren der Listenfelder<br />

Me!lstVorhandeneAusstattung.Requery<br />

Me!lstNichtVorhandeneAusstattung.Requery<br />

Set db = Nothing<br />

Listing 3.20: Prozedur zum Hinzufügen eines Datensatzes zur Tabelle tbl Fahr zeuge Aus stat tun gen<br />

Entfernen eines Ausstattungsmerkmals<br />

Zum Entfernen eines Ausstattungsmerkmals eines Fahrzeugs gibt es ebenfalls zwei<br />

Möglichkeiten. Entweder Sie markieren den zu entfernenden Datensatz und klicken auf<br />

die Schaltfläche zum Entfernen eines Datensatzes oder Sie klicken doppelt auf den zu<br />

entfernenden Eintrag im Listenfeld.<br />

Der Aufbau der dadurch ausgelösten Prozeduren ist identisch mit dem der Prozeduren<br />

zum Hinzufügen eines Ausstattungsmerkmals und die Prozedur Entfernen ist das<br />

Pendant zur Prozedur Hinzufuegen. Lediglich die ver wen dete SQL-Anweisung hat ein<br />

anderes Aussehen:


Abbildung verschiedener Beziehungsarten<br />

Private Sub Entfernen(lngFahrzeugID As Long, lngAusstattungID As Long)<br />

End Sub<br />

Dim db As DAO.Database<br />

Dim strSQL As String<br />

Set db = CurrentDb<br />

strSQL = "DELETE FROM tblFahrzeugeAusstattungen " _<br />

& "WHERE FahrzeugID = " & lngFahrzeugID _<br />

& " AND AusstattungID = " & lngAusstattungID<br />

db.Execute strSQL<br />

Me!lstVorhandeneAusstattung.Requery<br />

Me!lstNichtVorhandeneAusstattung.Requery<br />

Set db = Nothing<br />

Listing 3.21: Prozedur zum Entfernen eines Ausstattungsmerkmals eines Fahrzeugs<br />

Hinzufügen oder Entfernen aller Ausstattungsmerkmale<br />

Die beiden Schaltflächen mit dem doppelten Kleiner- beziehungsweise Größer-Zeichen<br />

bewegen jeweils alle Ausstattungen eines Fahrzeugs von einem Listenfeld ins andere.<br />

Aus Datensicht bedeutet das, dass entweder <strong>für</strong> jedes Ausstattungsmerkmal in Kom bina<br />

tion mit dem aktuell angezeigten Fahrzeug ein Datensatz in der Tabelle tblFahrzeuge­<br />

Aus stattungen angelegt wird oder dass alle vorhandenen Einträge <strong>für</strong> ein Fahrzeug entfernt<br />

werden. Das Hinzufügen aller Ausstattungsmerkmale erledigt die Prozedur, die<br />

durch das Er eig nis Beim Klicken der Schaltfläche cmdAlleHinzufuegen ausgelöst wird.<br />

Im Gegensatz zu den Prozeduren zum Hinzufügen eines einzelnen Datensatzes braucht<br />

hier nicht ge prüft zu werden, ob ein Eintrag des Listenfeldes markiert ist:<br />

Private Sub cmdAlleHinzufuegen_Click()<br />

Dim db As DAO.Database<br />

Dim strSQL As String<br />

If Not IsNull(Me!FahrzeugID) Then<br />

End If<br />

End Sub<br />

Set db = CurrentDb<br />

strSQL = "INSERT INTO tblFahrzeugeAusstattungen " _<br />

& "SELECT " & Me!FahrzeugID & " AS FahrzeugID, " _<br />

& "AusstattungID FROM tblAusstattungen"<br />

db.Execute strSQL<br />

Me!lstVorhandeneAusstattung.Requery<br />

Me!lstNichtVorhandeneAusstattung.Requery<br />

Set db = Nothing<br />

Listing 3.22: Hinzufügen aller vorhandenen Ausstattungsmerkmale zu einem Fahrzeug<br />

231


Kapitel 3 Formulare<br />

Das Entfernen aller Ausstattungen eines Fahrzeugs geschieht in der folgenden Prozedur.<br />

Der wesentliche Unterschied zur vorherigen Prozedur liegt in der SQL-Anweisung:<br />

232<br />

Private Sub cmdAlleEntfernen_Click()<br />

Dim db As DAO.Database<br />

Dim strSQL As String<br />

If Not IsNull(Me!FahrzeugID) Then<br />

End If<br />

End Sub<br />

Set db = CurrentDb<br />

strSQL = "DELETE FROM tblFahrzeugeAusstattungen " _<br />

& "WHERE FahrzeugID = " & Me!FahrzeugID<br />

db.Execute strSQL<br />

Me!lstVorhandeneAusstattung.Requery<br />

Me!lstNichtVorhandeneAusstattung.Requery<br />

Set db = Nothing<br />

Listing 3.23: Entfernen aller Ausstattungsmerkmale eines Fahrzeugs<br />

3.5.12 Reflexive Beziehungen<br />

Für die Darstellung der Daten aus reflexiven Beziehungen bietet sich das Treeview-<br />

Steuerelement an. Mit ein wenig Fantasie lassen sich hierarchische Daten zwar auch in<br />

Listenfeldern oder gar in Textfeldern anzeigen, aber ihre Anwendung macht nicht wirklich<br />

Spaß. Daher finden Sie nachfolgend eine Anleitung zum Erstellen eines Formulars<br />

mit einen Treeview-Steuerelement zur Darstellung der Hierarchie von Mitarbeitern und<br />

ihren Vorgesetzten. Eine ausführliche Vorstellung der Funktionen und Möglichkeiten<br />

des Treeview-Steuer ele ments finden Sie in »Steuerelemente« ab Seite 265.<br />

Das Treeview-Steuerelement legen Sie über den Dialog ActiveX­Steuerelement einfügen<br />

an, den Sie mit dem Ribbon-Eintrag Entwurf|Steuerelemente|ActiveX­Steuerelement<br />

einfügen aufrufen (siehe Abbildung 3.60). Benennen Sie das neu hinzugefügte<br />

Steuerelement ctl Tree view. Anschließend können Sie direkt den <strong>für</strong> das Füllen des<br />

Treeview-Steuerelements be nötigten Code schreiben.<br />

Füllen des Treeview-Steuerelements<br />

Zum Speisen des Treeview-Steuerelements mit Daten aus einer Tabelle mit einer reflexiven<br />

Beziehung benötigen Sie eine rekursive Prozedur. Das bedeutet, dass sich die Proze<br />

dur immer wieder selbst aufruft, solange die Datensatzquelle tiefer verschachtelte<br />

Ele mente enthält, und erst dann die folgenden Elemente der übergeordneten Ebenen<br />

ab arbeitet. Den Start macht allerdings eine herkömmliche Routine. Sie legt eine modulweit<br />

gültige Variable namens objTreeview mit einem Verweis auf das Steuerelement an.


Abbildung 3.60: Einfügen des Treeview-Steuerelements<br />

Abbildung verschiedener Beziehungsarten<br />

Anschließend durchläuft die Routine alle Datensätze eines Recordsets mit allen Mit arbeitern,<br />

die keinen Vorgesetzten haben, und fügt je einen Knoten zum Treeview-Steuerele<br />

ment hinzu. Dabei legt sie als Beschriftung den Nachnamen und den Vornamen des<br />

ak tuellen Mitarbeiters an. Außerdem verwendet sie die Key-Eigenschaft des neuen<br />

Ele ments, um den Wert des Feldes MitarbeiterID des aktuellen Datensatzes hinzuzufügen.<br />

Da Werte der Eigenschaft Key mit einem Buchstaben beginnen müssen, stellt die<br />

Routine ein x voran. Nach dem Anlegen des Knotens ruft sie die Routine zum Anlegen<br />

der un tergeordneten Mitarbeiter auf – dazu weiter unten mehr. Im Anschluss daran<br />

werden wei tere Datensätze – soweit vorhanden – auf die gleiche Art abgearbeitet.<br />

Dim objTreeview As TreeView<br />

Dim db As DAO.Database<br />

Private Sub Form_Load()<br />

Dim rst As DAO.Recordset<br />

Dim objNode As Node<br />

Dim objListItem As ListItem<br />

Set objTreeview = Me!ctlTreeview.Object<br />

objTreeview.Nodes.Clear<br />

Set db = CurrentDb<br />

Set rst = db.OpenRecordset( _<br />

"SELECT * FROM tblMitarbeiterMitVorgesetzten " _<br />

& "WHERE VorgesetzterID IS NULL", dbOpenDynaset)<br />

Do While Not rst.EOF<br />

Set objNode = objTreeview.Nodes.Add<br />

With objNode<br />

.Text = rst!Nachname & ", " & rst!Vorname<br />

.Key = "x" & rst!MitarbeiterID<br />

233


Kapitel 3 Formulare<br />

234<br />

Loop<br />

End Sub<br />

End With<br />

AddChilds rst!MitarbeiterID<br />

rst.MoveNext<br />

Listing 3.24: Diese Routine fügt die Elemente der ersten Ebene in das Treeview-Steuerelement<br />

ein<br />

Die Routine AddChilds erwartet die MitarbeiterID des übergeordneten Mitarbeiters als<br />

Parameter. Per OpenRecordset legt die Routine eine Datensatzgruppe an, die alle Mitar<br />

beiter-Datensätze enthält, deren Vorgesetzter der Mitarbeiter mit der übergebenen<br />

Mit ar beiterID ist. Anschließend wird <strong>für</strong> jeden »Untergebenen« ein Element unterhalb<br />

des Vorgesetzten an gelegt.<br />

Um das Element des Vorgesetzten zu ermitteln, setzt die Routine den Buch sta ben x<br />

und die MitarbeiterID zu dem Wert zusammen, den die aufrufende Routine als Key <strong>für</strong><br />

den übergeordneten Mitarbeiter angelegt hat, und findet so das passende Ele ment. Für<br />

jeden »Untergebenen« wird diese Routine rekursiv aufgerufen, um auch die »Un ter gebenen«<br />

der »Untergebenen« zu finden und entsprechende Elemente anzulegen.<br />

Private Sub AddChilds(lngMitarbeiterID As Long)<br />

Dim rst As DAO.Recordset<br />

Dim objNode As Node<br />

Set rst = db.OpenRecordset( _<br />

"SELECT * FROM tblMitarbeiterMitVorgesetzten " _<br />

& "WHERE VorgesetzterID = " _<br />

& lngMitarbeiterID, dbOpenDynaset)<br />

Do While Not rst.EOF<br />

Loop<br />

End Sub<br />

Set objNode = objTreeview.Nodes.Add(Relative:="x" _<br />

& lngMitarbeiterID, Relationship:=tvwChild)<br />

With objNode<br />

.Text = rst!Nachname & ", " & rst!Vorname<br />

.Key = "x" & rst!MitarbeiterID<br />

End With<br />

AddChilds rst!MitarbeiterID<br />

rst.MoveNext<br />

Listing 3.25: Rekursiver Teil der Prozeduren zum Füllen des Treeview-Steuerelements<br />

Auf diese Weise werden alle Datensätze durchlaufen und dem Treeview-Steuerelement<br />

hinzugefügt.


Anzeigen des Detailformulars zu einem Element<br />

Von Formular zu Formular<br />

Natürlich sollen Sie auch etwas mit dem gefüllten Treeview anfangen können. Daher finden<br />

Sie nachfolgend eine kleine Routine, die nach einem Klick auf eine da<strong>für</strong> vorgesehene<br />

Schaltfläche ein Detailformular mit den Daten des aktuell im Treeview-Steuerelement<br />

markierten Eintrags anzeigt:<br />

Private Sub cmdDetails_Click()<br />

End Sub<br />

Dim lngMitarbeiterID As Long<br />

lngMitarbeiterID = CLng(Mid(objTreeview.SelectedItem.Key, 2))<br />

DoCmd.OpenForm "frmMitarbeiterDetail", _<br />

WhereCondition:="MitarbeiterID = " & lngMitarbeiterID<br />

Listing 3.26: Code zum Öffnen eines Detailformulars zum aktuellen Element des Treeview-<br />

Steuerelements<br />

Das gefüllte Treeview-Steuerelement mit Detailformular <strong>für</strong> einen der Einträge sieht wie<br />

in Abbildung 3.61 aus.<br />

Abbildung 3.61: Treeview-Steuerelement mit Detailformular (frmMitarbeiterMitVorgesetzten, frm­<br />

Mit arbeiterDetail)<br />

3.6 Von Formular zu Formular<br />

Weiter oben haben Sie bereits einige Möglichkeiten kennen gelernt, mit denen Sie<br />

von einem Formular aus ein weiteres Formular aufrufen und diesem bestimmte<br />

Informationen übergeben können. Bei der Anwendung dieser Möglichkeiten sind einige<br />

Voraussetzungen zu beachten, damit Sie länger Spaß an den auf diese Weise »verknüpften«<br />

Formularen haben.<br />

235


Kapitel 3 Formulare<br />

Die wichtigste Regel ist: Sorgen Sie <strong>für</strong> möglichst wenig Abhängigkeiten zwischen aufrufendem<br />

und aufgerufenem Formular. Das bedeutet in diesem Fall, dass Sie die Abhängigkeit<br />

erstens unidirektional auslegen und zweitens an bestimmten Punkten konzentrieren<br />

sollten – etwa auf das Öffnen und das Schließen des aufgerufenen For mu lars.<br />

Zur besseren Verständlichkeit heißt das aufrufende Formular in den nächsten Ab schnitten<br />

»Parent« und das aufgerufene Formular »Child«. Unidirektionale Abhängigkeit bedeutet,<br />

dass zwar das Parent-Formular das Child-Formular kennen muss, aber nicht<br />

umgekehrt. In der Praxis sieht das folgendermaßen aus:<br />

» Das Parent-Formular kennt den Namen des Child-Formulars und ruft dieses darüber<br />

auf.<br />

» Das Parent-Formular kennt die Datensatzquelle des Child-Formulars und weiß, wie<br />

eine Bedingung zur Einschränkung der Datensatzquelle aussehen muss.<br />

» Das Parent-Formular weiß, welche Werte es dem Child-Formular mit dem Öffnungsargument<br />

übergeben kann.<br />

Umgekehrt weiß das Child-Formular nichts vom Parent-Formular – es wertet lediglich<br />

die vom Parent-Formular übergebenen Informationen aus, ist aber nicht von der Lie ferung<br />

dieser Informationen abhängig.<br />

Andersherum soll das Parent-Formular neben den beim Aufruf übergebenen In forma<br />

tio nen nach dem Schließen des Child-Formulars Werte von dort auslesen können.<br />

Daher darf das Child-Formular nicht geschlossen, sondern nur unsichtbar gemacht<br />

wer den.<br />

Außer diesen zwei Kontakten – Parameterübergabe beim Aufruf und Auslesen des<br />

Child-Formulars vor dem Schließen – finden im Optimalfall keine Kontakte statt. Das<br />

heißt insbesondere, dass das Child-Formular nicht auf das Parent-Formular zugreift.<br />

Es gibt natürlich die Möglichkeit, Variablen, die zwischen Parent- und Child-Formular<br />

hin- und hergereicht werden sollen, in globalen Variablen zu speichern; man könnte<br />

auch vom Child-Formular aus lesend und schreibend auf das Parent-Formular zugreifen.<br />

Ersteres bedingt, dass beide Formulare die globalen Variablen kennen müssen, und<br />

Letz teres, dass das Parent-Formular das Child-Formular kennen muss und umgekehrt<br />

und diese damit voneinander abhängig sind.<br />

Seit <strong>Access</strong> 2007 gibt es mit den TempVars noch eine weitere Möglichkeit. Nähere<br />

Informationen dazu finden Sie unter »TempVars« ab Seite 450.<br />

Formulare aufrufen und vor dem Schließen auslesen<br />

VBA-technisch sieht das wie folgt aus. Der Aufruf des Child-Formulars erfolgt mit der<br />

DoCmd.OpenForm-Methode. Damit das Parent-Formular das Child-Formular nach dem<br />

236


Von Formular zu Formular<br />

Aus blenden und vor dem Schließen auslesen kann, muss das Child-Formular als modaler<br />

Dialog geöffnet werden. Das bedeutet, dass keine anderen Aktionen in der <strong>Access</strong>-<br />

An wendung möglich sind, solange das Child-Formular geöffnet ist. Auf diese Weise verhindern<br />

Sie Wechsel wir kungen zwischen Child-Formular und anderen Elementen der<br />

Benutzeroberfläche. Um ein Formular mit der OpenForm-Methode als modalen Dialog<br />

zu öffnen, verwendet man den Parameter WindowMode mit dem Wert acDialog:<br />

DoCmd.OpenForm "frmChild", WindowMode:=acDialog<br />

Wichtigster Nebeneffekt des Öffnens als modaler Dialog ist, dass auch die aufrufende<br />

Prozedur angehalten wird. Diese läuft erst dann weiter, wenn das Child-Formular durch<br />

Setzen der Eigenschaft Visible auf den Wert True oder durch Schließen den Fokus verliert.<br />

Die aufrufende Routine weiß dann bei der nächsten Zeile zumindest sicher, dass<br />

das Child-Formular entweder geschlossen oder ausgeblendet ist. Erstrebenswert ist<br />

natürlich Letzteres, denn sonst könnten Sie von dort keine Informationen mehr erlangen.<br />

Also sorgen Sie da<strong>für</strong>, dass das Formular nicht auf herkömmlichem Wege geschlossen,<br />

sondern nur über eine spezielle Schaltfläche ausgeblendet werden kann. Die passende<br />

Er eignisprozedur der Schaltfläche mit dem Namen cmdOK sieht dann folgendermaßen<br />

aus:<br />

Private Sub cmdOK_Click()<br />

End Sub<br />

Me.Visible = True<br />

Listing 3.27: Ausblenden eines Formulars<br />

In der aufrufenden Prozedur sollten Sie auf jeden Fall prüfen, ob das Formular noch<br />

geöffnet ist. Dazu können Sie die Eigenschaft IsLoaded des entsprechenden Elements<br />

der AllForms-Auflistung verwenden:<br />

If CurrentProject.AllForms("").IsLoaded = True Then<br />

Aber auch das verschafft noch keine Sicherheit, denn das Formular könnte ja auch in der<br />

Entwurfsansicht geöffnet sein. Also folgt eine weitere Prüfung:<br />

…<br />

If CurrentProject.AllForms("").CurrentView = _<br />

acCurViewFormBrowse Then<br />

Anschließend kann das Child-Formular in Ruhe ausgelesen werden, bevor es geschlossen<br />

wird.<br />

Beispiel <strong>für</strong> das Aufrufen eines weiteren Formulars<br />

Wenn Sie ein Kombinationsfeld etwa <strong>für</strong> die Auswahl der Kategorie eines Artikels verwenden,<br />

sollten Sie die Möglichkeit bieten, leicht weitere Kategorien anzulegen. Im<br />

237


Kapitel 3 Formulare<br />

fol genden Beispiel wird bei der Eingabe eines Eintrags, der in der Datensatzherkunft<br />

eines Kombinationsfeldes noch nicht vorhanden ist (siehe Abbildung 3.62), ein weiteres<br />

Formular zum Anlegen eines neuen Datensatzes in der Kategorien-Tabelle angezeigt<br />

(siehe Abbildung 3.63).<br />

Abbildung 3.62: Anlegen einer neuen Kategorie (frmArtikel) …<br />

Abbildung 3.63: … in einem eigenen Formular (frmKategorien)<br />

Ob der Benutzer einen noch nicht in der Datensatzherkunft eines Kombinationsfeldes<br />

vorhandenen Datensatz eingefügt hat, stellen Sie daran fest, dass das Ereignis Bei nicht<br />

in Liste ausgelöst wird. Dieses Ereignis machen Sie sich zu Nutze und fragen darin den<br />

Benutzer, ob er den neuen Eintrag tatsächlich anlegen möchte – vielleicht hat er sich ja<br />

auch nur vertippt.<br />

Anderenfalls öffnet die Routine das Formular frmKategorien (siehe Abbildung 3.63) mit<br />

einem neuen Datensatz, der automatisch mit der neuen Kategorie gefüllt wird – dazu<br />

später mehr.<br />

Nach der Eingabe und dem Ausblenden des Formulars prüft die Routine, ob das<br />

Formular noch in der Formularansicht geöffnet ist, und liest gegebenenfalls die<br />

KategorieID des neuen Eintrags ein. Nach dem Schließen des aufgerufenen Formulars,<br />

das zu diesem Zeitpunkt nur ausgeblendet, aber nicht geschlossen ist, stellt die Routine<br />

das Kombinationsfeld auf den neuen Wert ein.<br />

238<br />

Private Sub KategorieID_NotInList(NewData As String, Response As Integer)<br />

Dim lngKategorieID As Long<br />

If MsgBox("Möchten Sie diese Kategorie anlegen?", _


End If<br />

End Sub<br />

vbYesNo + vbExclamation, "Neue Kategorie") = vbYes Then<br />

DoCmd.OpenForm "frmKategorien", DataMode:=acFormAdd, _<br />

OpenArgs:=NewData, WindowMode:=acDialog<br />

Besonderheiten von Unterformularen<br />

If CurrentProject.AllForms("frmKategorien").IsLoaded = True Then<br />

End If<br />

If CurrentProject.AllForms("frmKategorien").CurrentView = _<br />

End If<br />

acCurViewFormBrowse Then<br />

lngKategorieID = Forms!frmKategorien!KategorieID<br />

DoCmd.Close acForm, "frmKategorien"<br />

Me!KategorieID = lngKategorieID<br />

Me!KategorieID.Requery<br />

Response = acDataErrContinue<br />

Listing 3.28: Aufrufen eines weiteren Formulars zum Eingeben verknüpfter Daten<br />

3.7 Besonderheiten von Unterformularen<br />

Unterformulare können auf verschiedene Arten eingesetzt werden. Meist gibt es hier<br />

und da Probleme – etwa, wenn Daten im Unterformular eingegeben werden, ohne<br />

dass ein Datensatz im Hauptformular existiert, oder wenn Änderungen rückgängig<br />

gemacht werden sollen, obwohl diese Änderungen im Unterformular bereits gespeichert<br />

sind.<br />

3.7.1 Eingabe von Daten ohne Detaildatensatz<br />

In Abschnitt 4.5.10 haben Sie ein Formular mit Unterformular zur Eingabe von Pro jekten<br />

und Projektzeiten kennen gelernt. Wenn Sie dort im Hauptformular auf einen neuen<br />

Datensatz springen und im Unterformular Daten anlegen, bekommen Sie folgendes<br />

Problem: Die Datensätze im Unterformular sind dann nicht mit einem Datensatz im<br />

Hauptformular verknüpft und verschwinden in die ewigen Jagdgründe – zumindest aus<br />

Sicht dieses Formulars, denn es zeigt im Unterformular nur Datensätze an, die mit dem<br />

im Hauptformular befindlichen Datensatz verknüpft sind (siehe Abbildung 3.64).<br />

Das ist zumindest das Standardverhalten: Seit <strong>Access</strong> 2007 können Sie mit der Eigen<br />

schaft Leeren Hauptentwurf filtern (VBA: FilterOnEmptyMaster) des Unterformular-<br />

Steuer ele ments festlegen, ob im Falle eines leeren Datensatzes im Hauptformular<br />

alle Da ten sätze der Datensatzquelle des Unterformulars oder keiner angezeigt werden<br />

soll.<br />

239


Kapitel 3 Formulare<br />

Abbildung 3.64: Eingabe von Daten in ein Unterformular ohne verknüpften Datensatz im Hauptformular<br />

(frmProjektzeiten)<br />

Um diese Probleme zu vermeiden, sollten Sie verhindern, dass der Benutzer mit der<br />

Be ar beitung von Daten im Unterformular beginnt, bevor er nicht mindestens ein Feld<br />

im Hauptdatensatz ausgefüllt hat. Dazu legen Sie <strong>für</strong> die Ereigniseigenschaft Beim<br />

Hingehen die folgende Prozedur an:<br />

240<br />

Private Sub sfmProjektzeiten_Enter()<br />

If Me.NewRecord Then<br />

End If<br />

End Sub<br />

If Not Me.Dirty Then<br />

End If<br />

MsgBox "Bitte legen Sie zuerst einen Projektnamen an."<br />

Me!Projekt.SetFocus<br />

Listing 3.29: Kein Datensatz im Unterformular ohne Pendant im Hauptformular<br />

Die Prozedur prüft, ob das Hauptformular einen neuen Datensatz enthält und ob dieser<br />

gegebenenfalls bereits bearbeitet wurde – womit der benötigte Primärschlüsselwert<br />

vorhanden wäre. Falls nicht, fordert die Routine den Benutzer auf, zunächst ein Projekt<br />

im Hauptformular anzulegen.<br />

3.7.2 Undo in Haupt- und Unterformular<br />

Viele Formulare zeigen nicht nur selbst Daten an, sondern enthalten auch noch ein Unter<br />

formular zur Bearbeitung von Daten aus verknüpften Tabellen. Wie in gebundenen<br />

For mularen üblich, gilt auch hier: Wechselt man den Datensatz, wird er auch in der<br />

zu grunde liegenden Tabelle gespeichert. Bis dahin lassen sich Änderungen am aktuell<br />

bearbeiteten Datensatz noch relativ leicht durch Auslösen des Undo-Ereignisses rückgängig<br />

machen, etwa durch Betätigen der Esc-Taste.


Besonderheiten von Unterformularen<br />

Viele Formulare enthalten eine Implementierung einer Undo-Funktion in Form einer<br />

Abbrechen-Schaltfläche. Den unbedarften Benutzer täuscht man freilich damit, denn<br />

diese Schaltfläche macht lediglich die Änderungen an dem im Hauptformular angezeigten<br />

Datensatz rückgängig. Die Änderungen im Unterformular sind und bleiben gespeichert.<br />

Abbildung 3.65 zeigt, wie solch ein Formular aussieht. Es handelt sich um das bereits<br />

weiter oben hergeleitete Formular mit zunächst zwei (sichtbaren) Erweiterungen: einer<br />

OK­ und einer Abbrechen-Schaltfläche.<br />

Abbildung 3.65: Formular mit Unterformular und Abbrechen-Funktion (frmProjekt zeiten_Un do)<br />

Der Code, der durch das Beim Klicken-Ereignis der jeweiligen Schaltfläche ausgelöst<br />

wird, sieht etwa wie folgt aus:<br />

Private Sub cmdOK_Click()<br />

End Sub<br />

'Formular schließen<br />

DoCmd.Close acForm, Me.Name<br />

Private Sub cmdAbbrechen_Click()<br />

'Prüfen, ob Datensatz seit letzter Speicherung geändert wurde<br />

If Me.Dirty = True Then<br />

End If<br />

End Sub<br />

'Falls ja, Änderungen rückgängig machen<br />

Me.Undo<br />

'Formular schließen<br />

DoCmd.Close acForm, Me.Name<br />

Listing 3.30: Ereignisprozeduren der OK- und der Abbrechen-Schaltfläche<br />

241


Kapitel 3 Formulare<br />

Eine Änderung im Unterformular wird auf jeden Fall gespeichert, da das Verlassen des<br />

Unterformulars das Speichern des aktuellen Datensatzes zur Folge hat.<br />

Wenn Sie nun einmal eine Korrektur an den Projektzeiten eines Projekts vornehmen<br />

und dabei aus Versehen einige Datensätze löschen, weil Sie sich in einem anderen<br />

Projekt wähnten, gibt es keine Undo-Funktion: Solche Änderungen führen Sie ohne Netz<br />

und doppelten Boden aus.<br />

In den folgenden Abschnitten erfahren Sie, wie Sie den Benutzern Ihrer Datenbank<br />

(und vielleicht auch sich selbst) das Leben in dieser Hinsicht erleichtern können. Dabei<br />

machen Sie Gebrauch von einer Transaktion, die bei der ersten Änderung an einem<br />

Datensatz des Hauptformulars oder der verknüpften Datensätze im Unterformular gestartet<br />

und mit Speichern des Datensatzes im Hauptformular (etwa durch Betätigen der<br />

OK-Schaltfläche oder den Wechsel zu einem anderen Datensatz) beendet wird – was<br />

zum endgültigen Speichern der Änderungen im Haupt- und Unterformular führt. Auch<br />

die Abbrechen-Schaltfläche kann die Transaktion beenden – allerdings sollen dabei alle<br />

seit Beginn der Transaktion durchgeführten Änderungen verworfen werden.<br />

Normalerweise ist eine Transaktion ein gängiges Mittel, um mehrere Da ten bank operatio<br />

nen zu bündeln und je nach Verlauf der Operationen komplett durchzuführen oder zu<br />

ver werfen. Das kann doch bei den Aktionen innerhalb eines Formulars nicht viel anders<br />

sein: Also startet man die Transaktion einfach beim Öffnen des Formulars und beendet<br />

sie mit Betätigen der OK­ oder der Abbrechen-Schaltfläche. Oder doch nicht? Nein, ganz<br />

so einfach ist es nicht. <strong>Access</strong> führt nämlich die in Zusammenhang mit gebundenen Formu<br />

laren anfallenden Datenbankzugriffe nicht im Kontext des aktuellen Workspace aus.<br />

Wenn Sie also einen Datensatz ändern und speichern, bekommt die Transaktion davon<br />

überhaupt nichts mit.<br />

Abhilfe schafft überhaupt erst die mit <strong>Access</strong> 2000 eingeführte Recordset-Eigenschaft<br />

von Formularen. Damit lässt sich ein Formular an ein zuvor erstelltes Recordset-<br />

Objekt binden, das wiederum sehr wohl unter die Kontrolle einer Transaktion fallen<br />

kann. Es sind also folgende Maßnahmen <strong>für</strong> die Verwendung einer Transaktion zur<br />

Zu sam men fassung von Datenbankoperationen in einem Formular mit Unterformular<br />

notwendig:<br />

» Binden des Haupt- und des Unterformulars an Recordset-Objekte<br />

» Starten der Transaktion beim Ändern des Datensatzes im Hauptformular oder in<br />

einem der verknüpften Datensätze im Unterformular<br />

» Durchführen der Transaktion beim Klick auf die OK-Schaltfläche oder beim Wechseln<br />

des Datensatzes im Hauptformular<br />

» Verwerfen der Operationen der Transaktion beim Betätigen der Abbrechen-Schaltfläche<br />

242


Besonderheiten von Unterformularen<br />

Die letzten beiden Punkte setzen voraus, dass die entsprechenden Prozeduren Infor<br />

ma tionen über den aktuellen Status haben – ist bereits eine Transaktion gestartet<br />

wor den und wurde diese bereits beendet? Diese Information muss vom Haupt- und<br />

vom Unterformular aus schreib- und lesbar sein, da Datenbankoperationen in beiden<br />

Formularen eine Transaktion in Gang setzen können.<br />

Das ist deshalb wichtig, weil das Beenden einer Transaktion, die noch nicht gestartet<br />

wurde, zu einem Fehler führt. Genauso wichtig ist es, keine zweite Transaktion zu starten,<br />

wenn bereits eine Trans ak tion gestartet wurde. Dies würde als »verschachtelte«<br />

Transaktion gewertet werden; die Be tätigung der OK­ oder Abbrechen-Schaltfläche würde<br />

möglicherweise nur die innere von mehreren verschachtelten Transaktionen beenden<br />

und zu einem unvorhersehbaren Er gebnis führen.<br />

Öffentliche Variablen <strong>für</strong> den Transaktionsstatus<br />

Es gibt mehrere Möglichkeiten, den Status bezüglich der Transaktion zu speichern und<br />

in Haupt- und Unterformular zugänglich zu machen. Globale Variablen verwendet man<br />

besser nicht, da diese allzu leicht von einer anderen Stelle aus geändert werden können.<br />

Besser ist die Verwendung von Membervariablen in einem der beiden Formulare und<br />

ihre Veröffentlichung über entsprechende Property-Prozeduren.<br />

Der folgende Quellcode zeigt, wie der Kopf des Klassenmoduls des Hauptformulars<br />

aussieht:<br />

Option Compare Database<br />

Option Explicit<br />

Dim mDirtyForm As Boolean<br />

Dim mDeletedForm As Boolean<br />

Dim db As DAO.Database<br />

Dim wrk As DAO.Workspace<br />

Public Property Get DirtyForm() As Boolean<br />

DirtyForm = mDirtyForm<br />

End Property<br />

Public Property Let DirtyForm(bolDirtyForm As Boolean)<br />

mDirtyForm = bolDirtyForm<br />

End Property<br />

Listing 3.31: Kopf des Klassenmoduls des Formulars frmProjektzeiten_Undo<br />

Die Eigenschaft mDirtyForm machen Sie durch die nachfolgenden Property-Prozeduren<br />

von außerhalb schreib- und lesbar. Auf diese Weise können Sie auf die wichtigen In for-<br />

243


Kapitel 3 Formulare<br />

ma tionen über den Zustand der Transaktion vom Haupt- und auch vom Un ter for mu lar<br />

aus zugreifen.<br />

Die Verwendung von Property Get- und Property Let-Pro ze duren ist ein kleiner Vorgriff<br />

auf das Kapitel »Objektorientierte Programmierung« ab Seite 845.<br />

Recordset-Objekt als Datensatzquelle<br />

Weiter oben wurde bereits erwähnt, dass Transaktionen von den Vorgängen im For mu lar<br />

und im Unterformular nur etwas mitbekommen, wenn die Datensatzquelle ein Re cordset-Objekt<br />

ist.<br />

Für das Hauptformular weisen Sie diese Eigenschaft am besten direkt beim Öffnen zu.<br />

Die Prozedur, die durch die Ereigniseigenschaft Beim Öffnen ausgelöst wird, hat folgendes<br />

Aussehen:<br />

244<br />

Private Sub Form_Open(Cancel As Integer)<br />

End Sub<br />

'Recordset-, Database- und Workspace-Objekt deklarieren<br />

Dim rst As DAO.Recordset<br />

Set db = DBEngine(0)(0)<br />

Set wrk = DBEngine.Workspaces(0)<br />

'Recordset-Objekt öffnen...<br />

Set rst = db.OpenRecordset("tblProjekte", dbOpenDynaset)<br />

'...und der entsprechenden Eigenschaft des Formulars zuweisen<br />

Set Me.Recordset = rst<br />

Listing 3.32: Ereignisprozedur Form_Open des Hauptformulars frmProjektzeiten_Undo<br />

Da Sie dem Formular nun dynamisch die Datensatzquelle zuweisen, können Sie die<br />

Eigenschaft Datensatzquelle leeren. Wichtig ist, dass die Felder weiterhin an die entsprechenden<br />

Felder der Datensatzquelle gebunden sind. Anderenfalls können diese natürlich<br />

nicht die gewünschten Daten anzeigen.<br />

Neben dem Zuweisen der Datensatzquelle per Recordset-Eigenschaft ist die Deklaration<br />

des Workspace-Objekts wrk die zweite Besonderheit in dieser Prozedur. Ein benutzerdefiniertes<br />

Workspace-Objekt können Sie anschaulich als Arbeitsbereich betrachten, in<br />

dem die aktuelle Anwendung temporär abläuft.<br />

Kein neues Formular-Recordset während einer Transaktion<br />

Wenn eine Transaktion einmal gestartet ist, können Sie dem Formular kein neues<br />

Recordset-Objekt zuweisen. Diese Regel bedeutet kein besonderes Problem, da<br />

Formular und Unterformular durchaus vor der ersten Änderung der Daten und damit<br />

dem Starten der Transaktion mit den benötigten Recordsets versehen werden können.


Besonderheiten von Unterformularen<br />

Es ergibt sich nur eine Einschränkung: Sie können während einer Transaktion nicht den<br />

Datensatz im Hauptformular wechseln. Warum nicht? Weil bei der Anzeige eines neuen<br />

Datensatzes im Hauptformular auch die im Unterformular angezeigten Daten aktualisiert<br />

werden müssen. Und das geht wiederum nur durch das Zuweisen eines neuen Recordsets,<br />

das die mit dem Datensatz im Hauptformular korrespondierenden Daten enthält.<br />

Das ist aber nicht schlimm, denn dass beim Wechseln des Datensatzes die Daten des<br />

vor herigen Datensatzes gespeichert werden, sollte dem Benutzer klar sein. Die technische<br />

Umsetzung indes ist nicht ganz einfach.<br />

Fest steht nur, dass beim Wechsel des Datensatzes, der das Ereignis Beim Anzeigen des<br />

Hauptformulars auslöst, dem Un ter for mu lar ein neues Recordset zugewiesen werden<br />

muss. Um den Rest kümmern Sie sich später. Die Prozedur Form_Current sieht nun<br />

folgendermaßen aus:<br />

Private Sub Form_Current()<br />

End Sub<br />

Dim strSQL As String<br />

Dim rst As DAO.Recordset<br />

'Zusammenstellen des SQL-Strings <strong>für</strong> das Recordset<br />

strSQL = "SELECT tblProjektzeiten.ProjektID, " _<br />

& "tblProjektzeiten.ProjektzeitID, " _<br />

& "tblProjektzeiten.MitarbeiterID, " _<br />

& "tblProjektzeiten.Datum, " _<br />

& "tblProjektzeiten.Zeit, " _<br />

& "tblMitarbeiter.Telefon " _<br />

& "FROM tblProjektzeiten INNER JOIN tblMitarbeiter " _<br />

& "ON tblProjektzeiten.MitarbeiterID = " _<br />

& " tblMitarbeiter.MitarbeiterID " _<br />

& "WHERE ProjektID = " & Nz(Me!ProjektID, 0)<br />

'Öffnen des Recordset<br />

Set rst = db.OpenRecordset(strSQL, dbOpenDynaset)<br />

'Zuweisen des Recordset an das Unterformular<br />

Set Me!sfmProjektzeiten_Undo.Form.Recordset = rst<br />

Listing 3.33: Quellcode der Prozedur Form_Current des Hauptformulars<br />

Alternativ können Sie auch die gespeicherte Abfrage qryProjektzeitenMitarbeiter verwenden<br />

(siehe Abbildung 3.50). Die Prozedur sähe dann folgendermaßen aus:<br />

Private Sub Form_Current()<br />

Dim rst As DAO.Recordset<br />

'Öffnen des Recordset<br />

Set rst = db.OpenRecordset("SELECT * FROM " _<br />

245


Kapitel 3 Formulare<br />

246<br />

End Sub<br />

& "qryProjektzeitenMitarbeiter WHERE ProjektID = " _<br />

& Nz(Me!ProjektID, 0), dbOpenDynaset)<br />

'Zuweisen des Recordset an das Unterformular<br />

Set Me!sfmProjektzeiten_Undo.Form.Recordset = rst<br />

Listing 3.34: Vereinfachte und performantere Version der Prozedur Form_Current<br />

Dadurch, dass die im Unterformular angezeigten Datensätze ohnehin nach dem im<br />

Hauptformular angezeigten Projekt gefiltert werden, sind die beiden Eigenschaften<br />

Verknüpfen von und Verknüpfen nach im Unterformularsteuerelement nicht mehr<br />

notwen dig. Leeren Sie also diese beiden Eigenschaften.<br />

Dadurch müssen Sie beim Anlegen neuer Datensätze im Unterformular da<strong>für</strong> sorgen,<br />

dass diese Datensätze irgendwie mit der ProjektID des Datensatzes im Hauptformular<br />

versehen werden. Dazu jedoch später mehr.<br />

Sie kommen trotzdem nicht umhin, da<strong>für</strong> zu sorgen, dass keine Datensätze im Un ter formu<br />

lar angelegt werden, ohne dass sich ein Datensatz im Hauptformular befindet. Dazu<br />

kön nen Sie die Routine aus Listing 3.29 verwenden.<br />

Transaktionen in Formularen<br />

Nun geht es an das eigentliche Problem: Das Einfassen von Änderungen im Haupt- und<br />

Un terformular in eine Transaktion, um getätigte Änderungen <strong>für</strong> Haupt- und Unter formu<br />

lar komplett revidieren zu können.<br />

Dazu veranschaulichen Sie sich zunächst, welche Datensatzänderungen überhaupt berücksichtigt<br />

werden müssen, und leiten davon ab, in welchen Ereignisprozeduren die<br />

Transaktion gestartet, durchgeführt oder abgebrochen wird.<br />

» Hinzufügen eines Datensatzes im Hauptformular: löst Form_Dirty im Hauptformular<br />

aus.<br />

» Ändern eines Datensatzes im Hauptformular: löst Form_Dirty im Hauptformular aus.<br />

» Löschen eines Datensatzes im Hauptformular: kann nicht rückgängig gemacht werden,<br />

da das Formular unmittelbar nach dem Löschen auf einen anderen Datensatz<br />

springt.<br />

» Hinzufügen eines Datensatzes im Unterformular: löst Form_Dirty im Unterformular<br />

aus.<br />

» Ändern eines Datensatzes im Unterformular: löst Form_Dirty im Unterformular aus.<br />

» Löschen eines Datensatzes im Unterformular: löst Form_Delete im Unterformular<br />

aus.


Besonderheiten von Unterformularen<br />

Damit stehen bereits die Ereignisse fest, die eine Transaktion starten sollen. Schauen<br />

Sie sich die dadurch ausgelösten Prozeduren an. Den Anfang macht die Prozedur Form_<br />

Dirty des Hauptformulars:<br />

Private Sub Form_Dirty(Cancel As Integer)<br />

'Wenn aktueller Datensatz noch nicht geändert...<br />

If Me.DirtyForm = False Then<br />

End If<br />

End Sub<br />

'Eigenschaft DirtyForm auf True setzen<br />

Me.DirtyForm = True<br />

'Transaktion starten<br />

wrk.BeginTrans<br />

Listing 3.35: Start einer Transaktion beim Ändern oder Hinzufügen eines Datensatzes im Hauptformular<br />

Im Unterformular muss die Prozedur Form_Dirty noch ein wenig mehr leisten. Sie sorgt<br />

nicht nur da<strong>für</strong>, dass im Falle der ersten Änderung die Transaktion gestartet und die<br />

Eigenschaft DirtyForm des Hauptformulars auf True gesetzt wird.<br />

Zusätzlich prüft sie, ob es sich bei der Änderung um einen neuen Datensatz handelt.<br />

Wie weiter oben erwähnt, sind Haupt- und Unterformular nicht über die entsprechenden<br />

Werte <strong>für</strong> die Eigenschaften Verknüpfen von und Verknüpfen nach miteinander verknüpft.<br />

Der Wert des Verknüpfungsfeldes ProjektID wird nicht automatisch gefüllt. Die Prozedur<br />

übernimmt daher auch diese Aufgabe und weist dem Feld ProjektID des Unterformulars<br />

den entsprechenden Wert des Hauptformulars zu.<br />

Damit im Unterformular überhaupt eine Transaktion gestartet werden kann, sind noch<br />

einige Zeilen Code notwendig. So befindet sich im Modulkopf neben den obligatorischen<br />

Zeilen noch die Deklaration des Workspace-Objekts <strong>für</strong> das Unterformular:<br />

Option Compare Database<br />

Option Explicit<br />

Dim wrk As DAO.Workspace<br />

Dieses stellt das Form_Open-Ereignis des Unterformulars auf den gleichen Workspace<br />

wie im Hauptformular ein:<br />

Private Sub Form_Open(Cancel As Integer)<br />

End Sub<br />

Set wrk = DBEngine.Workspaces(0)<br />

Listing 3.36: Zuweisen des Workspace-Objekts<br />

247


Kapitel 3 Formulare<br />

Die Prozedur Form_Dirty ist eine von zwei Prozeduren, die vom Unterformular aus eine<br />

Transaktion starten können:<br />

248<br />

Private Sub Form_Dirty(Cancel As Integer)<br />

'Wenn es sich um einen neuen Datensatz handelt,<br />

'Wert <strong>für</strong> die Herstellung der Verknüpfung über das<br />

'Feld ProjektID zuweisen<br />

If Me.NewRecord Then<br />

End If<br />

Me!ProjektID = Me.Parent!ProjektID<br />

'Wenn noch keine Änderung im Haupt- oder Unterformular<br />

'vorliegt, DirtyForm auf True setzen und Transaktion starten<br />

If Me.Parent.DirtyForm = False Then<br />

End If<br />

End Sub<br />

Me.Parent.DirtyForm = True<br />

wrk.BeginTrans<br />

Listing 3.37: Form_Dirty-Prozedur des Unterformulars sfmProjektzeiten_Undo<br />

Die Prozedur, die durch die Beim Löschen-Eigenschaft des Unterformulars ausgelöst<br />

wird, muss sich nicht um die ProjektID scheren. Sie startet lediglich die Transaktion,<br />

falls dies noch nicht geschehen ist:<br />

Private Sub Form_Delete(Cancel As Integer)<br />

'Wenn noch keine Änderung im Haupt- oder Unterformular...<br />

If Me.Parent.DirtyForm = False Then<br />

End If<br />

End Sub<br />

Me.Parent.DirtyForm = True<br />

wrk.BeginTrans<br />

Listing 3.38: Die Prozedur Form_Delete startet gegebenenfalls eine Transaktion<br />

Abbrechen der Transaktion<br />

Das Abbrechen der Transaktion erreichen Sie durch Betätigen der Abbrechen-<br />

Schaltfläche. Die dadurch ausgelöste Prozedur prüft, ob Änderungen im Haupt- oder<br />

Unterformular durchgeführt wurden und damit eine Transaktion gestartet wurde.<br />

Falls ja, werden die Änderungen durch die Rollback-Methode des Workspace-Objekts<br />

wieder verworfen.<br />

Private Sub cmdAbbrechen_Click()<br />

'Prüfen, ob Daten geändert wurden


If Me.DirtyForm = True Then<br />

End If<br />

End Sub<br />

'Transaktion abbrechen<br />

wrk.Rollback<br />

'Formular schließen<br />

DoCmd.Close acForm, Me.Name<br />

Besonderheiten von Unterformularen<br />

Listing 3.39: Die Abbrechen-Schaltfläche verwirft alle Änderungen der aktuellen Daten des<br />

Haupt- und Unterformulars<br />

Durchführen der Transaktion<br />

Die Durchführung der Transaktion erfolgt durch Speichern des Datensatzes im Hauptformular.<br />

Dazu gibt es zum Beispiel folgende Möglichkeiten:<br />

» Schließen des Formulars mit der OK-Schaltfläche<br />

» Wechseln zu einem anderen Datensatz im Hauptformular<br />

» Schließen des Formulars auf anderem Wege (beispielsweise Betätigen des Schließen-<br />

Buttons oben rechts im Formular)<br />

In diesem Beispiel werden nur die beiden ersten Möglichkeiten zum Speichern eines<br />

Datensatzes beschrieben. Zum Schließen des Formulars mit der OK-Schaltfläche ist die<br />

Prozedur verantwortlich, die durch das Ereignis Beim Klicken der Schaltfläche ausgelöst<br />

wird. Die Prozedur prüft anhand der Eigenschaft DirtyForm, ob überhaupt Änderungen<br />

im Haupt- oder Unterformular durchgeführt wurden und dementsprechend eine<br />

Transaktion gestartet wurde.<br />

Falls ja, schließt sie die Transaktion mit der Methode CommitTrans ab.<br />

Private Sub cmdOK_Click()<br />

'Speichern der Änderungen<br />

DoCmd.RunCommand acCmdSaveRecord<br />

'Wenn Änderungen vorliegen und damit eine<br />

'Transaktion gestartet wurde:<br />

If Me.DirtyForm = True Then<br />

End If<br />

End Sub<br />

'Transaktion durchführen<br />

wrk.CommitTrans<br />

'Formular schließen<br />

DoCmd.Close acForm, Me.Name<br />

Listing 3.40: Das Schließen des Formulars mit der OK-Schaltfläche führt die Transaktion durch<br />

249


Kapitel 3 Formulare<br />

Der Wechsel zu einem anderen Datensatz im Formular löst das Ereignis Beim Anzeigen<br />

aus. Die erste Version dieser Prozedur (siehe Listing 3.33) sieht noch keine Funktion<br />

zum Abschließen einer Transaktion vor.<br />

Die folgende Variante holt dies nach:<br />

250<br />

Private Sub Form_Current()<br />

'Wenn Daten geändert und Transaktion gestartet...<br />

If Me.DirtyForm = True Then<br />

End If<br />

'aktuellen Datensatz speichern<br />

DoCmd.RunCommand acCmdSaveRecord<br />

'Transaktion durchführen<br />

wrk.CommitTrans<br />

'Formular als "gespeichert" kennzeichnen<br />

Me.DirtyForm = False<br />

If mDeletedForm = False Then<br />

Else<br />

End If<br />

End Sub<br />

'Prozedur zum Aktualisieren des Unterformulars aufrufen<br />

UnterformularAktualisieren<br />

mDeletedForm = False<br />

Listing 3.41: Abschließen der Transaktion beim Datensatzwechsel im Hauptformular<br />

Die eigentliche Funktionalität zum Aktualisieren der Datensatzquelle des Unter for mulars<br />

wird dabei in eine eigene Prozedur ausgegliedert, weil diese noch von einer weiteren<br />

Ereignisprozedur aufgerufen werden soll. Die ausgegliederte Prozedur sieht wie<br />

folgt aus:<br />

Private Sub UnterformularAktualisieren()<br />

End Sub<br />

Dim strSQL As String<br />

Dim rst As DAO.Recordset<br />

'Zusammenstellen des SQL-Strings<br />

strSQL = "SELECT * FROM qryProjektzeitenMitarbeiter " _<br />

& "WHERE ProjektID = " & Nz(Me!ProjektID, 0)<br />

'Öffnen des Recordset<br />

Set rst = db.OpenRecordset(strSQL, dbOpenDynaset)<br />

'Zuweisen des Recordset an das Unterformular<br />

Set Me!sfmProjektzeiten_Undo.Form.Recordset = rst<br />

Listing 3.42: Prozedur zum Aktualisieren des Unterformulars


Löschen des aktuellen Datensatzes im Hauptformular<br />

Besonderheiten von Unterformularen<br />

Bleibt noch ein Problem: Wenn Sie den aktuell im Hauptformular angezeigten Datensatz<br />

löschen möchten, erscheint der Fehler aus Abbildung 3.66. Das Merkwürdige ist, dass<br />

beim Löschen gar keine Transaktion per VBA gestartet wurde – woher kommt nun diese<br />

Transaktion? Offensichtlich schließt <strong>Access</strong> den Löschvorgang automatisch in eine<br />

Transaktion ein. Normalerweise würden Sie davon gar nichts mitbekommen, da innerhalb<br />

dieser Transaktion keine unerlaubten Aktionen erfolgen wie das hier bemängelte<br />

Zuweisen eines Recordset-Objekts an die entsprechende Eigenschaft.<br />

Hier liegt der Fall anders und bei genauer Betrachtung des Ablaufs der Ereignisse beim<br />

Löschen eines Datensatzes wird einiges klarer. Die Ereignisse haben folgende Reihenfolge:<br />

» Form_Delete<br />

» Form_Current<br />

» Form_BeforeDelConfirm<br />

» Form_AfterDelConfirm<br />

Dabei werden die beiden letzten Ereignisse nur ausgelöst, wenn die Einstellung Er weitert|Be<br />

arbeiten|Bestätigen|Datensatzänderungen in den <strong>Access</strong>-Optionen aktiviert ist.<br />

Abbildung 3.66: Fehler beim Löschen des Datensatzes im Hauptformular<br />

Wichtig ist zunächst, dass nach dem Delete-Ereignis der zu löschende Datensatz beziehungs<br />

weise die zu löschenden Datensätze zunächst temporär gespeichert werden, bis<br />

fest steht, dass der Löschvorgang nicht durch das Ereignis Vor Löschbestätigung des Formu<br />

lars abgebrochen wird. Und genau dieses temporäre Speichern erledigt die Trans aktion,<br />

die sich nicht mit dem Zuweisen des Recordset-Objekts zum Unterformular verträgt.<br />

Das Problem lösen Sie, indem Sie den Zeitpunkt der Zuweisung des Recordset-Objekts<br />

ein fach nach hinten verschieben – vom Form_Current- ins Form_AfterDelConfirm-<br />

Ereignis. Dazu verwenden Sie die folgenden Prozeduren.<br />

Die Prozedur Form_Delete stellt die Variable mDeletedForm auf den Wert True ein. Diese<br />

Eigenschaft wird in der Prozedur Form_Current ausgewertet; wenn sie den Wert True enthält,<br />

wird das Aktualisieren des Unterformulars unterbunden (siehe auch Listing 3.41).<br />

251


Kapitel 3 Formulare<br />

252<br />

Private Sub Form_Delete(Cancel As Integer)<br />

End Sub<br />

mDeletedForm = True<br />

Listing 3.43: Setzen einer Eigenschaft, an der andere Prozeduren den laufenden Löschvorgang<br />

erkennen können<br />

Die Prozedur Form_BeforeDelConfirm können Sie theoretisch auch weglassen. In diesem<br />

Fall unterbindet sie die Standardmeldung von <strong>Access</strong> beim Löschen des Datensatzes<br />

(siehe Abbildung 3.67).<br />

Private Sub Form_BeforeDelConfirm(Cancel As Integer, Response As Integer)<br />

End Sub<br />

Response = acDataErrContinue<br />

Listing 3.44: Verhindern der Rückfrage beim Löschen<br />

Abbildung 3.67: Standardmeldung von <strong>Access</strong> beim Löschen von Daten<br />

Die Prozedur Form_AfterDelConfirm ist wiederum sehr wichtig: Sie wird nur ausgelöst,<br />

wenn Sie wie in Listing 4.42 die <strong>Access</strong>-Meldung unterbinden oder wenn Sie den<br />

Löschvorgang im Fall der Anzeige bestätigen.<br />

Die Prozedur ruft die Routine auf, die das Unterformular aktualisiert, und holt damit genau<br />

den Vorgang nach, der durch Setzen der Variablen mDeletedForm im Form_Current-<br />

Ereignis unterbunden wurde.<br />

Private Sub Form_AfterDelConfirm(Status As Integer)<br />

End Sub<br />

UnterformularAktualisieren<br />

Listing 3.45: Aktualisieren des Unterformulars nach vollendetem Löschvorgang<br />

Restarbeiten<br />

Damit das Beispiel wie beschrieben funktioniert, brauchen Sie lediglich die Eigenschaften<br />

der Beziehungen zwischen den im Haupt- und Unterformular angezeigten Tabellen so<br />

anzupassen, dass beim Löschen des Datensatzes im Hauptformular direkt die entsprechenden<br />

Unterformulareinträge mitgelöscht werden. Dazu reicht das Setzen der<br />

Eigenschaft Löschweitergabe an verwandte Datensätze im Eigenschaftsfenster der passenden<br />

Beziehung (siehe Abbildung 3.68).


Abbildung 3.68: Aktivieren der Löschweitergabe zwischen verknüpften Tabellen<br />

Einsatz in eigenen Formularen<br />

Eingabevalidierung<br />

Dem Einsatz dieser Technik in Ihren eigenen Formularen steht prinzipiell nichts im<br />

We ge. Sie müssen lediglich alle in den beiden Formularmodulen enthaltenen De klara<br />

tionen und Prozeduren in Ihre Formulare übertragen und einige Zeilen Code den<br />

Ge gebenheiten Ihrer eigenen Datenbank anpassen – etwa die Datenherkünfte <strong>für</strong><br />

Haupt- und Unterformular und die Zeile, die beim Anlegen eines neuen Datensatzes im<br />

Unterformular den Wert <strong>für</strong> den Fremdschlüssel festlegt (siehe Listing 3.37).<br />

3.8 Eingabevalidierung<br />

Zur defensiven Programmierung gehört, dass Sie keine ungültigen Daten zulassen. Das<br />

fängt bei der Eingabe von Daten in Formularen an. Dieser Teil beschreibt die unterschiedlichen<br />

Möglichkeiten zur Validierung der Eingabe. Die wichtigste Frage bei der<br />

Validierung ist nicht, wie diese erfolgen soll, sondern zu welchem Zeitpunkt. Die folgenden<br />

drei Regeln sind praxiserprobt:<br />

» Fehlerhafte Eingaben wie Zeichenketten in Datumsfelder werden sofort geahndet.<br />

» Fehlende Eingaben werden erst beim Bestätigen des Datensatzes bemängelt.<br />

» Die Handhabung von abhängigen Feldern ist Geschmackssache – Beispiel Startdatum<br />

und Enddatum. Am einfachsten ist eine Prüfung beim Bestätigen des Datensatzes.<br />

3.8.1 Validieren direkt bei der Eingabe<br />

Das Validieren unmittelbar nach der Eingabe erfolgt in einer Prozedur, die durch die<br />

Ereigniseigenschaft Vor Aktualisierung des jeweiligen Steuerelements ausgeführt wird.<br />

Eine solche Routine könnte beispielsweise wie folgt aussehen:<br />

253


Kapitel 3 Formulare<br />

254<br />

Private Sub Projekt_BeforeUpdate(Cancel As Integer)<br />

If IsNumeric(Left(Me!Projekt, 1)) Then<br />

End If<br />

End Sub<br />

MsgBox "Der Projektname muss mit einem Buchstaben beginnen.", _<br />

vbOKOnly + vbExclamation, "Eingabefehler"<br />

Cancel = True<br />

Exit Sub<br />

Listing 3.46: Validierung bei der Eingabe<br />

Die Routine prüft, ob die in das Feld Projekt eingegebenen Werte nicht mit einer Zahl<br />

anfangen. Sollte dies doch der Fall sein, erscheint eine entsprechende Meldung (siehe<br />

Abbildung 3.69).<br />

Abbildung 3.69: Projektbezeichnungen, die mit einer Zahl beginnen, sind nicht erlaubt (frm ­<br />

Projekte Detailansicht)<br />

3.8.2 Validieren vor dem Speichern<br />

Vor dem Speichern werden vor allem Pflichtfelder geprüft. Falls im obigen Formular die<br />

Felder Projekt und Startdatum Pflichtfelder wären, würde die Prüfung wie folgt aussehen:<br />

Private Function Validierung() As Boolean<br />

If IsNull(Me!Projekt) Then<br />

End If<br />

MsgBox "Bitte geben Sie einen Projektnamen ein.", _<br />

vbOKOnly + vbExclamation, "Fehlende Eingabe"<br />

Me!Projekt.SetFocus<br />

Exit Function<br />

If IsNull(Me!Startdatum) Then<br />

MsgBox "Bitte geben Sie das Startdatum ein.", _<br />

vbOKOnly + vbExclamation, "Fehlende Eingabe"


End If<br />

Me!Startdatum.SetFocus<br />

Exit Function<br />

If Not IsNull(Me!Startdatum) And Not IsNull(Me!Enddatum) Then<br />

End If<br />

If Me!Startdatum > Me!Enddatum Then<br />

End If<br />

MsgBox "Das Enddatum darf nicht hinter " _<br />

& "dem Startdatum liegen.", _<br />

vbOKOnly + vbExclamation, "Falsche Datumsangabe"<br />

Me!Enddatum.SetFocus<br />

Exit Function<br />

Validierung = True<br />

End Function<br />

Listing 3.47: Validierung vor dem Speichern des Datensatzes<br />

Eingabevalidierung<br />

Die Prozedur prüft von oben nach unten alle Pflichtfelder. Sobald sie auf eines trifft, das<br />

leer ist, gibt sie eine Fehlermeldung aus, setzt den Fokus auf das Feld mit dem fehlenden<br />

Inhalt und bricht die Prozedur ab. Außerdem wird der Funktionswert nur auf True<br />

gesetzt, wenn alle Validierungen erfolgreich verlaufen sind.<br />

Abhängige Felder prüfen<br />

Im letzten Teil der Routine aus Listing 3.47 finden Sie ein Beispiel, wie sich abhängige<br />

Felder prüfen lassen. In diesem Beispiel wird sichergestellt, dass das Startdatum nicht<br />

hinter dem Enddatum liegt.<br />

Aufruf der Validierung vor dem Speichern<br />

Nun müssen Sie diese Funktion nur noch von geeigneter Stelle aus aufrufen. Leider<br />

reicht es nicht, dies mit der Ereigniseigenschaft Vor Aktualisierung abzudecken. Die dazugehörende<br />

Ereignisprozedur enthält auch den passenden Cancel-Parameter, der das<br />

Speichern des Datensatzes bei falschen oder fehlenden Daten abbrechen könnte.<br />

Es gibt allerdings eine Schwachstelle: Theoretisch wird diese Routine zwar durch alle<br />

relevanten Vorgänge wie Wechsel des Datensatzes oder Schließen des Formulars aufgerufen<br />

– aber wenn das Schließen einmal ausgelöst wurde, hält auch das Setzen des<br />

Cancel-Parameters der Form_BeforeUpdate-Prozedur das Schließen nicht mehr auf.<br />

Somit würde in diesem Fall ein Datensatz mit falschen oder fehlenden Daten gespeichert.<br />

Daher rufen Sie die Funktion Validierung von zwei Prozeduren aus auf: Von der Form_<br />

BeforeUpdate-Prozedur sowie von der cmdOK_Click-Prozedur aus. Um auszuschließen,<br />

255


Kapitel 3 Formulare<br />

dass das Formular anders als mit der OK-Schaltfläche geschlossen wird, stellen Sie<br />

noch die Eigenschaft Schließen-Schaltfläche auf den Wert Nein ein.<br />

Die beiden Ereignisprozeduren sehen schließlich wie folgt aus:<br />

256<br />

Private Sub cmdOK_Click()<br />

If Validierung = True Then<br />

End If<br />

End Sub<br />

DoCmd.Close acForm, Me.Name<br />

Private Sub Form_BeforeUpdate(Cancel As Integer)<br />

If Validierung = False Then<br />

End If<br />

End Sub<br />

Cancel = True<br />

Listing 3.48: Diese beiden Routinen rufen die Funktion Validierung auf<br />

Alternativ können versierte Benutzer das Formular natürlich noch mit Strg + F4 schließen.<br />

Um dem vorzubeugen, verwenden Sie eine Prozedur, die beim Ereignis Bei Taste<br />

ausgelöst wird und das Betätigen dieser Tastenkombination unterbindet.<br />

Gleichzeitig müssen Sie die Eigenschaft Tastenvorschau auf den Wert Ja einstellen:<br />

Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)<br />

If KeyCode = 115 And Shift = 2 Then<br />

End If<br />

End Sub<br />

KeyCode = 0<br />

Listing 3.49: Unterbinden der Tastenkombination zum Schließen eines Formulars<br />

Tastenkombinationen sperren und bereitstellen mit dem<br />

AutoKeys-Makro<br />

Davon abgesehen ist das eine gute Gelegenheit, hier das AutoKeys-Makro vorzustellen:<br />

Das ist nämlich neben dem AutoExec-Makro das zweite Makro, das aufgrund seines<br />

Namens automatisch zu bestimmten Gelegenheiten ausgeführt wird – in diesem Fall<br />

beim Eingeben von Tastenkombinationen.<br />

Sie können damit nämlich beliebige Tastenkombinationen festlegen. Legen Sie einfach<br />

ein neues Makro an und fügen Sie pro Tastenkombination eine Anweisung hinzu. Die<br />

Tastenkombination tragen Sie dabei in der Spalte Makroname ein und verwenden dabei<br />

die Syntax der SendKeys-An wei sung von VBA – die Kürzel <strong>für</strong> die Tastenkombinationen


Eingabevalidierung<br />

finden Sie in der VBA-On line hilfe. Die Tastenkombination Strg + F4 fügen Sie dort etwa<br />

so ein: ^{F4}.<br />

Lassen Sie die Spalte Aktion leer, veranlasst <strong>Access</strong> beim Betätigen dieser Tastenkombination<br />

schlicht nichts mehr – genau wie gewünscht. Natürlich können Sie auf diese<br />

Weise auch Tastenkombinationen <strong>für</strong> richtige Funktionen festlegen. Das beschriebene<br />

Makro finden Sie in der Beispieldatenbank.<br />

3.8.3 Sonderfälle beim Validieren<br />

Mit der oben genannten Vorgehensweise können Sie nicht alle möglichen<br />

Eingabefehler abfangen. Wenn im Tabellenentwurf festgelegte Einschränkungen (siehe<br />

»Integritätsregeln« auf Seite 81) bei der Eingabe oder beim Speichern des Datensatzes<br />

nicht berücksichtigt werden, werden die dadurch hervorgerufenen Meldungen vor dem<br />

Auslösen der Vor Aktualisierung-Ereignisprozeduren von Steuerelementen und For mularen<br />

angezeigt.<br />

Das ist zum Beispiel der Fall, wenn Sie einen Text in ein Da tums feld ein geben (siehe<br />

Abbildung 3.70).<br />

Abbildung 3.70: <strong>Access</strong>-eigene Validierungsmeldung<br />

Wenn Sie diese Meldungen umgehen möchten, verwenden Sie die Bei Fehler-Er eig nisei<br />

gen schaft des Formulars. Hier müssen Sie zwar ein wenig mehr Aufwand betreiben<br />

als bei den oben beschriebenen Validierungsfunktionen, aber da<strong>für</strong> ersetzen Sie damit<br />

auch die <strong>Access</strong>-eigenen Validierungsmeldungen.<br />

Im Formular frmProjekteDetailansicht kann es etwa passieren, dass eines der Felder<br />

Startdatum oder Enddatum mit einem Wert bestückt wird, der nicht den Datentyp Datum/<br />

Uhrzeit enthält, oder dass kein Kunde <strong>für</strong> das Projekt ausgewählt wird. Beide Fehler würden<br />

die Integritätsregeln verletzen und eine <strong>Access</strong>-eigene Meldung heraufbeschwören.<br />

Diese Meldungen können Sie nur im Bei Fehler-Ereignis abfangen. Die folgende Routine<br />

wird durch dieses Ereignis ausgelöst und prüft, ob eines der beiden Datumsfelder einen<br />

ungültigen Wert enthält. Falls ja, gibt es eine entsprechende Meldung aus; der Fokus<br />

wird dann automatisch auf das betroffene Steuerelement gesetzt.<br />

Damit Sie auf den richtigen Fehler reagieren, prüfen Sie in einer Select Case-<br />

Verzweigung die in DataErr enthaltene Fehlernummer und verzweigen entsprechend.<br />

Mit der An wei sung Screen.ActiveControl ermitteln Sie, bei welchem Steuerelement der<br />

257


Kapitel 3 Formulare<br />

Fehler ausgelöst wurde, und behandeln den Fehler dem betroffenen Feld entsprechend.<br />

Ähnlich ist es beim Speichern des Datensatzes ohne Füllen des Pflichtfeldes KundeID. Die<br />

Fehlernummer lautet 3201 und Sie ermitteln ganz einfach, ob das Feld KundeID <strong>für</strong> diesen<br />

Fehler verantwortlich ist, indem Sie dessen Inhalt überprüfen. Ist dieser Null, muss<br />

der Benutzer den entsprechenden Kunden noch nachreichen. Wenn Sie nicht sicher sind,<br />

welche Fehlernummer sich hinter den eingebauten Feh ler mel dungen verbirgt, lassen Sie<br />

sich in dieser Routine einfach den Wert DataErr mit der Debug.Print-Methode ausgeben.<br />

258<br />

Private Sub Form_Error(DataErr As Integer, Response As Integer)<br />

End Sub<br />

Dim ctl As Control<br />

Select Case DataErr<br />

Case 2113<br />

Set ctl = Screen.ActiveControl<br />

Select Case ctl.Name<br />

Case "Startdatum", "Enddatum"<br />

End Select<br />

Case 3201<br />

End Select<br />

MsgBox "Bitte geben Sie ein Datum im Format " _<br />

"'dd.mm.jjjj' ein.", vbOKOnly + vbExclamation, _<br />

"Eingabefehler"<br />

Response = acDataErrContinue<br />

Exit Sub<br />

If Me!KundeID = 0 Then<br />

End If<br />

MsgBox "Bitte wählen Sie einen Kunden <strong>für</strong> dieses " _<br />

& "Projekt aus.", vbOKOnly + vbExclamation, _<br />

"Fehlende Eingabe"<br />

Response = acDataErrContinue<br />

Me!KundeID.SetFocus<br />

Exit Sub<br />

Listing 3.50: Behandlung von Verletzungen der Integrität bei der Dateneingabe<br />

3.9 Suchen in Formularen<br />

Die Suche nach bestimmten Daten erfordert je nach Anwendungsfall unterschiedliche<br />

Maßnahmen. In den folgenden Abschnitten finden Sie zwei Möglichkeiten, die sich <strong>für</strong><br />

viele Anwendungen eignen – eine Schnellauswahl per Kombinationsfeld und ein Text feld<br />

zum schnellen Filtern von Listenfeldern.


3.9.1 Schnelles Suchen in Formularen<br />

Suchen in Formularen<br />

Für die Suche in Tabellen, Abfragen und Formularen bietet <strong>Access</strong> ab Version 2007 ein<br />

unscheinbares Text feld im Navigationsbereich dieser Objekte. Hier geben Sie einfach<br />

den gesuchten Be griff Buchstabe <strong>für</strong> Buchstabe ein und <strong>Access</strong> sucht die jeweils nächste<br />

Fundstelle in den Daten der Datensatzquelle des Formulars (siehe Abbildung 3.71).<br />

Das Betätigen der Ein ga betaste bewirkt das Aufsuchen der nächsten Fundstelle.<br />

Der Nachteil dieses Suchfelds ist, dass es die Suche nicht auf bestimmte Datenfelder<br />

begrenzt, sondern einfach in allen Steuerelementen des Formulars sucht – und das ist<br />

nicht immer unbedingt erwünscht.<br />

Abbildung 3.71: Die eingebaute Suche von <strong>Access</strong><br />

3.9.2 Schnelles Filtern in der Datenblattansicht<br />

Seit der Version 2007 liefert <strong>Access</strong> eine Filter-Funktion <strong>für</strong> die Datenblattansicht<br />

von Tabellen, Abfragen und Formularen mit. Sie rufen diese Funktion über die kleine<br />

Schaltfläche mit dem Pfeil nach unten in den Spaltenköpfen der Datenblattansicht auf<br />

(siehe Abbildung 3.72).<br />

Die Filterkriterien unterscheiden sich je nach Datentyp. Sehr nützlich ist der Datumsfilter:<br />

Er bietet Vergleichskriterien wie morgen, heute, gestern, nächste Woche, diese Woche,<br />

letz te Woche und so weiter. Dieses Feature liefert einen guten Grund, dem Benutzer<br />

Da ten in der Datenblattansicht statt in einem Endlosformular zu präsentieren. Die Bedie<br />

nung dieses Filters ist im Übrigen selbsterklärend.<br />

3.9.3 Schnellauswahl per Kombinationsfeld<br />

Eine der einfachsten Möglichkeiten zum Einbau einer Suche ist ein Kombinationsfeld zur<br />

Auswahl von Werten aus einem oder aus mehreren kombinierten Feldern. Das folgende<br />

Beispiel basiert auf einem Formular zum Bearbeiten von Kontakten.<br />

259


Kapitel 3 Formulare<br />

Abbildung 3.72: Filtern von Datenblättern<br />

Die Schnellauswahl per Kombinationsfeld ist nur dann sinnvoll, wenn die gewünschten<br />

Datensätze nach einem einzigen Kriterium durchsucht werden – beispielsweise nach<br />

dem Nachnamen eines Kontaktes.<br />

Diese Variante finden Sie im folgenden Beispiel: Das Formular aus Abbildung 3.73 enthält<br />

alle Felder der Tabelle tblKontakte, zwei Schaltflächen mit der Beschriftung OK und<br />

Abbrechen sowie ein Kombinationsfeld namens cboSchnellsuche.<br />

Abbildung 3.73: Formular mit Schnellauswahl in der Entwurfsansicht<br />

Das Kombinationsfeld verwendet die Abfrage aus Abbildung 3.74 als Datensatzherkunft.<br />

Die Abfrage enthält ein Feld mit dem Primärschlüsselfeld der Tabelle tblKontakte sowie<br />

ein aus den beiden Feldern Nachname und Vorname zusammengesetztes Feld, wobei die<br />

beiden Werte durch ein Komma getrennt werden.<br />

260


Suchen in Formularen<br />

Abbildung 3.74: Datensatzherkunft des Kombinationsfeldes zur Schnellauswahl von Kontakten<br />

Damit lassen sich mit dem Kombinationsfeld alle Einträge der Tabelle tblKontakte in<br />

der Form , anzeigen. Damit das Formular auch den ausgewählten<br />

Datensatz einblendet, fügen Sie der Ereigniseigenschaft Nach Aktualisierung des<br />

Kombinationsfeldes die folgende Prozedur hinzu:<br />

Private Sub cboSchnellauswahl_AfterUpdate()<br />

End Sub<br />

Me.Recordset.FindFirst "KontaktID = " & Me!cboSchnellauswahl<br />

Listing 3.51: Datensatz suchen <strong>für</strong> Formulare mit Recordset-Eigenschaft<br />

Das Auswählen eines Eintrags des Kombinationsfeldes führt nun zur Anzeige des gewünsch<br />

ten Datensatzes im Formular (siehe Abbildung 3.75).<br />

Abbildung 3.75: Die Schnellsuche im Einsatz (frmSchnellsuchePerKombinationsfeld)<br />

Es fehlt nur noch die Ak tu alisierung der Datensatzherkunft des Kombinationsfeldes<br />

beim Ändern, Hinzufügen oder Löschen von Datensätzen im Formular. Diese Aufgabe<br />

261


Kapitel 3 Formulare<br />

übernimmt die folgende Prozedur, die durch die Ereigniseigenschaft Beim Anzeigen des<br />

Formulars ausgelöst wird:<br />

262<br />

Private Sub Form_Current()<br />

End Sub<br />

'Aktualisieren des Inhalts des Kombinationsfeldes<br />

Me!cboSchnellauswahl.Requery<br />

Listing 3.52: Aktualisieren des Kombinationsfeldes<br />

3.9.4 Schnelles Filtern von Listenfeldern<br />

Listenfelder kommen oft zum Einsatz, wenn Datensätze in einer Übersicht zur Auswahl<br />

angeboten werden sollen. Wenn viele Datensätze zur Auswahl bereitstehen, macht eine<br />

Möglichkeit zum Filtern der Datensätze Sinn.<br />

Die üblichen Filter-Funktionen bestehen aus verschiedenen Textfeldern zur Eingabe der<br />

Filterkriterien und einer Schaltfläche zum Auslösen des Filtervorgangs mit den neuen<br />

Parametern. Je mehr Felder man mit einer solchen Filter-Funktion abdeckt, desto<br />

mächtiger ist diese – da<strong>für</strong> benötigt man aber auch entsprechend viele Steuerelemente,<br />

durch die sich der Benutzer erst einmal hindurchkämpfen muss.<br />

In vielen Fällen ist das gar nicht notwendig; dort lässt sich eine viel handlichere Lösung<br />

einsetzen: Stellen Sie dem Benutzer einfach ein Textfeld bereit, das in beliebig vielen<br />

Feldern des Listenfeldes sucht und unmittelbar nach der Eingabe eines jeden<br />

Buchstabens die angezeigten Datensätze filtert.<br />

Dazu ist neben ein wenig Routinearbeit noch ein kleiner Trick notwendig – sehen Sie<br />

einfach selbst. Abbildung 3.76 zeigt das fertige Formular in der Entwurfsansicht. Es<br />

enthält ein Lis ten feld zur Anzeige der Datensätze, eine Schaltfläche zum Anzeigen der<br />

Detailansicht des markierten Datensatzes und ein Textfeld zur Eingabe des Suchbegriffs.<br />

Abbildung 3.76: Listenfeld mit Schnellfilter


Suchen in Formularen<br />

Das erste Textfeld namens txtSuche dient der Eingabe des Suchbegriffs. Der Inhalt des<br />

Listenfeldes lstKontakte soll nach jeder Änderung des im Textfeld txtSuche enthaltenen<br />

Suchbegriffs aktualisiert werden.<br />

Das Listenfeld bezieht seine Daten aus der Abfrage qryKontakteListenfeld (siehe<br />

Abbildung 3.77). Die Abfrage enthält die anzuzeigenden Felder in der gewünschten Reihen<br />

fol ge und Sortierung.<br />

Damit das Listenfeld nach jeder Änderung des Inhalts des Textfeldes txtSuche aktualisiert<br />

wird, verwenden Sie eine sonst recht stiefmütterlich behandelte Ereigniseigenschaft.<br />

Die Ereigniseigenschaft Bei Änderung des Feldes zur Eingabe des Suchbegriffs wird bei<br />

jeder Änderung des Inhalts ausgelöst. Für die entsprechende Ereignisprozedur fügen<br />

Sie den folgenden Code ein:<br />

Abbildung 3.77: Datensatzherkunft des Listenfeldes zur Anzeige der Kontakte<br />

Private Sub txtSuche_Change()<br />

End Sub<br />

Dim strSuchbegriff As String<br />

'Suchbegriff in Variable speichern<br />

strSuchbegriff = Me!txtSuche.Text<br />

'Neue Datensatzherkunft zuweisen<br />

Me!lstKontakte.RowSource = "SELECT * FROM qryKontakteListenfeld " _<br />

"WHERE Nachname LIKE '" & strSuchbegriff & "*'"<br />

'Inhalt des Listenfeldes aktualisieren<br />

Me!lstKontakte.Requery<br />

Listing 3.53: Prozedur zum Filtern des Listenfeldes<br />

Der Trick ist, dass Sie nicht den Inhalt des Textfeldes über die Eigenschaft Value auswerten,<br />

sondern die Text-Eigenschaft verwenden. Die Value-Eigenschaft wird nämlich erst<br />

nach dem Aktualisieren des Textfeldes auf den tatsächlichen Inhalt eingestellt.<br />

263


Kapitel 3 Formulare<br />

Der Abfrageausdruck ermittelt alle Datensätze der Abfrage qryKontakteListenfeld, in denen<br />

der Nachname mit dem im Textfeld angegebenen Ausdruck beginnt.<br />

Der Vollständigkeit halber finden Sie hier noch den Code zum Anzeigen des im Listenfeld<br />

ausgewählten Kontaktes. Der Kontakt soll im Formular aus dem vorherigen Beispiel<br />

angezeigt werden. Zwei Aktionen sollen dazu führen: ein Doppelklick auf das Listenfeld<br />

und ein Klick auf die Schaltfläche cmdAnzeigen. Die entsprechenden Ereignisse sollen<br />

prinzipiell die gleiche Aktion auslösen – daher legen Sie da<strong>für</strong> eine neue Prozedur an<br />

und rufen diese über die beiden Ereignisprozeduren auf:<br />

264<br />

Private Sub cmdAnzeigen_Click()<br />

End Sub<br />

Anzeigen<br />

Private Sub lstKontakte_DblClick(Cancel As Integer)<br />

End Sub<br />

Anzeigen<br />

Private Sub Anzeigen()<br />

If Not IsNull(Me!lstKontakte) Then<br />

End If<br />

End Sub<br />

DoCmd.OpenForm "frmSchnellsuchePerKombifeld", _<br />

WhereCondition:="KontaktID = " & Me!lstKontakte, _<br />

WindowMode:=acDialog<br />

Listing 3.54: Prozeduren zum Anzeigen der Details eines Kontaktdatensatzes


Copyright<br />

Daten, Texte, Design und Grafiken dieses eBooks, sowie die eventuell<br />

angebotenen eBook-Zusatzdaten sind urheberrechtlich geschützt. Dieses eBook<br />

stellen wir lediglich als persönliche Einzelplatz-Lizenz zur Verfügung!<br />

Jede andere Verwendung dieses eBooks oder zugehöriger Materialien und<br />

Informationen, einschließlich<br />

der Reproduktion,<br />

der Weitergabe,<br />

des Weitervertriebs,<br />

der Platzierung im Internet, in Intranets, in Extranets,<br />

der Veränderung,<br />

des Weiterverkaufs und<br />

der Veröffentlichung<br />

bedarf der schriftlichen Genehmigung des Verlags. Insbesondere ist die<br />

Entfernung oder Änderung des vom Verlag vergebenen Passwortschutzes<br />

ausdrücklich untersagt!<br />

Bei Fragen zu diesem Thema wenden Sie sich bitte an: info@pearson.de<br />

Zusatzdaten<br />

Möglicherweise liegt dem gedruckten Buch eine CD-ROM mit Zusatzdaten bei.<br />

Die Zurverfügungstellung dieser Daten auf unseren Websites ist eine freiwillige<br />

Leistung des Verlags. Der Rechtsweg ist ausgeschlossen.<br />

Hinweis<br />

Dieses und viele weitere eBooks können Sie rund um die Uhr und legal auf<br />

unserer Website herunterladen:<br />

http://ebooks.pearson.de

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!