In der Beitragsreihe „Ereignisreiche Berichte“ (ab Ausgabe 12/2005) haben Sie bereits erfahren, welche Ereignisse Berichte bereitstellen und für welche Zwecke sie sich einsetzen lassen. Neben diesen Ereignissen liefern Berichte einige Eigenschaften und Methoden, ohne die so manche Aufgabe nicht zu lösen wäre. Im vorliegenden Beitrag erfahren Sie, worum es sich dabei dreht und wie Sie die Eigenschaften und Methoden verwenden.
Steuerelemente und Berichtsbereiche referenzieren
Wenn Sie den Wert eines Steuerelements lesen oder schreiben möchten, greifen Sie wie in Formularen darauf zu: entweder über die entsprechende Auflistung, inklusive Objektname, oder, wenn die Referenz aus dem gleichen Objekt heraus erfolgt, mit dem Schlüsselwort Me.
Für die Variante mit dem Objektnamen gibt es mehrere Möglichkeiten, unter anderem die folgenden:Reports.Item("rptBeispiel").Controls _("ctlBeispiel")
Reports!rptBeispiel!ctlBeispiel
Mit dem Schlüsselwort Me sieht der Zugriff etwa folgendermaßen aus: Me!ctlBeispiel
Berichtsbereiche referenzieren
Auch die einzelnen Berichtsbereiche kann man referenzieren. Das macht Sinn, wenn man beispielsweise einen Bereich ein- oder ausblenden oder eine Eigenschaft ändern möchte. Der Verweis auf einen Berichtsbereich erfolgt über die Section-Auflistung eines Berichts. Dabei kommen für die Standardbereiche Zahlenwerte oder Konstanten in Frage, während man die Gruppenköpfe und -füße nur mit Zahlenwerten referenzieren kann. Den Detailbereich machen Sie etwa mit folgenden Anweisungen unsichtbar:Me.Section(0).Visible = False
Me.Section(acDetail).Visible = False
Für die übrigen Bereiche verwenden Sie die folgenden Konstanten:
-
Berichtskopf: acHeader (1)
-
Berichtsfuß: acFooter (2)
-
Seitenkopf: acPageHeader (3)
-
Seitenfuß: acPageFooter (4)
Die Zahlenwerte für die einzelnen Gruppierungsbereiche sehen folgendermaßen aus: Der Kopfbereich der ersten Gruppierungsebene erhält den Zahlenwert 5, der entsprechende Fußbereich den Zahlenwert 6. Die Kopf- und Fußbereiche der übrigen Gruppierungsebenen werden entsprechend durchnummeriert.
Neben der Konstanten und dem Zahlenwert gibt es eine weitere Möglichkeit, einen Berichtsbereich zu referenzieren. Dabei handelt es sich um den Wert der Eigenschaft Name des jeweiligen Bereichs. Den Namen können Sie nach Ihren eigenen Wünschen anpassen, die standardmäßig vergebenen Namen reichen allerdings meist aus (Bild 1).
Über den Namen können Sie auf einen Bereich so zugreifen, als ob es sich dabei um ein Steuerelement des Berichts handelt. Die Bereichsnamen werden sogar von IntelliSense unterstützt (Bild 2). Die Routine aus Listing 1 zeigt, wie Sie die Namen und Höhen aller vorhandenen Berichtsbereiche im Direktfenster ausgeben können.
Auch die Gruppierungsebenen eines Berichts bieten eine weitere Möglichkeit des Zugriffs, nämlich über die GroupLevel-Auflistung. Die folgende Anweisung gibt etwa den Namen des Feldes aus, nach dem die erste Gruppierungsebene gruppiert wird:
Debug.Print Me.GroupLevel(0).ControlSource
Doch Vorsicht: Die Gruppierungsebenen sind keinesfalls mit den Berichtsbereichen identisch und haben völlig andere Eigenschaften und Methoden.
Private Sub Report_Open(Cancel As Integer)
Dim i As Integer
On Error Resume Next
For i = 0 To 24
Debug.Print Me.Section(i).Name, Me.Section(i).Height
Next i
End Sub
Gruppierungen im Griff
Schauen Sie sich zunächst die Eigenschaften und Methoden der Gruppierungsebenen an. Die Eigenschaften sind weitgehend mit denen aus dem Dialog Sortieren und gruppieren identisch.
Unter VBA stehen die folgenden Eigenschaften zur Verfügung, wobei einige im Anschluss ausführlicher besprochen werden müssen:
-
ControlSource: Gibt das Feld an, nach dem sortiert oder gruppiert werden soll.
-
GroupHeader: Schreibgeschützte Eigenschaft, die angibt, ob die Gruppierung einen Gruppenkopf enthält.
-
GroupFooter: Schreibgeschützte Eigenschaft, die angibt, ob die Gruppierung einen Gruppenfuß enthält.
-
GroupOn: Entspricht der Eigenschaft Gruppieren nach. Die Eigenschaft kann verschiedene Werte annehmen, die vom Datentyp des unter ControlSource angegebenen Feldes abhängen.
-
GroupInterval: Entspricht der Eigenschaft Intervall. Die Eigenschaft kann verschiedene Zahlenwerte angeben, die das Aussehen des Intervalls bestimmen.
-
KeepTogether: Legt fest, ob die Datensätze und Gruppenkopf/-fuß, wenn möglich, auf einer Seite gedruckt werden sollen.
-
SortOrder: Entspricht der Eigenschaft Sortierreihenfolge. Enthält den Wert False (aufsteigende Sortierung) oder True (absteigende Sortierung).
Die Eigenschaften GroupOn und GroupInterval hängen vom Wert der Eigenschaft ControlSource beziehungsweise vom Datentyp des dort angegebenen Feldes ab.
Gruppierung nach einem Textfeld
Wenn Sie etwa nach einem Textfeld gruppieren, kann die Eigenschaft GroupOn die Werte 0 (gruppiert nach dem kompletten Feldwert) oder 1 (gruppiert nach den ersten x Zeichen) annehmen, wobei x dem Wert der Eigenschaft Group-Interval entspricht. Wenn Sie also die Artikel-Tabelle der Nordwind-Datenbank so gruppieren möchten, dass alle Artikel mit dem gleichen Anfangsbuchstaben gruppiert und der Anfangsbuchstabe im Gruppenkopf angezeigt werden soll, realisieren Sie das unter VBA mit der Ereignisprozedur aus Listing 2.
Private Sub Report_Open(Cancel As Integer)
'Erste Gruppierungsebene referenzieren
With Me.GroupLevel(0)
'Zu gruppierendes Feld festlegen
.ControlSource = "Artikelname"
'Gruppieren nach einer bestimmten Anzahl von Zeichen...
.GroupOn = 1
'...die hier festgelegt wird:
.GroupInterval = 1
'Gruppenkopf und Detailbereich sollen zusammengehalten werden.
.KeepTogether = 2
End With
End Sub
Der zugrunde liegende Bericht sieht im Entwurf wie in Bild 3 aus. Das Ergebnis können Sie in Bild 4 betrachten.
Weitere Datentypen
Für die anderen Datentypen sieht das im Prinzip ganz ähnlich aus. Enthält das unter Control-Source angegebene Feld einen Datums-/Zeitdatentyp, sorgt der Wert 0 für die Eigenschaft GroupOn ebenfalls dafür, dass die Gruppierung nach dem ganzen Wert erfolgt. Sie können aber auch nach den im Datum enthaltenen Bestandteilen sortieren, also etwa nach dem Jahr. Dazu verwenden Sie für die Eigenschaft GroupOn die folgenden Zahlenwerte:
-
2: Jahr
-
3: Quartal
-
4: Monat
-
5: Woche
-
6: Tag
-
7: Stunde
-
8: Minute
.
Der Clou ist, dass Sie nicht nur nach den genannten Einheiten gruppieren können, sondern auch nach deren Vielfachen. Sie können also auch jeweils 30 Minuten zusammenfassen. Dazu legen Sie einfach das gewünschte Vielfache mit der Eigenschaft GroupInterval fest.
Im folgenden Beispiel sorgen zwei Gruppierungen und eine Sortierung dafür, dass die Bestellungen der Nordwind-Datenbank nach Datum sortiert und nach Jahren und Monaten gruppiert werden – wobei jeweils zwei Monate zusammengefasst werden sollen.
Den Aufbau des Berichts finden Sie in Bild 5. Damit die Ausgabe wie in Bild 6 aussieht, stellen Sie die Eigenschaften der einzelnen Gruppierungsebenen mit der Prozedur aus Listing 3 ein.
Private Sub Report_Open(Cancel As Integer)
'Äußere Gruppierung: Jahre
With Me.GroupLevel(0)
'Gruppierung nach Bestelldatum
.ControlSource = "Bestelldatum"
'Gruppierungseinheit: Jahre
.GroupOn = 2
'Gruppierungsintervall: 1
.GroupInterval = 1
'Zusammenhalten: nein
.KeepTogether = 0
End With
'Innere Gruppierung: Monate
With Me.GroupLevel(1)
'Gruppierung wiederum nach dem Bestelldatum...
.ControlSource = "Bestelldatum"
'... hier allerdings nach Monaten...
.GroupOn = 4
'... und zwar in Zweier-Intervallen.
.GroupInterval = 2
'Zusammenhalten: mit ersten Detaildatensatz
.KeepTogether = 2
End With
End Sub
Wichtig ist in diesem Zusammenhang, dass die Gruppierungen in der Reihenfolge der Abarbeitung von 0 bis n durchnummeriert werden. Etwas Fummelarbeit ist die Ausgabe der betroffenen Monate, also etwa „Juli/August“. Der dazu verwendete Ausdruck für die Eigenschaft Steuerelementinhalt des entsprechenden Textfeldes lautet folgendermaßen:
=Format("1." & Wenn(Monat([Bestelldatum]) Mod 2=0;Monat([Bestelldatum])-1;Monat([Bestelldatum])) & "." & Jahr([Bestelldatum]);"mmmm") & "/" & Format("1." & Wenn(Monat([Bestelldatum]) Mod 2=0;Monat([Bestelldatum]); Monat([Bestelldatum]+1)) & ." & Jahr([Bestelldatum]);" mmmm")
Der Ausdruck verwendet das Bestelldatum des ersten Datensatzes der Gruppierung und prüft, ob die Monatszahl gerade oder ungerade ist. Als erster der zwei Monate soll immer ein ungerader Monat genannt werden, damit die Monate Januar/Februar, März/April und so weiter zusammen ausgegeben werden. Für die Gruppierung erledigt Access dies automatisch, aber für die Überschrift im Gruppenkopf ist das nicht selbstverständlich.
Weitere Bereichseigenschaften
Natürlich gibt es noch eine Menge weiterer Eigenschaften, mit denen sich Bereiche und damit auch Gruppierungen manipulieren lassen. Diese stehen jedoch nicht zur Laufzeit zur Verfügung, sondern nur beim Entwurf und sollen deshalb hier nicht im Detail besprochen werden.
Bereiche in Aktion
Im Gegensatz zu den zuvor genannten Eigenschaften enthalten die einzelnen Bereiche auch Eigenschaften, die zur Entwurfszeit gar nicht verfügbar sind – das heißt, dass diese auch nicht über das Eigenschaftsfenster der Berichtsbereiche geändert werden können.
Wenn Sie während der Ausgabe eines Berichts auf das Layout des Berichts Einfluss nehmen möchten, um beispielsweise mehrere Datensätze pro Zeile zu drucken, sind die Eigenschaften MoveLayout, NextRecord und PrintSection für Sie interessant.
Zeilenvorschub unterbinden
Im ersten Beispiel kommt die Eigenschaft Move-Layout zum Einsatz. Access-Berichte werden bereichsweise von oben nach unten abgearbeitet. Dabei werden neue Bereiche in der Regel unter dem zuvor ausgegebenen Bereich gedruckt. Mit der Eigenschaft MoveLayout können Sie dies in manchen Fällen gezielt beeinflussen: Wenn Sie diese etwa im Ereignis Beim Drucken des Detailbereichs auf False setzen, erfolgt kein Vorschub auf die nächste Druckposition, sondern der aktuelle Bereich wird auf der gleichen Höhe wie der vorherige Bereich ausgegeben.
Zur Veranschaulichung soll das folgende Beispiel einen Lottozettel mit den Zahlen von 1 bis 49 erzeugen. Weniger Geübte legen vielleicht nun 49 Textfelder oder Bezeichnungsfelder auf dem Bericht an und füllen diese mit den entsprechenden Zahlen. Wer mit VBA vertraut ist und die Eigenschaft MoveLayout kennt, legt nur ein Textfeld an und verwendet eine Tabelle mit den Zahlen 1 bis 49 als Grundlage. Der Bericht sieht dann im Entwurf wie in Bild 7 aus.
Damit der Bericht eine Matrix von sieben mal sieben Zahlen anzeigt, sorgen Sie einfach dafür, dass der Bericht nur alle sieben Datensätze die Zeile wechselt. Ohne weiteres Zutun würden nun jeweils sieben Zahlen an der gleichen Stelle ausgegeben, was die Lesbarkeit natürlich etwas einschränkt. Also sorgen Sie noch dafür, dass das Textfeld jeweils um eine bestimmte Anzahl Pixel nach rechts verschoben wird.
Beides erledigt die Routine aus Listing 4, die beim Formatieren des Detailbereichs ausgelöst wird. Das Ergebnis ist in Bild 8 zu sehen.
Private Sub Detailbereich_Format(Cancel As Integer, FormatCount As Integer)
'Abstand des Textfeldes vom linken Rand festlegen
Me.Controls("txtZahl").Left = ((Me.txtZahl + 6) Mod 7) * 600
'Wenn die Zahl kein Mehrfaches von 7 ist, keine neue Zeile beginnen
If Me.txtZahl Mod 7 = 0 Then
Me.MoveLayout = True
Else
Me.MoveLayout = False
End If
End Sub
Datensatz nicht drucken
Die Eigenschaft PrintSection legt fest, ob der aktuelle Datensatz gedruckt wird oder nicht. Das kann man zwar auch durch entsprechende Einschränkung der Datenherkunft erreichen, aber im Gegensatz dazu bewirkt die Einstellung Print-Section = False, dass ein dem Bereich entsprechender Zeilenvorschub erfolgt, aber keine Inhalte ausgegeben werden.
Nicht zum nächsten Datensatz springen
Möchten Sie einen Datensatz nicht nur einmal, sondern mehrmals drucken, setzen Sie die Eigenschaft NextRecord ein. Die Einstellung Next- Record = False sorgt dafür, dass der aktuelle Datensatz erneut gedruckt wird.
Das folgende Beispiel zeigt, wie Sie die beiden Eigenschaften PrintSection und NextRecord kombinieren können, um nach jedem fünften Datensatz einen Abstand einzufügen. Dazu fügen Sie zu einem Bericht mit einer Auflistung von Artikeln einfach die Routine aus Listing 5 hinzu, die beim Formatieren des Detailbereichs ausgelöst wird. Bild 9 zeigt die Auswirkungen auf den Bericht.
Dim intAnzahl As Integer
Private Sub Detailbereich_Format(Cancel As Integer, FormatCount As Integer)
'Anzahl der gedruckten Detailbereiche erhöhen
intAnzahl = intAnzahl + 1
'Nach jedem fünften Datensatz einen leeren Bereich einfügen
If intAnzahl Mod 6 = 0 Then
Me.NextRecord = False
Me.PrintSection = False
Else
Me.NextRecord = True
Me.PrintSection = True
End If
End Sub
Kombinationen der drei Eigenschaften Move-Layout, PrintSection und NextRecord erzielen die unterschiedlichste Wirkung. Das bekannteste Beispiel dürfte der Einsatz beim Drucken von Etiketten sein – hier geht es vor allem darum, eventuell bereits entnommene Etiketten auf dem zu bedruckenden Etikettenbogen auszulassen und je Etikett beliebig viele Kopien drucken zu können. Da dieses Beispiel jedoch im Internet in allen Varianten zu finden ist, geht dieser Beitrag nicht näher darauf ein. Interessanter sind da die unterschiedlichen Kombinationen der drei Eigenschaften:
-
PrintSection = False: Statt des aktuellen Datensatzes wird ein leerer Bereich gedruckt.
-
NextRecord = False: Der aktuelle Datensatz wird erneut gedruckt.
-
MoveLayout = False: Es findet kein Zeilenvorschub statt, der folgende Datensatz wird über den aktuellen Datensatz gedruckt.
-
PrintSection = False und NextRecord = False: Wie im obigen Beispiel wird ein leerer Bereich gedruckt, aber kein Datensatz ausgelassen.
-
PrintSection = False und MoveLayout = False: Ein Datensatz wird komplett ausgelassen. Die übrigen Kombinationen sind nicht zulässig.