GRUNDLAGEN

Ereignisreiche Berichte

15.01.2006 von André Minhorst
Berichte haben gegenüber Formularen einen entscheidenden Vorteil: Sie können Daten flexibel gruppieren und die Gruppierungen mit Kopf- und Fußbereichen versehen. Diese Bereiche sowie die entsprechenden Bereiche des Berichts und jeder Seite offerieren je drei verschiedene Ereignisse, was überschaubar aussieht. Auf den zweiten Blick stellen sich diesea Ereignisse jedoch als etwas vertrackt heraus.

Die einzelnen Berichtsbereiche, zu denen Berichtskopf und -fuß, Seitenkopf und -fuß sowie die Kopf- und Fußbereiche der Gruppierungen sowie der Detailbereich zählen, haben je drei Ereignisse: Beim Formatieren, Beim Drucken und Bei Rücknahme. Davon ausgenommen sind Seitenkopf- und Seitenfußbereich, bei denen das Ereignis Bei Rücknahme fehlt.

Passt es oder passt es nicht?

Der Grund dafür, warum genau die für jede Seite einmal erscheinenden Bereiche kein Ereignis Bei Rücknahme haben, ist prinzipiell schon die Erklärung der Funktion der drei unterschiedlichen Ereignisse der Berichtsbereiche:

Beim Formatieren: Anpassen von Steuerelementen

Mit dem einen oder anderen Beispiel wird die Bedeutung der einzelnen Berichtsbereiche schnell klar. Das Ereignis Beim Formatieren findet statt, bevor für den aktuellen Bereich ermittelt wird, wie groß er wird, welche Steuerelemente enthalten sind und ob er überhaupt auf die aktuelle Seite passt. Das heißt, dass man Änderungen, die sich auf das Layout der Ausgabe auswirken könnten, hier vornehmen sollte.

Ein einfaches Beispiel liefert wieder einmal die Nordwind-Datenbank. Die Artikel-Tabelle hält in diesem Fall für einen einfachen Bericht mit wenigen Beispielfeldern her. Zusätzlich zu den Artikelinformationen soll dann ein Textfeld angezeigt werden, wenn es sich bei einem Artikel um einen Auslaufartikel handelt und dieser zudem ausverkauft ist (Bild 1). Dieser Text soll nun nicht nur einfach ein- oder ausgeblendet werden, zusätzlich soll beim Ausblenden des Textfeldes kein Leerraum zwischen zwei Datensätzen erscheinen. Dazu stellt man die Eigenschaft Verkleinerbar sowohl für das Textfeld als auch für den Detailbereich auf Ja ein.

Bild 1: Anlegen einer Ereignisprozedur für einen Berichtsbereich.

Falls Sie sich fragen, warum man statt des Textfeldes nicht einfach ein Bezeichnungsfeld für die Anzeige der Meldung verwendet: Nur Textfelder bieten die Eigenschaft Verkleinerbar. Ein Bezeichnungsfeld ließe sich zwar ausblenden, aber der Platz würde nicht für den folgenden Datensatz freigegeben.

Fehlt noch die Ereignisprozedur. Diese fragt lediglich den Wert der beiden zu untersuchenden Felder ab und stellt die Eigenschaft Visible des Textfeldes auf den entsprechenden Wert ein (Listing 1).

Private Sub Detailbereich_Format(Cancel As Integer, FormatCount As Integer)
If Me!Auslaufartikel = True And Me!Lagerbestand = 0 Then
Me!txtHinweis.Visible = True
Else
Me!txtHinweis.Visible = False
End If
End Sub

Zuständigkeiten


Das Ereignis Beim Formatieren des Detailbereichs kann auf die Steuerelemente beziehungsweise Daten des aktuellen Datensatzes zugreifen. Bei den anderen Bereichen sieht es anders aus: In der entsprechenden Ereignisprozedur eines Gruppenkopfes können Sie beispielsweise auf die Steuerelemente des Gruppenkopfes und des ersten im Detailbereich angezeigten Datensatzes zugreifen. Im Gruppenfuß stehen die Steuerelemente des Gruppenfußes sowie des letzten Da tensatzes des Detailbereichs zur Verfügung.

Der Beispielbericht aus Bild 2 und die Ereignisprozeduren aus Listing 2 zeigen dies. Je ein Meldungsfenster gibt den beim Formatieren des Gruppenkopfes beziehungsweise Gruppenfußes aktuellen Datensatz aus. Dabei handelt es sich, wie der Versuch zeigt, um den jeweils ersten beziehungsweise letzten Datensatz der Gruppierung.

Bild 2: Kopfbereich einer Gruppierung mit Ereignisprozedur.

Private Sub Gruppenfuß1_Format(Cancel As Integer, FormatCount As Integer)
MsgBox "Letzter Artikel: " & Me!Artikelname
End Sub
Private Sub Gruppenkopf0_Format(Cancel As Integer, FormatCount As Integer)
MsgBox "Erster Artikel: " & Me!Artikelname
End Sub

Beim Drucken: Feintuning


Fragt sich, was man im Ereignis Beim Drucken noch tun kann und soll. Da gibt es eine kleine Regel: Erledigen Sie dort alle Dinge, die nicht beim Ereignis Beim Formatieren ausgeführt werden müssen. Dabei handelt es sich vorrangig um Aktionen, die Anordnung und Position der enthaltenen Steuerelemente betreffen.

Alles andere – Anpassen von Texten, Färben von Steuerelementen, Ändern von Schriftarten und so weiter – erledigen Sie im Ereignis Beim Drucken.

Der Grund ist einfach: Das Ereignis Beim Formatieren wird unter Umständen für jeden Bereich mehrfach ausgeführt, und mehrfach ausgeführte Operationen kosten Rechenzeit. Soeinfach ist bei Access manchmal!

Beispiel: Anzahl Datensätze je Bereich


Der Bericht aus Bild 3 gruppiert Artikel nach Kategorien. Er soll für jede Kategorie und für jede Seite die Anzahl der enthaltenen beziehungsweise gedruckten Artikel ausgeben. Dazu verwendet er jeweils den folgenden Ausdruck: =Anzahl([Artikel-Nr])

Bild 3: Ausgabe der Anzahl von Artikeln je Kategorie und Seite.

In der Berichtsvorschau zeigt sich dann der Unterschied zwischen Theorie und Praxis: Im Kategoriefuß zeigt der Bericht die Anzahl der enthaltenen Artikel ohne Probleme an, aber im Seitenfuß gibt es einen Fehler. Manche Dinge funktionieren in Berichten einfach nicht so, wie man sie plant. Und bevor man stundenlang im Internet nach dem Grund sucht, beschäftigt man sich lieber mit einer Alternative. Diese bezieht wiederum, wer hätte es gedacht, das Ereignis Beim Drucken ein.

Leeren Sie die Eigenschaft Steuerelementinhalt des Textfeldes im Seitenfuß des Berichts und fügen stattdessen zwei Ereignisprozeduren in den Bericht ein. Die erste wird durch das Ereignis Bei Seite des Berichts ausgelöst und stellt den Wert des Textfeldes auf 0 ein, die zweite tritt beim Ereignis Beim Drucken des Detailbereichs in Aktion und erhöht den Wert dieses Textfeldes um 1. Die beiden Ereignisprozeduren finden Sie in Listing 3.

Private Sub Detailbereich_Print(Cancel As Integer, PrintCount As Integer)
Me!txtAnzahlArtikelSeite = Me!txtAnzahlArtikelSeite + 1
End Sub
Private Sub Report_Page()
Me!txtAnzahlArtikelSeite = 0
End Sub

FormatCount und PrintCount


Diese beiden Parameter FormatCount und Print Count der Ereignisprozeduren Beim Formatieren
beziehungsweise Beim Drucken enthalten die Anzahl der Aufrufe der jeweiligen Ereignisprozedur. Dies ist vor allem für das Ereignis Beim Formatieren interessant, denn es tritt recht häufig auf – nämlich immer dann, wenn ein Bereich (das gilt sowohl für den Detailbereich als auch für die Kopf- und Fußbereiche) nicht mehr auf die aktuelle Seite passt und auf der nächsten Seite gedruckt werden soll.

Wenn Sie im Ereignis Beim Formatieren einen Zähler verwenden, müssen Sie in geeigneter Weise auf die Möglichkeit reagieren, dass der Bereich mehrmals für den gleichen Datensatz aufgerufen wird – indem Sie etwa den Wert des Parameters FormatCount prüfen und den Zähler nur im ersten Durchlauf erhöhen:


If FormatCount = 1 Then
i = i + 1 'irgendetwas zählen
End If

Nicht für Seitenkopf und Zeilenfuß: Bei Rücknahme


Mit dem Ereignis Bei Rücknahme wird es kompliziert. Dieses Ereignis wird ausgelöst, wenn ein Bereich nicht mehr in der gewünschten Form auf eine Seite passt. „In der gewünschten Form“ bedeutet hier, dass für die Eigenschaft Zusammenhalten einer Gruppierung einer der Werte Mit 1. Detaildatensatz oder Ganze Gruppe eingestellt wurde. Ist der Wert Mit 1.Detaildatensatz eingestellt, erscheinen Gruppenkopf und der erste Datensatz auf jeden Fall auf einer Seite. Das heißt, dass beide auf die nächste Seite verschoben werden, wenn der Platz auf der aktuellen Seite nicht mehr ausreicht.

Dementsprechend sorgt Ganze Gruppe dafür, dass am besten alle Datensätze einer Gruppe mit Gruppenkopf und -fuß auf einer Seite dargestellt werden, aber zumindest eine neue Seite je Gruppe begonnen wird.

Das Ereignis Bei Rücknahme tritt nun beispielsweise ein, wenn Access auf einer Berichtsseite einige Datensätze einer Gruppierung gedruckt hat, dann eine neue Gruppierung beginnt, indem es den Gruppenkopf noch auf die gleiche Seite druckt und dann feststellt, dass der Gruppenkopf zumindest mit dem ersten Datensatz zusammen gedruckt werden soll – was aber mangels Platz nicht funktioniert.

Dann tritt das Retreat-Ereignis zunächst in umgekehrter Reihenfolge für alle bereits formatierten Datensätze und dann für den fälschlich formatierten Gruppenkopf ein.

Auch dieses Ereignis müssen Sie berücksichtigen, wenn Sie im Format-Ereignis einen Zähler verwenden. Wenn nämlich für einen Bereich, der bereits formatiert wurde, das Retreat-Ereignis ausgelöst wird, wird der Parameter FormatCount beim darauffolgenden Format-Ereignis für den gleichen Bereich nicht auf den Wert 2 gesetzt!

Experimente mit Bereichsereignissen


Da all dies nicht unbedingt leicht zu verstehen ist, lassen Sie sich einfach einmal ausgeben, welches Ereignis zu welcher Zeit stattfindet. Dazu nutzen Sie den Bericht aus Bild 4. Er verwendet die Tabelle Artikel der Nordwind-Datenbank als Datenherkunft. Zusätzlich zum Detailbereich enthält der Bericht den Kopf- und den Fußbereich einer Gruppierung über die Kategorie der jeweiligen Artikel.

Bild 4: Beispiel für das Testen der verschiedenen Bereichsereignisse.

Jeder der drei Bereiche ist mit dem vollen Angebot an Ereignisprozeduren ausgestattet. Die Prozeduren finden Sie in Listing 4.

Option Compare Database
Option Explicit
Private Sub Detailbereich_Format(Cancel As Integer, FormatCount As Integer)
Debug.Print "Detailbereich_Format " & Me.Artikel_Nr & " " & FormatCount
End Sub
Private Sub Detailbereich_Print(Cancel As Integer, PrintCount As Integer)
Debug.Print "Detailbereich_Print " & Me.Artikel_Nr & " " & PrintCount
End Sub
Private Sub Detailbereich_Retreat()
Debug.Print "Detailbereich_Retreat " & Me.Artikel_Nr
End Sub
Private Sub Gruppenfuß1_Format(Cancel As Integer, FormatCount As Integer)
Debug.Print "Gruppenfuß1_Format " & Me.Artikel_Nr & " " & FormatCount
End Sub
Private Sub Gruppenfuß1_Print(Cancel As Integer, PrintCount As Integer)
Debug.Print "Gruppenfuß1_Print " & Me.Artikel_Nr & " " & PrintCount
End Sub
Private Sub Gruppenfuß1_Retreat()
Debug.Print "Gruppenfuß1_Retreat " & Me.Artikel_Nr
End Sub
Private Sub Gruppenkopf3_Format(Cancel As Integer, FormatCount As Integer)
Debug.Print "Gruppenkopf1_Format " & Me.Artikel_Nr & " " & FormatCount
End Sub
Private Sub Gruppenkopf3_Print(Cancel As Integer, PrintCount As Integer)
Debug.Print "Gruppenkopf1_Print " & Me.Artikel_Nr & " " & PrintCount
End Sub
Private Sub Gruppenkopf3_Retreat()
Debug.Print "Gruppenkopf1_Retreat " & Me.Artikel_Nr
End Sub
Private Sub Report_Page()
Debug.Print "--------------------------"
End Sub

Bild 5 zeigt die Ereignisse, die beim Ausgeben der ersten drei Seiten des Berichts in der Vorschauansicht ausgelöst werden. Die gestrichelte Linie kennzeichnet jeweils das Ereignis Bei Seite, das die Formatierung der Folgeseite einleitet. Hier sind direkt die beiden angesprochenen Fälle zu erkennen. Am Ende der ersten Seite wird für den Artikel mit der Nummer 35 das Format-Ereignis ausgelöst. Dies geschieht zu Beginn der zweiten Seite nochmals, da dieser Artikel nicht mehr auf die erste Seite passt. Wie gewünscht liefert der Parameter FormatCount hier den Wert 2.

Bild 5: Auswertung der Reihenfolge der Berichtsereignisse.

Am Ende der dritten Seite versucht der Bericht, noch eine neue Gruppierung unterzubringen, was aber nicht gelingt, da die Eigenschaft Zusammenhalten der Gruppierung auf Ganze Gruppe eingestellt ist. Dementsprechend wird sowohl für den Gruppenkopf als auch für den ersten Datensatz das Retreat-Ereignis ausgelöst. In Bild 5 ist nicht mehr zu erkennen, dass der Gruppenkopf und der erste Datensatz auf der nächsten Seite zwar erneut formatiert werden, aber der Parameter FormatCount für den Datensatz nicht den eigentlich fälligen Wert 2 liefert. In der Praxis bedeutet dies: Wenn Sie einen Zähler verwenden und mit der Rücknahme von Bereichen zu rechnen ist, müssen Sie den Zähler in den jeweiligen Retreat-Ereignissen wieder vermindern.

Abbrechen, bitte!

Die Ereignisse Format und Print bringen neben dem FormatCount- und dem PrintCount-Parameter noch den von den Formularereignissen sicher bekannten Cancel-Parameter mit. Damit können Sie die Ausgabe eines Bereichs in Abhängigkeit mit den entsprechenden Bedingungen unterbinden.

Zusammenfassung

Wer mit Access komplizierte Berichte erstellen möchte, kommt um die Programmierung der Berichtsereignisse nicht herum. Die beiden Teile dieser Beitragsreihe vermitteln die notwendigen Grundlagen für das Verständnis der zur Verfügung stehenden Ereignisse.