Lektionen/Formulare & Berichte

Kombifelder & Nicht-in-Liste

Fortgeschritten11 Min. Lesezeit

Kombinationsfelder (Combo-Boxen) sind die Arbeitspferde jeder Access-Oberfläche: Sie zeigen eine Liste, geben aber im Hintergrund oft einen Schlüsselwert weiter. Mit VBA machst du sie flexibel — dynamische Listen, neue Einträge on-the-fly und Auswahl, die von einer anderen Auswahl abhängt.

RowSource dynamisch setzen

Die angezeigte Liste steuert die Eigenschaft RowSource. Je nach RowSourceType ist das eine Tabelle/Abfrage („Table/Query") oder eine feste Werteliste („Value List"). Beides kannst du zur Laufzeit setzen:

' Als SQL-Abfrage
Me!cboKunde.RowSource = _
    "SELECT KundenID, Firma FROM tblKunde " & _
    "WHERE Aktiv = True ORDER BY Firma"

' Als feste Werteliste
Me!cboStatus.RowSourceType = "Value List"
Me!cboStatus.RowSource = "offen;in Arbeit;erledigt"

Nach dem Setzen aktualisiert Access die Liste automatisch. Baust du die Datengrundlage anderweitig um, hilft ein Me!cboKunde.Requery.

Gebundene Spalte und Spaltenbreiten

Ein Kombifeld kann mehrere Spalten anzeigen, gibt als Value aber nur eine zurück — die gebundene Spalte (BoundColumn). Typisch: Spalte 1 ist die ID (gebunden, aber ausgeblendet), Spalte 2 der lesbare Name.

EigenschaftBedeutung
ColumnCountAnzahl der Spalten in der Liste
BoundColumnwelche Spalte den Value liefert (1-basiert)
ColumnWidthsBreiten je Spalte (0cm blendet aus)
With Me!cboKunde
    .RowSource = "SELECT KundenID, Firma FROM tblKunde ORDER BY Firma"
    .ColumnCount = 2
    .BoundColumn = 1          ' Value = KundenID
    .ColumnWidths = "0cm;5cm" ' ID unsichtbar, Firma sichtbar
End With

Me!cboKunde liefert jetzt die KundenID, obwohl der Anwender die Firma liest.

Auf einzelne Spalten zugreifen mit Column()

Manchmal brauchst du eine andere als die gebundene Spalte — etwa den angezeigten Namen, um ihn in ein Textfeld zu schreiben. Dafür gibt es Column(Index), 0-basiert:

Private Sub cboKunde_AfterUpdate()
    ' Spalte 0 = KundenID, Spalte 1 = Firma
    Me!txtFirma = Me!cboKunde.Column(1)
End Sub

Achtung, zwei Zählweisen: BoundColumn und ColumnWidths zählen ab 1, Column() zählt ab 0. Die gebundene Spalte 1 ist also Column(0). Das ist die häufigste Fehlerquelle bei Kombifeldern.

Optional nimmt Column(Spalte, Zeile) auch eine Zeilennummer — nützlich, um die ganze Liste durchzugehen, ohne ein Recordset zu öffnen.

Neue Einträge zulassen: das NotInList-Ereignis

Tippt der Anwender in ein gebundenes Kombifeld einen Wert, der nicht in der Liste steht, meckert Access. Damit das nicht als Fehler endet, brauchst du zweierlei: Die Eigenschaft Nur Listeneinträge (LimitToList) muss True sein, und du behandelst das Ereignis NotInList. Dort legst du den neuen Eintrag an und meldest Access über Response zurück, wie es weitergehen soll.

Private Sub cboKategorie_NotInList(NewData As String, Response As Integer)
    Dim antwort As VbMsgBoxResult

    antwort = MsgBox("'" & NewData & "' ist neu. Anlegen?", _
                     vbYesNo + vbQuestion, "Neue Kategorie")

    If antwort = vbYes Then
        ' Neuen Datensatz per DAO in die Quelltabelle schreiben
        CurrentDb.Execute _
            "INSERT INTO tblKategorie (Bezeichnung) VALUES ('" & _
            Replace(NewData, "'", "''") & "')", dbFailOnError

        ' Access anweisen: Eintrag wurde hinzugefügt -> Liste neu laden
        Response = acDataErrAdded
    Else
        ' Abbrechen ohne Meldung, Eingabe zurücknehmen
        Response = acDataErrContinue
        Me!cboKategorie.Undo
    End If
End Sub

Die drei möglichen Rückgabewerte für Response:

KonstanteWirkung
acDataErrAddedEintrag wurde angelegt, Access requert die Liste selbst
acDataErrContinuekeine Fehlermeldung, Wert wird verworfen
acDataErrDisplayAccess zeigt seine Standard-Fehlermeldung (Vorgabe)

SQL-Injection & Hochkommas: Übernimm Benutzereingaben nie ungeprüft in SQL. Das Replace(NewData, "'", "''") verdoppelt einfache Anführungszeichen, damit Namen wie O'Brien nicht die Anweisung zerlegen. Bei mehr als Kleinkram nutze eine parametrisierte QueryDef statt Stringverkettung.

Reaktion auf die Auswahl: AfterUpdate

AfterUpdate läuft, nachdem der Anwender einen Wert ausgewählt (oder eingegeben) hat. Das ist der Ort für Folgeaktionen — Felder füllen, filtern, ein zweites Kombifeld nachladen:

Private Sub cboKunde_AfterUpdate()
    Me!txtFirma = Me!cboKunde.Column(1)
    Me!ctlUnterBestellungen.Form.Requery
End Sub

Kaskadierende Kombifelder

Ein Klassiker: Das zweite Kombifeld zeigt nur, was zum ersten passt — Land → Stadt, Kunde → Projekt. Der Trick besteht aus zwei Teilen. Erstens filtert die RowSource des zweiten Felds über das erste, zweitens requerst du das zweite Feld im AfterUpdate des ersten.

Die RowSource von cboProjekt verweist im Entwurf auf das erste Feld:

SELECT ProjektID, Titel FROM tblProjekt
WHERE KundenID = Forms!frmAuftrag!cboKunde
ORDER BY Titel

Im Code sorgst du dafür, dass die Liste bei jeder neuen Kundenauswahl neu ausgewertet wird:

Private Sub cboKunde_AfterUpdate()
    ' Zweite Liste an die neue Kundenauswahl anpassen
    Me!cboProjekt.Requery

    ' Alte Auswahl verwerfen, falls sie nicht mehr passt
    Me!cboProjekt = Null
End Sub

Ohne das Requery würde cboProjekt die alte Projektliste behalten. Das = Null verhindert, dass ein Projekt des vorigen Kunden stehen bleibt, das nun gar nicht mehr in der Liste ist.

Tipp: Setze cboProjekt erst auf Null und dann Requery, wenn dein Feld an eine Spalte gebunden ist — sonst kann ein ungültiger Restwert kurz Probleme machen. In der Praxis funktioniert beide Reihenfolgen meist; teste mit echten Daten.

Zusammengefasst

  • RowSource setzt die Liste zur Laufzeit — als SQL (RowSourceType „Table/Query") oder als Werteliste; ein Requery frischt sie auf.
  • BoundColumn bestimmt, welche Spalte der Value ist; mit ColumnWidths (0cm) blendest du z. B. die ID aus.
  • Column(Index) liest einzelne Spalten — 0-basiert, während BoundColumn 1-basiert zählt (häufige Fehlerquelle).
  • Im NotInList-Ereignis legst du neue Einträge an und meldest mit Response = acDataErrAdded zurück, dass die Liste neu geladen werden soll.
  • Kaskadierende Kombifelder verknüpfst du über die gefilterte RowSource plus Requery (und = Null) im AfterUpdate des übergeordneten Felds.
Nächste Lektion
Endlosformulare & bedingte Formatierung