Home > Artikel > Ausgabe 5/2011 > Werte zu Kombinationsfeldern hinzufügen

Werte zu Kombinationsfeldern hinzufügen

  PDF ansehen

  Download PDF und Beispieldatenbank

Kombinationsfelder bieten meist Daten aus Lookup-Tabellen zur Auswahl an. Das bedeutet, dass Sie damit etwa die Anrede oder den Titel einer Person festlegen können, wobei Anreden und Titel in separaten Tabellen gespeichert sind. Das ist schon hilfreich. Noch praktischer wäre es allerdings, wenn Sie neue Einträge für die separaten Tabellen direkt über das Kombinationsfeld eintragen könnten. Wie dies funktioniert, zeigt dieser Artikel.

Beispieldatenbank

Die Beispiele zu diesem Artikel finden Sie in der Datenbank 1105_KombifelderErweitern.mdb.

Lookup-Tabellen

Wie Sie Lookuptabellen anlegen, haben Sie bereits in Anreden und Co. mit Wertlisten oder ­Lookup-Tabellen verwalten erfahren. Der Benutzer kann dort über ein Kombinationsfeld auf einfache Weise beispielsweise einen der Werte Herr oder Frau als Anrede für eine Person auswählen.Das ist natürlich kein besonders gutes Beispiel, wenn es darum geht, die Daten einer Lookup-Tabelle zu erweitern. Interessanter sind Kandidaten wie Abteilungen, Funktionen, Kategorien et cetera.

Im Rahmen dieses Artikels kümmern wir uns um die Artikelkategorien der Südsturm-Datenbank und wollen diese über die Benutzeroberfläche erweitern. Dabei interessieren uns besonders die beiden Tabellen tblArtikel und tblKategorien. Die Tabelle tblArtikel enthält ein Feld namens KategorieID, mit dem der Primärschlüsselwert der passenden Kategorie der Tabelle tblKategorien festgelegt werden kann (siehe Bild 1).

Artikel und Kategorien werden über das Fremdschlüsselfeld KategorieID verknüpft

Bild 1: Artikel und Kategorien werden über das Fremdschlüsselfeld KategorieID verknüpft

Damit der Benutzer die Kategorie einfach per Nachschlagefeld beziehungsweise Kombinationsfeld auswählen kann, wurde das Feld KategorieID wie in ?[basics] Nachschlagefeld für eine 1:n-Beziehung einrichten beschrieben in ein Nachschlagefeld umgewandelt.

Formular zur Anzeige von Artikeln und Kategorien

Das Formular aus Bild 2 heißt frmArtikelUndKategorien und verwendet die Tabelle tblArtikel als Datenherkunft. Um das Beispiel einfach zu halten, haben wir einfach nur die drei Felder ArtikelID, Artikelname und KategorieID in den Detailbereich der Entwurfsansicht des Formulars gezogen. Dadurch, dass das Feld KategorieID bereits im Tabellenentwurf als Nachschlagefeld definiert wurde, wird es auch im Formular gleich als Kombinationsfeld ausgeführt. Wenn Sie nun in die Formularansicht wechseln, können Sie vorhandene Artikel bearbeiten oder neue Artikel eingeben und dabei die Kategorie bequem per Kombinationsfeld auswählen (siehe Bild 3).

Kombinationsfeldeinträge auswählen

Bild 2: Kombinationsfeldeinträge auswählen

Das Beispielformular in der Entwurfsansicht

Bild 3: Das Beispielformular in der Entwurfsansicht

Beim Anlegen der Steuerelemente, die an die Felder ArtikelID, Artikelname und KategorieID gebunden sind, vergibt Access eben diese Bezeichnungen als Steuerelementnamen. Damit wir später im Artikel besser zwischen den Feldern und Steuerelementen unterscheiden können, erweitern Sie die Namen der Steuerelemente um entsprechende Präfixe, also txtArtikelID, txtArtikelname und cboKategorieID. Mehr zu Präfixen erfahren Sie unter Konventionen.

Neue Kategorien eingeben

Was aber, wenn Sie nun einen Artikel anlegen, der sich in keine der vorhandenen Kategorien einsortieren lässt? Ganz klar: Der Benutzer öffnet die Tabelle tblKategorien, trägt die neue Kategorie ein und kann diese dann nach dem Aktualisieren des Formulars frmArtikelUndKategorien auswählen ... oder doch nicht? Nein! Der Benutzer soll auf gar keinen Fall direkt auf die Tabellen einer Anwendung zugreifen. Sie als Entwickler dürfen natürlich in Ausnahmefällen direkt Daten in Tabellen bearbeiten, aber dem Benutzer sollten Sie geeignete Formulare zur Verfügung stellen.

Es gibt nun zwei Möglichkeiten zur Eingabe von Kategorien:

  • Sie legen ein neues Formular an, das an die Tabelle tblKategorien gebunden ist und erlauben darüber das Eingeben und Bearbeiten der Kategorien. Dieses Formular können Sie beispielsweise über eine Schaltfläche rechts vom Kombinationsfeld öffnen.
  • Sie erlauben dem Benutzer, neben den vorhanden Kategorien auch neue Kategorien direkt in das Kombinationsfeld einzugeben. Diese werden dann, gegebenenfalls nach Rückfrage, in der Tabelle tblKategorien gespeichert.

Beide Varianten haben Vorteile: Die erste erlaubt gleichzeitig, die bestehenden Kategorien zu bearbeiten oder auch mal gleich mehrere neue Kategorien einzugeben. Die zweite ist direkter: Der Benutzer braucht nur den Kategorietext einzugeben und diese wird direkt für den aktuellen Artikel übernommen. In diesem Artikel betrachten wir die zweite Methode, die erste schauen wir uns in einem weiteren Artikel namens Meldungsfenster anzeigen und auswerten an.

Kombinationsfeld aufbohren

Ob der Benutzer neue Werte in ein Kombinationsfeld eingeben kann, hängt in erster Linie von der Eigenschaft Nur Listeneinträge ab (siehe Bild 4). Nun könnten Sie auf die Idee kommen, dass wir diese Eigenschaft für unsere Zwecke, nämlich im laufenden Betrieb direkt neue Kategorien in das Kombinationsfeld einzugeben, auf den Wert Nein einstellen müssen. Der Versuch scheitert: Die Eigenschaft darf laut der dabei erscheinenden Meldung nur auf Nein eingestellt werden, wenn die erste sichtbare Spalte die gebundene Spalte ist.

Die Eigenschaft Nur Listeneinträge

Bild 4: Die Eigenschaft Nur Listeneinträge

In unserem Fall enthält die erste Spalte der Datensatzherkunft des Kombinationsfeldes das Feld KategorieID, angezeigt wird aber die zweite Spalte Kategoriename. Die Eigenschaft Nur Listeneinträge könnten Sie nur auf Nein einstellen, wenn die KategorieID als erstes Feld im Kombinationsfeld angezeigt würde.

Das lässt sich ändern, indem Sie die Eigenschaft Spaltenbreiten auf einen Wert wie 1cm ändern – die Daten des Feldes KategorieID werden dann auf einer Breite von einem Zentimeter angezeigt, das Feld Kategoriename nimmt den verbleibenden Platz der aufgeklappten Liste ein (im Steuerelement selbst wird nun nur noch die KategorieID angezeigt).

Nun lässt sich die Eigenschaft Nur Listeneinträge auf den Wert Nein einstellen. Ist es nun auch möglich, neue Werte für die gebundene Spalte des Steuerelements, also für die KategorieID, einzugeben? Nein: Das Feld KategorieID ist als Fremdschlüsselfeld zum gleichnamigen Feld der Tabelle tblKategorien ausgelegt und gleichzeitig gibt es eine mit referentieller Integrität festgelegte Beziehung zwischen diesen beiden Feldern.

Dies besagt, dass das Fremdschlüsselfeld KategorieID nur den Wert Null oder einen der Werte des Feldes KategorieID der Tabelle tblKategorien aufnehmen darf. Sprich: Sie können nun zwar theoretisch Werte in das Kombinationsfeld cboKategorieID eingeben, aber dies löst einen Fehler aus, weil die entsprechende Beziehung nur vorhandene Werte oder Null als Wert des zugrunde liegenden Feldes erlaubt.

Wenn Sie dieses Verhalten reproduzieren möchten, schauen Sie sich das Formular frmKategorieID­ErsteSpalte an (siehe auch Bild 6).

Fehler beim Einfügen eines neuen Wertes in ein Kombinationsfeld, das durch eine Beziehung mit referentieller Integrität nur vorhandene Werte oder Null aufnehmen kann.

Bild 5: Fehler beim Einfügen eines neuen Wertes in ein Kombinationsfeld, das durch eine Beziehung mit referentieller Integrität nur vorhandene Werte oder Null aufnehmen kann.

Listeneinträge und mehr

Es gibt jedoch eine Konstellation, die es erlaubt, Listeneinträge auszuwählen und auch neue Einträge zu einem Feld hinzuzufügen. Dies wäre beispielsweise der Fall, wenn die Kategorie nicht aus einer separaten Tabelle ausgewählt wird, sondern einfach nur in ein Textfeld der Tabelle tblArtikel eingetragen würde.

Diese Variante beleuchtet der Beitrag Kombinationsfeld als Eingabehilfe für Textfelder in Ausgabe 6/2011 von Access [basics].

Neue Kategorie per Kombinationsfeld

Aus den bisherigen Erkenntnissen folgern wir: Eine direkte Eingabe einer neuen Kategorie ist nicht so einfach möglich. Es sind dazu vielmehr die folgenden Schritte zu erledigen, die auch in Bild 7 dargestellt werden:

Ablauf beim Hinzufügen eines Eintrag zu einer Lookup-Tabelle per Kombinationsfeld

Bild 6: Ablauf beim Hinzufügen eines Eintrag zu einer Lookup-Tabelle per Kombinationsfeld

  • (1) Eingeben des neuen Wertes in das Kombinationsfeld
  • (2) Eintragen eines neuen Datensatzes mit der gewünschten Kategorie in die Tabelle tblKategorien
  • (3) Aktualisieren der Datensatzherkunft des Kom­bi­na­tionsfeldes cboKategorieID, damit die neue Ka­te­gorie hier ausgewählt werden kann (4)
  • (5) Automatisches Ermitteln der KategorieID der neuen Kategorie und Eintragen dieses Wertes in das Fremdschlüsselfeld KategorieID der Tabelle tblArtikel

Den ersten Schritt erledigt der Benutzer, die übrigen sollen per Code automatisch durchgeführt werden. Zunächst benötigen Sie dabei eine Möglichkeit, überhaupt zu erkennen, dass der Benutzer einen nicht vorhandenen Eintrag eingegeben hat. Praktischerweise bietet das Kombinationsfeld dazu ein passendes Ereignis, nämlich Bei Nicht in Liste. Wählen Sie für die entsprechende Eigenschaft den Wert [Ereignisprozedur] aus und klicken Sie auf die Schaltfläche mit den drei Punkten (siehe Bild 8).

Dieses Ereignis wird ausgelöst, wenn der Benutzer einen neuen Eintrag in das Kombinationsfeld einträgt.

Bild 7: Dieses Ereignis wird ausgelöst, wenn der Benutzer einen neuen Eintrag in das Kombinationsfeld einträgt.

Die nun im VBA-Editor erscheinende Prozedur ergänzen Sie wie folgt:

Private Sub KategorieID_NotInList(NewData As String, Response As Integer)

     Dim lngKategorieID As Long

     Dim db As DAO.Database

     Set db = CurrentDb

     db.Execute "INSERT INTO tblKategorien (Kategoriename) VALUES('" & NewData & "')", dbFailOnError

     lngKategorieID = db.OpenRecordset ("SELECT @@IDENTITY").Fields(0)

     Me!cboKategorieID.Undo

     Me!cboKategorieID.Requery

     Me!cboKategorieID = lngKategorieID

     Response = acDataErrAdded

     Set db = Nothing

End Sub

Dies sieht auf den ersten Blick möglicherweise kompliziert aus, aber wir schauen uns die einzelnen Anweisungen nun genau an.

Zunächst einmal ist der Prozedurkopf bemerkenswert. Er enthält zwei Parameter. Der erste heißt New­Data und liefert der Prozedur genau den Wert, den der Benutzer neu in das Kombinationsfeld eingetragen hat.

Wenn der Benutzer also wie in der Abbildung den Wert Grillzubehör einträgt, können Sie über die Parametervariable NewData auf diesen Wert zugreifen.

Der zweite Parameter heißt Response und enthält einen Wert, der festlegt, wie Access auf die Eingabe eines neuen Ausdrucks im Kombinationsfeld reagieren soll. Standardmäßig ist diese Parametervariable mit dem Wert 1 gefüllt, was der Konstanten acDataErrDisplay entspricht – auf gut deutsch: Zeige eine Fehlermeldung an (in diesem Fall Der von Ihnen eingegebene Text ist kein Element der Liste.). Wenn Sie den Wert dieses Parameters innerhalb der Ereignisprozedur NotInList nicht ändern, gibt Access die Meldung aus Bild 5 aus.

Diese Fehlermeldung erscheint, wenn die Eigenschaft Nur Listeneinträge den Wert Ja enthält.

Bild 8: Diese Fehlermeldung erscheint, wenn die Eigenschaft Nur Listeneinträge den Wert Ja enthält.

Im Folgenden soll der eingegebene Ausdruck jedoch gespeichert und dem Feld KategorieID zugewiesen werden – die Fehlermeldung würde den Benutzer da nur verwirren. Damit diese nicht erscheint, müssen Sie irgendwo innerhalb der Ereignisprozedur NotInList die Variable Response auf den Wert 2 beziehungsweise acDataErrAdded festlegen. Eigentlich reicht 2, aber mit acDataErrAdded ist der Code besser lesbar. Access verwendet intern eine Liste von Konstanten wie acDataErrDisplay oder acDataErrAdded und weist diese automatisch den entsprechenden Zahlenwerten zu.

Die benötigte Anweisung zum Unterbinden der Fehlermeldung sieht also wie folgt aus:

Response = acDataErrAdded

Die Hauptarbeit ist jedoch das Speichern des neuen Wertes in der Tabelle tblKategorien und das Einstellen des Kombinationsfeldes KategorieID auf den neuen Wert.

Die folgenden drei Zeilen legen den neuen Datensatz in der Tabelle tblKategorien an. Dies wird durch eine Anfügeabfrageabfrage erledigt, die wir bereits in Aktionsabfragen - Teil I: Anfügeabfragen beschrieben haben. Unsere Anfügeabfrage legt einen neuen Datensatz in der Tabelle tblKategorien an, wobei das Feld Kategoriename mit dem Wert gefüllt wird, den der Parameter NewData uns übergibt:

Dim db As DAO.Database

Set db = CurrentDb

db.Execute "INSERT INTO tblKategorien (Kategoriename) VALUES('" & NewData & "')", dbFailOnError

Für den Ausdruck Grillzubehör sieht die Abfrage also beispielsweise so aus:

INSERT INTO tblKategorien(Kategoriename) VALUES('Grillzubehör')

Wie das Ausführen von Aktionsabfragen per VBA genau funktioniert, erfahren Sie übrigens unter Aktionsabfragen per VBA ausführen.

Danach folgt die Aufgabe, den Wert des Feldes KategorieID für den neuen Datensatz zu ermitteln. Diesen benötigen wir ja, um das gleichnamige Fremdschlüsselfeld der Tabelle tblArtikel zu füllen und so dem Artikel gleich die neue Kategorie zuzuweisen.

Das Primärschlüsselfeld KategorieID der Tabelle tblKategorien ist als Autowertfeld ausgelegt. Für diese gibt es eine spezielle Abfrage, die den zuletzt hinzugefügten Wert innerhalb der aktuellen Sitzung mit der Anwendung ermittelt. Den Wert speichern wir in der Variablen lngKategorieID, die so deklariert wird:

Dim lngKategorieID As Long

Die Abfrage zum Ermitteln dieses Wertes rufen Sie wie folgt auf, wobei der einzige zurückgelieferte Wert des Ergebnisses gleich in der Variablen lngKategorieID gespeichert wird:

lngKategorieID = db.OpenRecordset ("SELECT @@IDENTITY").Fields(0)

Die OpenRecordset-Anweisung wurde in Access [basics] bislang noch nicht erläutert. Nehmen Sie diese eine Zeile als gegeben hin, wir kommen in einer späteren Ausgabe darauf zurück.

Danach folgen noch einige Schritte, die das Ergebnis abrunden. Wir haben nun die Tabelle tblKategorien um den neuen Datensatz erweitert.

Das Kombinationsfeld cboKategorieID zeigt den neuen Eintrag aber noch nicht an, sondern nur die bisher schon vorhandenen Einträge. Also wenden Sie die Requery-Methode auf dieses Steuerelement an, damit seine Datensatzherkunft aktualisiert wird.

Dies würde allerdings einen Fehler auslösen, weil Access ein Kombinationsfeld nur aktualisieren kann, wenn eine bereits vorgenommene Änderung am Inhalt gespeichert wurde. Dies können wir nicht tun, also verwerfen wir die Änderung – das Eintragen der neuen Kategorie – einfach mit der Undo-Methode des Kombinationsfeldes:

Me!cboKategorieID.Undo

Dieses Zurücksetzen können wir deshalb so sorglos ausführen, weil wir den neu eingegebenen Wert ja bereits in der Tabelle tblKategorien gespeichert haben und den Wert des Feldes KategorieID für den entsprechenden Datensatz kennen. Danach aktualisieren wir schließlich die Datensatzherkunft des Kombinationsfeldes:

Me!cboKategorieID.Requery

Nun fehlt nur noch ein letzter Schritt – das Zuweisen des neuen Eintrags der Tabelle tblKategorien zum Kombinationsfeld cboKategorieID. Dies erledigt die folgende Anweisung:

Me!cboKategorieID = lngKategorieID

Fertig – die neue Kategorie wird kommentarlos in die Tabelle tblKategorien eingetragen und als Wert des Kombinationsfeldes ausgewählt.

Rückfrage vor dem Anlegen

Gelegentlich kann es hilfreich sein, nicht alle Aktionen des Benutzers kommentarlos hinzunehmen. Im Beispiel dieses Artikels kommen so etwa schnell einige neue Einträge in der Tabelle tblKategorien zusammen. Der Haken dabei ist: Wenn ein Benutzer das Kombinationsfeld nicht aufklappt und den gewünschten Eintrag auswählt, sondern schnell den gewünschten Eintrag eintippt, kann es vorkommen, dass dieser sich vertippt.

Das Resultat ist, dass die Tabelle tblKategorien dann nicht nur einen Eintrag wie Getränke, sondern auch noch Getraenke enthält. Und wenn der Benutzer dann später weitere Artikel beiden Kategorien zuweist, kann keine vernünftige Auswertung mehr nach den Kategorien mehr erfolgen.

Deshalb erweitern wir die Lösung noch um eine Rückfrage vor dem Speichern der neuen Kategorie, die wie in Bild 9 aussehen soll.

Rückfrage vor dem Anlegen eines neuen Datensatzes

Bild 9: Rückfrage vor dem Anlegen eines neuen Datensatzes

Dies erledigen Sie durch eine MsgBox-Funktion. Die MsgBox-Funktion erwartet Parameter etwa für den anzuzeigenden Text, die Titelleiste und die benötigten Schaltflächen. In unserem Fall soll die Titelleiste den Text Neue Kategorie anzeigen. Die Meldung selbst soll '' als neue Kategorie anlegen? lauten.

Dazu erweitern wir den Code um einige Zeilen. Die erte stellt die Meldung zusammen und speichert sie in einer Variablen, die zuvor deklariert wird:

Dim strMeldung As String

Für die Meldung verketten wir ein Hochkomma, den Inhalt von NewData und den Rest des Ausdrucks mit dem &-Operator:

strMeldung = "'" & NewData & "' als neue µ

Kategorie anlegen?"

Die MsgBox-Funktion liefert je nach gedrückter Schaltfläche verschiedene Ergebnisse zurück (siehe auch VBA-Befehle: MsgBox). Wenn dieses Ergebnis der Ja-Schaltfläche entspricht (vbYes), erhält die Variable bolAnlegen den Wert True:

Dim bolAnlegen As Boolean

bolAnlegen = MsgBox(strMeldung, vbYesNo, µ "Neue Kategorie") = vbYes

Die folgende If...Then-Bedingung (siehe VBA-Grundlagen – Teil III: If...Then- und Select Case-Bedingungen) prüft, ob bolAnlegen den Wert True enthält. Falls ja, wird die Kategorie wie oben beschrieben angelegt.

Falls nicht, geschieht nichts – außer, dass Sie dem Parameter Response die Konstante acDataErrContinue zuweisen. Dies bedeutet, dass weder ein Fehler ausgelöst noch ein neuer Wert zum Kombinationsfeld hinzugefügt wurde – das Kombinationsfeld wird dadurch schlicht aufgeklappt angezeigt:

If bolAnlegen = True Then

     ... Kategorie anlegen

     Response = acDataErrAdded

Else

     Response = acDataErrContinue

End If

Einsatz in eigenen Anwendungen

Wenn Sie die hier vorgestellte Technik in eigenen Formularen einsetzen möchten, brauchen Sie nur einige Änderungen an der vorgestellten Prozedur vorzunehmen. Im Modul mdlKombinationsfeldErweitern haben wir für Sie eine Rohfassung der notwendigen Prozedur gespeichert. Die zu ersetzenden Elemente sind jeweils in spitze Klammern eingefasst, zum Beispiel so:

Private Sub _NotInList µ

(NewData As String, Response As Integer)

Legen Sie also einfach die Prozedur für das Ereignis Bei Nicht in Liste an, ersetzen Sie diese durch die aus dem angegebenen Modul und füllen Sie die Platzhalter – fertig!