In der Datenblattansicht lassen sich ja bereits eine Menge Dinge erledigen – Daten einfügen, löschen, bearbeiten, kopieren, ausschneiden … Sie können sogar komplette Bereiche kopieren und in andere Bereiche einfügen, sofern diese Bereiche zueinander kompatibel sind, und Access ist hier recht tolerant. Was aber fehlt, ist die Markierung eines Zielbereichs, dessen Felder dann alle mit dem gleichen Wert gefüllt werden. Wenn Sie also etwa für alle Datensätze ein Ja/Nein-Feld anhaken möchten, müssen Sie dies immer noch manuell erledigen. Dieser Beitrag zeigt eine passende Lösung für Datenblätter in Formularen und Unterformularen.
Wenn Sie einen Bereich eines Formulars oder Unterformulars in der Datenblattansicht mit den gleichen Werten füllen wollten, waren Sie bislang auf zwei Möglichkeiten angewiesen:
- das Absetzen einer entsprechenden UPDATE-SQL-Aktionsabfrage, was aber den Nachteil hat, dass dies selbst die Fähigkeiten von Powerusern mitunter überschreitet, oder
- das manuelle Füllen der Felder mit dem gewünschten Wert.
Letzteres geschieht für eine bessere Effizienz am besten so, dass man eine gewisse Menge Felder mit dem Wert füllt (sagen wir zehn untereinander liegende Felder), dann diesen Bereich kopiert und in eine Markierung unter den gefüllten Feldern mit einer Spalte Breite und zehn Zeilen Höhe wieder einfügt. Wenn man viele Felder füllen muss, kann man nochmal größere Bereiche markieren und kopieren und dann in entsprechend größere Bereiche einfügen.
Der Nachteil: Man müsste immer ungefähr die gewünschte Menge Felder ermitteln, damit alle kopierten Werte dort hineinpassen. Der Vorteil: Access ist beim Einfügen nicht pingelig, die Größe von Quell- und Zielfeldern muss nicht unbedingt übereinstimmen. Das heißt, wenn Sie beispielsweise zehn Felder in einen Bereich von weniger als zehn Feldern einfügen, dann werden nur die markierten Felder gefüllt.
Wenn der Zielbereich größer ist als der Quellbereich, dann werden ebenfalls nur die entsprechend dem kopierten Bereich gefüllt.
Befriedigend ist das alles nicht: Fehler können so leicht auftreten, sei es durch das Auslassen eines zu füllenden Feldes oder durch überschreiben von Bereichen, die gar nicht geändert werden sollen. Es wäre also am einfach-sten, wenn man etwa die in Bild 1 markierten Felder mit einem Klick etwa aktivieren oder deaktivieren oder mit einem Wert füllen könnte.
Bild 1: Das wäre schön: Die markierten Felder alle mit einem Schlag beispielsweise aus- oder abwählen
Die Lösung
Ein viel besserer Ansatz wäre etwa ein Kontextmenü-Eintrag, den man für eine Markierung in der Datenblattansicht auswählen kann und der dann eine InputBox öffnet, der man den einzufügenden Wert übergibt. Nach Betätigen der OK-Schaltfläche soll dieser Wert in alle markierten Zellen eingefügt werden. Dabei soll es keine Rolle spielen, wie groß die Markierung ist – es sollen also sowohl mehrere Spalten als auch mehrere Zeilen markierbar sein, auch in Kombination miteinander. Noch schöner wäre es, wenn ein in der Zwischenablage befindlicher Wert gleich als Standardwert in der InputBox angezeigt würde.
Dies gilt es nun zu realisieren – machen wir uns ans Werk!
Einsatz der fertigen Lösung
Die fertige Lösung wird in Form einer Klasse kommen, die alle notwendigen Funktionen liefert. Die Klasse heißt clsDatasheetInsert und sollte in dem Hauptformular, welches das in der Datenblattansicht erscheinende Unterformular enthält, mit der Objektvariablen objDatasheetInsert deklariert werden:
Dim objDatasheetInsert As clsDatasheetInsert
In die Prozedur, die durch das Ereignis Beim Laden des Hauptformulars ausgelöst wird, fügen wir die folgenden Anweisungen ein:
Private Sub Form_Load() Set objDatasheetInsert = New clsDatasheetInsert With objDatasheetInsert Set .Form = Me!sfmArtikel.Form End With End Sub
Die erste Anweisung instanziert das neue Objekt und speichert einen Verweis darauf in der Variablen objData-sheetInsert. Die Anweisung innerhalb des With-Konstrukts weist der Eigenschaft Form des Objekts einen Verweis auf das Unterformular in der Datenblattansicht zu, das mit den Funktionen zum Füllen mehrerer Felder gleichzeitig versehen werden soll.
Danach können Sie dann wie in Bild 2 die Zielfelder markieren, mit der rechten Maustaste das Kontextmenü aufrufen und dann den Eintrag Wert einfügen auswählen, um die gewünschten Daten in die markierten Felder einzufügen.
Bild 2: Einfügen von Werten per Kontextmenü
Kontextmenü-Eintrag hinzufügen
Für diese Funktion benötigen wir zunächst einmal einen Kontextmenü-Eintrag – und zwar nicht in irgendeinem Kontextmenü, sondern genau in dem, das beim Rechtsklick auf eines der Felder oder auf mehrere markierte Felder erscheint.
Für einen groben überblick, um welches Kontextmenü es sich handeln könnte, schauen wir uns die Liste aller Commandbar-Objekte an, die den Typ msoBarTypePopup aufweisen. Das erledigt die folgende Prozedur, welche die Namen der Kontextmenüs im Direktbereich ausgibt (für diese und weitere Prozeduren benötigen Sie einen Verweis auf die Bibliothek Microsoft Office x.0 Object Library – s. Bild 3):
Bild 3: Verweis auf die Office-Bibliothek
Public Sub CommandbarsAusgeben() Dim cbr As Office.CommandBar For Each cbr In CommandBars If cbr.Type = msoBarTypePopup Then Debug.Print cbr.Name End If Next cbr End Sub
Hier erscheinen gleich zu Beginn die folgenden beiden Einträge:
- Form Datasheet
- Form Datasheet Cell
Nun müssen wir nur noch herausfinden, welches der beiden Kontextmenüs beim Anklicken eines Feldes im Datenblatt erscheint. Dazu legen wir einfach in allen betroffenen Kontextmenüs einen neuen Eintrag an, der den Namen des Kontextmenüs trägt:
Public Sub CommandbarsTesten() Dim cbr As Office.CommandBar Dim cbb As Office.CommandBarButton Set cbr = CommandBars.Item("Form Datasheet Cell") Set cbb = cbr.Controls.Add(msoControlButton, , , , True) cbb.Caption = "Form Datasheet Cell" Set cbr = CommandBars.Item("Form Datasheet") Set cbb = cbr.Controls.Add(msoControlButton, , , , True) cbb.Caption = "Form Datasheet" End Sub
Die Set cbr …-Anweisung referenziert jeweils eines der betroffenen Kontextmenüs, die Set cbb …-Anweisung fügt ein neues Steuerelement des Typs msoControlButton hinzu. über die Caption-Eigenschaft legt die Prozedur die Beschriftung des jeweiligen Eintrags fest.
Danach klicken wir einfach mit der rechten Maustaste auf das gewünschte Element im Datenblatt und erhalten ganz unten die Antwort auf unsere Frage: Das Kontextmenü Form Datasheet Cell ist unser gesuchtes Objekt (s. Bild 4).
Bild 4: Ermitteln des Kontextmenüs für das Datenblatt
Werte einfügen per Klasse
Weiter oben haben wir ja bereits erläutert, dass wir die Funktionalität zum Einfügen von Daten in mehrere Felder gleichzeitig in einer Klasse kapseln möchten. Das hat den Vorteil, dass Sie den Code ganz einfach in mehreren Formularen wiederverwenden können.
Diese Klasse heißt clsDatasheetFill und enthält eine Property Set-Prozedur, die einen Verweis auf das zu unterstützende Formular erwartet. Diesen Verweis speichert die Klasse in der wie folgt im Klassenmodul deklarierten Variablen:
Dim m_Form As Form
Die Prozedur selbst finden Sie in Listing 1. Sie trägt zuerst den Verweis auf das per Parameter übergebene Formular-Objekt in die Variable m_Form ein. Dann sorgt sie noch für die Erweiterung des Kontextmenüs um einen Eintrag mit dem Text Wert einfügen. Dazu füllt sie die Variable cbr mit einem Verweis auf das Element Form Datasheet Cell der CommandBars-Auflistung.
Public Property Set Form(frm As Form) Dim cbr As Office.CommandBar Dim i As Integer Set m_Form = frm Set cbr = CommandBars.Item("Form Datasheet Cell") For i = cbr.Controls.Count To 1 Step -1 If cbr.Controls.Item(i).Caption = "Wert einfügen" Then cbr.Controls.Item(i).Delete End If Next i Set cbbInsert = cbr.Controls.Add(msoControlButton, , , , True) cbbInsert.Caption = "Wert einfügen" End Property
Listing 1: Einrichten des Kontextmenüs
Anschließend durchläuft sie alle Einträge dieses Kontextmenüs und prüft diese auf die Beschriftung Wert einfügen. Sind bereits Einträge mit dieser Beschriftung vorhanden, entfernt die Prozedur diese mit der Delete-Methode des jeweiligen Elements. Danach legt sie diesen Eintrag neu an und speichert einen Verweis darauf in der Variablen cbbInsert. Die folgende Anweisung stellt nur noch die Beschriftung auf Wert einfügen ein.
Die Variable cbbInsert wurde noch nicht deklariert. Da wir für den neu hinzugefügten Eintrag zum Kontextmenü auch eine Ereignisprozedur hinterlegen möchten, die beim Anklicken des Eintrags ausgelöst wird, benötigen wir noch eine entsprechende, mit dem Schlüsselwort WithEvents ausgestattete Variable.
Auch diese landet wieder im Kopf des Klassenmoduls:
Dim WithEvents cbbInsert As CommandBarButton
Wie legen Sie nun die benötigte Ereignisprozedur an Dazu wählen Sie einfach im Klassenmodul im linken Kombinationsfeld den Eintrag cbbInsert aus, der dort nur deshalb erscheint, weil Sie die Objektvariable mit With-Events deklariert haben. Nun erscheint im rechten Kombinationsfeld das Standardereignis Click (im übrigen auch das einzige Ereignis dieses Objekts) und der VBA-Editor legt die passende Ereignisprozedur an (s. Bild 5).
Bild 5: Anlegen einer Ereignisprozedur, die beim Anklicken eines Kontextmenü-Eintrags ausgelöst wird
Diese Prozedur ergänzen Sie nun wie in Listing 2. Die Prozedur fragt per Inputbox den einzufügenden Inhalt ab. Dabei verwendet sie als Standardwert einen Wert, den die Funktion AusZwischenablage liefert.
Private Sub cbbInsert_Click(ByVal Ctrl As Office.CommandBarButton, CancelDefault As Boolean) Dim strInhalt As String Dim bolFill As Boolean strInhalt = InputBox("Geben Sie den einzufügenden Wert an:", "Zellen füllen", AusZwischenablage) If Len(strInhalt) > 0 Then bolFill = True Else If MsgBox("Felder leeren", vbYesNo) = vbYes Then bolFill = True End If End If If bolFill Then Fill strInhalt End If End Sub
Listing 2: Die Ereignisprozedur, die beim Auswählen des Kontextmenü-Eintrags ausgelöst wird
Diese finden Sie im Modul mdlTools. Die Funktion gibt den aktuellen Inhalt der Zwischenablage zurück. Auf diese Weise könnte der Benutzer wie gewohnt den einzufügenden Text per Strg + C oder mit dem Kontextmenü-Befehl Kopieren ermitteln.
Enthält die Variable strInhalt nach dem Eintragen des Ergebnisses der InputBox-Funktion keine leere Zeichenfolge, stellt die Prozedur die Variable bolFill auf den Wert True ein. Falls nicht, soll die Prozedur prüfen, ob der Benutzer vielleicht einfach nur die Abbrechen-Schaltfläche betätigt hat (was ebenfalls eine leere Zeichenkette zurückliefert) oder ob er tatsächlich eine leere Zeichenkette eingegeben hat, um die markierten Felder der Datenblattansicht zu leeren beziehungsweise mit einer leeren Zeichenkette zu füllen. Wählt der Benutzer hier die Antwort Ja (vbYes), wird bolFill ebenfalls auf True eingestellt. Hat bolFill im Anschluss den Wert True, ruft die Prozedur die Routine Fill mit dem einzusetzenden Wert aus der Variablen strInhalt als Parameter auf.
Füllen der markierten Felder
Nun folgt der interessante Teil der Lösung. Hier benötigen wir zunächst einen Ansatz, um die markierten Felder zu füllen.
Um die entsprechenden Inhalte zu ändern, gibt es zwei Wege:
- Bearbeiten der Datenherkunft des Formulars in der Datenblattansicht und Aktualisieren des Datenblatts mit den geänderten Daten oder
- Durchlaufen der markierten Felder und direktes Eintragen der Werte per VBA.
Für die erste Variante müssten wir jeweils den Namen des Feldes ermitteln sowie den Primärschlüsselwert des Datensatzes mit dem zu ändernden Feld.
Beides ist mit erhöhtem Aufwand verbunden, da wir zum Beispiel den Namen des Primärschlüsselfeldes ermitteln müssten, um dann mit dem Primärschlüsselwert auf den zu ändernden Datensatz zuzugreifen.
Die zweite Variante erscheint etwas intuitiver, da sie keinen direkten Zugriff auf die zugrunde liegenden Daten erfordert, sondern über die Benutzeroberfläche auf die zu ändernden Felder zugreift.
In beiden Fällen müssen wir die aktuelle Markierung zum Zeitpunkt der Betätigung des Kontextmenü-Eintrags ermitteln. Dies scheint relativ einfach zu sein – wozu gibt es schließlich die Eigenschaften SelTop, SelLeft, SelHeight und SelWidth des Form-Objekts
Leider liefern diese Eigenschaften etwas merkwürdige Werte, wie folgende Beispiele zeigen:
- Erstes Feld von links, erstes von oben: SelLeft = 2, SelWidth = 0, SelTop = 1, SelHeight = 0
- Linkes Feld, zweites von oben: SelLeft = 2, SelWidth = 0, SelTop = 2, SelHeight = 0
- Zweites Feld von links, erstes von oben: SelLeft = 3, SelWidth = 0, SelTop = 1, SelHeight = 0
- Zweites Feld von links, zweites von oben: SelLeft = 3, SelWidth = 0, SelTop = 2, SelHeight = 0
Der Index von links aus gesehen scheint mit dem Wert 2 zu beginnen statt mit 1, die Breite und Höhe der Markierung wird jeweils mit dem Wert 0 angegeben. Der Rest ist reproduzierbar.
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