Access 2010 Grundlagen für Entwickler - Addison-Wesley
Access 2010 Grundlagen für Entwickler - Addison-Wesley
Access 2010 Grundlagen für Entwickler - Addison-Wesley
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 ObjektDesigner|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 ObjektDesigner|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 ActiveXSteuerelement einfügen<br />
an, den Sie mit dem Ribbon-Eintrag Entwurf|Steuerelemente|ActiveXSteuerelement<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