Datenblattmarkierung füllen

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.

Das wäre schön: Die markierten Felder alle mit einem Schlag beispielsweise aus- oder abwählen

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.

Einfügen von Werten per Kontextmenü

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):

Verweis auf die Office-Bibliothek

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).

Ermitteln des Kontextmenüs für das Datenblatt

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).

Anlegen einer Ereignisprozedur, die beim Anklicken eines Kontextmenü-Eintrags ausgelöst wird

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

Schreibe einen Kommentar