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
[
Listing 3: Auslesen von Beschriftungen aus der angegebenen Tabelle
Diese Prozedur erwartet den Namen der Tabelle, die ausgelesen werden soll, als Parameter. Auch hier arbeiten wir wieder mit zwei Database-Objekten, um die Funktionalität gegebenenfalls schnell in ein Add-In umwandeln zu können. Wir referenzieren die zu untersuchende Tabelle als TableDef-Objekt (tdf) und die Tabelle mit den Beschriftungen als Recordset (rstCaptions).
Dann durchlaufen wir alle Felder der Tabelle über die Fields-Auflistung. Dabei prüft die Prozedur zuerst wieder, ob es sich bei dem aktuell untersuchten Feld um ein Primärschlüsselfeld handelt. Falls ja, soll dies später im Feld IsPrimaryKey der Tabelle tblCaptions vermerkt werden.
Wir wissen auch bereits, dass die Caption-Property nur für Felder existiert, für die bereits eine Beschriftung hinterlegt wurde. Also können wir auch hier nicht einfach auf diese Eigenschaft zugreifen, sondern müssen damit rechnen, dass beim Zugriff auf die nicht vorhandene Eigenschaft ein Fehler ausgelöst wird.
Also umgehen wir dies, indem wir die Variable strCaption zum Speichern der Beschriftung erst auf eine leere Zeichenkette setzen und dann bei deaktivierter eingebauter Fehlerbehandlung versuchen, auf fld.Properties(„Caption“) zuzugreifen. Ist strCaption danach nicht leer, haben wir ein Feld mit einer Beschriftung gefunden und wollen den Feldnamen und die Beschriftung in die Tabelle tblCaptions eintragen. Das erledigen wir wieder bei deaktivierter Fehlerbehandlung mit einer INSERT INTO-Anweisung. Das hat den Hintergrund, dass wir für die Kombination der Felder Fieldname und IsPrimaryKey einen eindeutigen Index definiert haben, und wenn es in der Tabelle bereits einen Datensatz für die Kombination dieser Felder gibt, löst der Versuch, diesen erneut anzulegen, einen Fehler aus.
Diesen behandeln wir diesmal, indem wir auf die Fehlernummer 3022 prüfen. Haben wir einen Treffer, lesen wir die für diesen Feldnamen gespeicherte Beschriftung in die Variable strCaptionOld ein. Unterscheidet sich dieser von der Beschriftung, die wir gerade anlegen wollten, zeigt die Prozedur eine MsgBox an, die den Benutzer fragt, ob die bisher gespeicherte Beschriftung für Felder dieses Namens überschrieben werden soll. Beantwortet der Benutzer diese Frage mit Ja, führt die Prozedur eine UPDATE-Abfrage durch, welche die Beschriftung im vorhandenen Datensatz für diesen Feldnamen aktualisiert. Auf diese Weise durchlaufen wir alle Felder der angegebenen Tabelle und tragen für die Felder, die eine Beschriftung enthalten, einen Datensatz in die Tabelle tblCaptions ein.
Beschriftungen aller Tabellen der aktuellen Datenbank auslesen
Bevor wir uns darum kümmern, wie wir diese Funktionen in Form eine Access-Add-Ins abbilden können, wollen wir noch erläutern, wie wir die Beschriftungen aller Tabellen einer Datenbank in einem Rutsch auslesen können – und wie wir die Felder aller Tabellen einer neuen Datenbank auf einmal mit diesen Beschriftungen versehen.
Dazu nutzen wir die Prozedur SaveAllCaptions aus Listing 4. Diese Prozedur definiert ein Recordset auf Basis der Systemtabelle MSysObjects. Das Recordset soll die Namen aller Elemente mit dem Typ 1 (entspricht lokalen Tabellen) enthalten, die nicht mit ~, USys, MSys oder f_ beginnen – dies sind alles temporäre Tabellen oder Systemtabellen. Was ist mit verknüpften Access-Tabellen? Diese müssen wir direkt in der jeweiligen Backenddatenbank bearbeiten. Was ist mit SQL Server- und anderen per ODBC verknüpften Tabellen? Diese stellen die gewünschte Eigenschaft ohnehin nicht zur Verfügung. Hier könnte man Abfragen erstellen, die auf diesen Tabellen basieren, und für die Felder der Abfrage entsprechende Beschriftungen hinterlegen.
Public Sub SaveAllCaptions() Dim db As DAO.Database Dim rst As DAO.Recordset Set db = CurrentDb Set rst = db.OpenRecordset("SELECT Name FROM MSysObjects WHERE Type = 1 AND NOT Name LIKE ''~*'' AND " _ & "NOT Name LIKE ''USys*'' AND NOT Name LIKE ''MSys*'' AND NOT Name LIKE ''f_*''", dbOpenDynaset) Do While Not rst.EOF SaveCaptions rst!Name rst.MoveNext Loop End Sub
Listing 4: Auslesen der Beschriftungen der Felder aller Tabellen
Die Prozedur durchläuft alle Tabellen des Recordsets und ruft für jede dieser Tabellen die Prozedur SaveCaptions auf, um die Beschriftungen der jeweils enthaltenen Felder in der Tabelle tblCaptions zu speichern.
Beschriftungen auf Felder aller Tabellen anwenden
Die nächste Prozedur funktioniert genauso – mit dem Unterschied, dass sie die Beschriftungen auf alle Tabellen der aktuellen Datenbank anwendet. Hier die gekürzte Form:
Public Sub ApplyAllCaptions() Dim db As DAO.Database Dim rst As DAO.Recordset Set db = CurrentDb Set rst = db.OpenRecordset("SELECT Name FROM " _ & "MSysObjects WHERE Type = 1 ...", dbOpenDynaset) Do While Not rst.EOF ApplyCaptions rst!Name rst.MoveNext Loop End Sub
Beschriftungen entfernen
Es kann passieren, dass Sie eine Datenbank von einem anderen Entwickler erhalten, der ausgiebig mit Beschriftungen für Tabellenfelder gearbeitet hat, aber Sie kommen aufgrund der eigenwilligen Benennungskonvention absolut nicht mit der Zuordnung von Beschriftungen und tatsächlichen Feldnamen klar. Nun wollen Sie auch nicht immer erst in den Entwurf einer Tabelle wechseln, um den tatsächlichen Namen eines Feldes auszulesen.
Die Option wäre, alle Beschriftungen zu diesem Zweck aus den Tabellen zu entfernen – zumindest temporär. Man könnte die Beschriftungen ja in einer Tabelle im Add-In zwischenspeichern und wiederherstellen (oder in einer anderen Eigenschaft wie beispielsweise der Tag-Eigenschaft).
Sobald die Arbeiten an der Datenbank abgeschlossen sind, können Sie die Bezeichnungen dann wieder in die Beschriftung-Eigenschaft zurückschreiben. Wir schauen uns auch diese Vorgehensweise noch an.
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
den kompletten Artikel im PDF-Format mit Beispieldatenbank
diesen und alle anderen Artikel mit dem Jahresabo