Bild 1: Anpassen der Beschriftung eines Feldes
Wozu sollte man Tabellenfeldbeschriftungen im Griff haben? Und was ist das überhaupt? Die Eigenschaft “Beschriftung” von Tabellenfeldern kennen viele Entwickler gar nicht. Dabei könnten sie sich eine Menge Zeit sparen, wenn sie diese Eigenschaft nutzen würden. Trägt man nämlich beispielsweise für ein Feld wie “EMail” eine Beschriftung wie “E-Mail” ein, dann wird diese statt des Feldnamens als Spaltenüberschrift der Tabelle in der Datenblattansicht angezeigt. Und es geht noch weiter: Wenn man in Formularen und Berichten Steuerelemente auf Basis dieser Felder anlegt, übernimmt Access auch dort den Wert der Eigenschaft Beschriftung statt des Feldnamens für die Bezeichnungsfelder Steuerelemente. Man braucht als nur an einer Stelle eine Änderung vorzunehmen und profitiert an vielen anderen Stellen davon. Und wenn man nun nicht in jeder neuen Datenbank die Beschriftungen erneut anlegen müsste … doch auch dafür haben wir in diesem Beitrag eine Lösung.
Eigentlich ist es gar nicht viel Arbeit: Wir erstellen eine neue Tabelle, und für Felder wie KundeID, AnredeID, Strasse, EMail et cetera hinterlegen wir für die Eigenschaft Beschriftung jeweils den anzuzeigende Text wie ID/Kunde-ID, Anrede, Straße oder E-Mail. In Bild 1 sehen Sie, wie wir das Feld Strasse entsprechend angepasst haben.
Bild 1: Anpassen der Beschriftung eines Feldes
Wechseln wir in die Datenblattansicht, sehen wir direkt die Werte der Eigenschaft Beschriftung der jeweiligen Felder statt der Feldnamen (siehe Bild 2).
Bild 2: Die Beschriftung wird beispielsweise in der Datenblattansicht angewendet.
Automatisieren der Beschriftungen
Allerdings ist das eine von vielen Aktionen, die man beim Anlegen neuer Tabellen berücksichtigen muss, und die dafür aufzuwendende Zeit summiert sich. Vor allem wenn man bedenkt, dass man diese Aufgabe im Laufe eines Entwicklerlebens sehr oft durchführt. Also sieht es so aus, als ob wir uns über die Automatisierung dieser Aufgabe Gedanken machen sollten!
Automatisieren der Festlegung von Feldbeschriftungen
Aber lässt sich das überhaupt automatisieren? Ist das nicht ein individueller Vorgang, der mit jeder Datenbank, jeder Tabelle und jedem Feld einzeln durchgeführt und vom Programmierer intensiv überdacht werden muss? Nein, das ist nicht der Fall. Außer, man möchte sich mit ein wenig Routinearbeit entspannen.
Wie also können wir diesen Vorgang automatisieren? Fest steht: Für die meisten Feldnamen gibt es genau eine passende Beschriftung, zumindest wenn man sich einmal auf einen Standard festgelegt hat. Ob zum Beispiel die Inhalte von Primärschlüssel-/Autowertfeldern angezeigt werden müssen, sei dahingestellt, aber in vielen Fällen geschieht das. Dann sollte der Benutzer statt eines Feldnamens wie KundeID oder BestellungID eine passendere Beschriftung wie Kunde-ID/Bestellung-ID, Kundennummer/Bestellnummer oder schlicht ID angezeigt bekommen.
Da einige Entwickler, wie auch ich, für Primärschlüsselfelder und Fremdschlüsselfelder die gleichen Bezeichnungen wählen – also zum Beispiel KundeID als Primärschlüsselfeld in der Tabelle tblKunden und KundeID als Fremdschlüsselfeld in der Tabelle tblBestellungen -, wollen wir hier unterscheiden. Während wir für das Fremdschlüsselfeld KundeID vermutlich die Beschriftung Kunde wählen werden, erhält das Primärschlüsselfeld KundeID eher eine Beschriftung wie Kunde-ID oder auch nur ID.
Automatisierung per VBA
Unsere Idee ist, dass wir die üblichen Zuordnungen von Feldnamen und Beschriftungen in einer Tabelle speichern, die wir beispielsweise tblCaptions nennen. Diese soll vier Felder enthalten – CaptionID, Fieldname, Caption und IsPrimaryKey.
Für die Kombination aus den Feldern Fieldname und IsPrimaryKey definieren wir einen eindeutigen Index, damit zu jedem Feldnamen für einfache Felder und für Primärschlüsselfelder nur jeweils eine Beschriftung hinterlegt werden kann (siehe Bild 3).
Bild 3: Tabelle zum Speichern der Zuordnung von Feldern und Beschriftungen
Zusätzlich wollen wir eine Prozedur programmieren, mit der wir die Felder einer Tabelle, die mit Beschriftungen ausgestattet werden soll, durchlaufen und prüfen, ob wir für die Feldnamen bereits entsprechende Beschriftungen in unserer Tabelle tblCaptions hinterlegt haben. Falls ja, soll die Beschriftung einfach für die Eigenschaft Beschriftung hinterlegt werden.
Für eine Tabelle wie tblKunden sieht der Inhalt der Tabelle tblCaption also wie in Bild 4 aus.
Bild 4: Die Tabelle tblCaptions mit einigen Beispieldaten
Beschriftungen per VBA zuweisen
Nun fehlt noch die passende Automatisierung. Diese erledigen wir mit einer VBA-Prozedur ApplyCaptions, die wir in Listing 1 abgebildet haben. Die Prozedur erwartet den Namen der Tabelle, deren Felder mit Beschriftungen ausgestattet werden sollen. Wir deklarieren vorsorglich schon einmal zwei Variablen für Database-Objekte: eines für die aktuelle Datenbank und eines für eine Add-In-Datenbank, denn wir werden die hier produzierten Elemente für die Automatisierung vermutlich nicht zu jeder neuen Datenbank hinzufügen, sondern diese über ein Add-In aufrufen wollen. Und bei Verwendung eines Add-Ins muss man unterscheiden, ob man auf die Daten der Tabellen der Add-In-Datenbank oder der aufrufenden Datenbank zugreifen möchte. Deshalb füllt die Prozedur auch die Variable db über die Funktion CurrentDb mit einem Verweis auf die Host-Datenbank und die Variable dbc über die Funktion CodeDb mit einem Verweis auf die Add-In-Datenbank. Und auch wenn wir aktuell noch direkt in der Host-Datenbank programmieren, ist das kein Problem, denn in diesem Fall greifen CurrentDb und CodeDb einfach beide auf die aktuelle Datenbank zu.
Public Sub ApplyCaptions(strTable As String) Dim db As DAO.Database Dim dbc As DAO.Database Dim rstCaptions As DAO.Recordset Dim tdf As DAO.TableDef Dim fld As DAO.Field Dim prp As DAO.Property Dim strCaption As String Dim bolPrimaryKey As Boolean Set db = CurrentDb Set dbc = CodeDb Set tdf = db.TableDefs(strTable) Set rstCaptions = dbc.OpenRecordset("SELECT * FROM tblCaptions", dbOpenDynaset) For Each fld In tdf.Fields bolPrimaryKey = IsPrimaryKey(tdf, fld) rstCaptions.FindFirst "Fieldname = ''" & fld.Name & "'' AND IsPrimaryKey = " & CInt(bolPrimaryKey) If Not rstCaptions.NoMatch Then strCaption = rstCaptions!Caption On Error Resume Next Set prp = fld.CreateProperty("Caption", dbText, strCaption) fld.Properties.Append prp If Not Err.Number = 0 Then fld.Properties("Caption") = strCaption End If On Error GoTo 0 End If Next fld End Sub
Listing 1: Anwenden von Beschriftungen auf die Felder der per Parameter übergebenen Tabelle
Weiter im Code: Dort referenzieren wir mit der TableDef-Variable tdf das TableDef-Objekt für die mit dem Parameter strTable übergebene Tabelle. Dann öffnen wir ein Recordset für den Zugriff auf die Daten der Tabelle tblCaptions und speichern den Verweis in rstCaptions.
Anschließend durchläuft die Prozedur alle Felder der Tabelle, deren Felder wir mit Beschriftungen ausstatten wollen, in einer For Each-Schleife. Innerhalb der Schleife prüft die Prozedur zunächst, ob es sich bei dem aktuell untersuchten Feld um ein Primärschlüsselfeld handelt. Dies geschieht mit der Funktion IsPrimaryKey, die wir weiter unten beschreiben.
Ob es sich um ein Primärschlüsselfeld handelt oder nicht, ist wie oben bereits erwähnt wichtig, da wir bei gleicher Benennung von Primärschlüsselfeldern und Fremdschlüsselfeldern gegebenenfalls unterschiedliche Beschriftungen verwenden wollen. Danach versucht die Prozedur, im Recordset mit den Beschriftungen einen Eintrag zu finden, dessen Feld Fieldname dem Namen des aktuell durchlaufenen Feldes entspricht und das, je nachdem, welches Ergebnis die Funktion IsPrimaryKey geliefert hat, auch dieses Feld in die Bedingung einbezieht.
Wurde ein Eintrag gefunden, liefert die in der kommenden If…Then-Bedingung überprüfte Eigenschaft NoMatch den Wert False. In diesem Fall werden die Anweisungen innerhalb der If…Then-Bedingung durchgeführt. Wir schreiben dort die zu dem Feld passende Beschriftung in die Variable strCaption und weisen diese dann der Eigenschaft Beschriftung zu.
Das ist nicht ganz trivial, denn im Gegensatz zum Tabellenentwurf in der Benutzeroberfläche, wo die Eigenschaft jederzeit zur Verfügung steht, ist die dahinter stehende Eigenschaft Caption standardmäßig gar nicht in der Auflistung der Properties eines Field-Objekts vorhanden. Das entsprechende Property-Objekt wird erst angelegt, wenn der Benutzer eine Beschriftung einträgt.
Um nicht prüfen zu müssen, ob die Property bereits vorhanden ist, versuchen wir einfach, diese bei deaktivierter Fehlerbehandlung anzulegen. Dazu nutzen wir zwei Anweisungen.
Die erste ruft die CreateProperty-Methode für das Feld auf und legt für die Eigenschaft Caption mit dem Datentyp dbText den Wert aus strCaption fest. Danach hängt sie das neu erstellte Property-Objekt an die Properties-Auflistung des Field-Objekts für das Feld an.
Ist die Property bereits vorhanden, löst dieser Vorgang einen Fehler aus. Das prüfen wir in der folgenden If…Then-Bedingung und legen in diesem Fall einfach den Wert der Eigenschaft Caption auf den Wert aus strCaption fest.
Dies wiederholen wir für alle Felder der angegebenen Tabelle und tragen so für alle Felder, für die wir in der Tabelle tblCaptions eine alternative Beschriftung angegeben haben, die jeweilige Beschriftung ein.
Herausfinden, ob ein Feld ein Primärschlüsselfeld ist
Die Funktion IsPrimaryKey erwartet das TableDef-Objekt und das Field-Objekt des zu untersuchenden Feldes als Parameter (siehe Listing 2). Sie durchläuft alle Index-Elemente des übergebenen TableDef-Objekts in einer For Each-Schleife. Wenn der Index ein Primärschlüssel ist, durchläuft sie außerdem alle Felder, die zu diesem Primärschlüsselindex gehören (ein Primärschlüssel kann ja auch aus mehreren Feldern bestehen). Ist das zu untersuchende Feld hier enthalten, handelt es sich bei diesem um ein Primärschlüsselfeld und wir können die Funktion wieder verlassen.
Public Function IsPrimaryKey(tdf As DAO.TableDef, fld As DAO.Field) As Boolean Dim idx As DAO.Index Dim fldPK As DAO.Field For Each idx In tdf.Indexes If idx.Primary Then For Each fldPK In idx.Fields If fld.Name = fldPK.Name Then IsPrimaryKey = True Exit Function End If Next fldPK End If Next idx End Function
Listing 2: Funktion zum Prüfen, ob ein Feld ein Primärschlüsselfeld ist
Noch mehr Automation: Beschriftungen einlesen
Da wir uns nun schon ein wenig in das Thema eingearbeitet haben, können wir auch noch weiter automatisieren. Wieso sollten wir eigentlich die Felder und die dafür vorgesehenen Beschriftungen von Hand in die Tabelle tblCaptions eintragen?
Nicht, dass das nicht auch eine entspannende Beschäftigung wäre … Aber noch praktischer wäre es doch, wenn wir die Beschriftung für alle vorhandenen Felder einer Tabelle auch per VBA einlesen könnten. Und genau dazu können wir die Prozedur SaveCaptions aus Listing 3 nutzen.
Public Sub SaveCaptions(strTable As String) Dim db As DAO.Database Dim dbc As DAO.Database Dim tdf As DAO.TableDef Dim fld As DAO.Field Dim prp As DAO.Property Dim rstCaptions As DAO.Recordset Dim strCaption As String Dim strCaptionOld As String Dim bolPrimaryKey As Boolean Set db = CurrentDb Set dbc = CodeDb Set tdf = db.TableDefs(strTable) Set rstCaptions = dbc.OpenRecordset("SELECT * FROM tblCaptions", dbOpenDynaset) For Each fld In tdf.Fields bolPrimaryKey = IsPrimaryKey(tdf, fld) strCaption = "" On Error Resume Next strCaption = fld.Properties("Caption") On Error GoTo 0 If Not Len(strCaption) = 0 Then On Error Resume Next dbc.Execute "INSERT INTO tblCaptions(Fieldname, Caption, IsPrimaryKey) VALUES(''" & fld.Name & "'', ''" _ & strCaption & "'', " & CInt(bolPrimaryKey) & ")", dbFailOnError Select Case Err.Number Case 3022 rstCaptions.FindFirst ("Fieldname = ''" & fld.Name & "'' AND IsPrimaryKey = " & CInt(bolPrimaryKey)) strCaptionOld = rstCaptions!Caption If Not strCaption = strCaptionOld Then If MsgBox("Für das Feld ''" & fld.Name & "'' wurde bereits die Beschriftung ''" & strCaptionOld _ & "'' hinterlegt. Soll die in der Tabelle ''" & strTable & "'' gefundene Beschriftung ''" _ & strCaption & "'' dafür gespeichert werden?", vbYesNo + vbExclamation, _ "Beschriftung bereits vorhanden.") = vbYes Then dbc.Execute "UPDATE tblCaptions SET Caption = ''" & strCaption & "'', IsPrimaryKey = " _ & CInt(bolPrimaryKey) & " WHERE Fieldname = ''" & fld.Name & "''", dbFailOnError End If End If Case 0 Case Else MsgBox "Fehler: " & Err.Number & ", " & Err.Description End Select End If Next fld End Sub