Translate

Samstag, 31. Januar 2015

ACCESS: Klassenmodul mit ausgelagerten Events und Mehrfachinstanziierung über Collection

Heute möchte ich mich dem Thema selbstdefinierte Klassenmodule widmen, mit der besonderheit nicht nur Funktionalität auszulagern, sondern auch die Events von Formularen und deren Controls. Des weiteren möchte ich aufzeigen, wie mehrere Klasseninstanziierungen in einer Collection verwaltet werden können.
Hinweis: Diese Technik steht ab der Version Access 2002 zur Verfügung.

Nehmen wir mal an, wir möchten die Anzahl der Änderungen in einem Textfeld-Steuerelement (ggf. sogar in sämtlichen) zählen und diese Anzahl soll über ein Steuerelement im Formular angezeigt werden.

Angenommen unser Eingabe-Steuerelement trägt den Namen Suchname und die Anzahl der Änderungen soll im Steuerelement mit dem Namen txtAnzAenderungen ausgegeben werden.
Eine simple Aufgabe!
Und hier ist dann auch gleich die einfache Lösung: 
Private Sub Suchname_AfterUpdate()
    Me.txtAnzAenderungen = Me.txtAnzAenderungen + 1
End Sub

Sollte jedoch diese Funktionalität für mehrer Textfelder (z.B. alle im Formular) benötigt werden, dann muss im Code für jedes einzelne Textfeld eine Event-Prozedur angelegt werden. Das wird sehr unübersichtlich und erzeugt schnell langen Code.
Vielleicht wird diese Funktionalität sogar in anderen Formularen auch noch benötigt.
Und wieder muss der exakt gleiche Code vervielfältigt werden.
Und plötzlich wird unsere einfache Lösung zu einem riesen Konstrukt!

Möchten wir das? Nein! Daher lagern wir die Funktionalität und auch die Events in einem Klassenmodul einfach aus.
Unser selbstdefinierte Klassenmodul mit dem Namen clsEventManager sieht dann so aus:


Option Compare Database
Option Explicit

    Dim cTargetControl As Access.TextBox
    Private WithEvents cTxtBox As Access.TextBox
    Private WithEvents cFrm As Access.Form

    Public Function Ini(MyTextBox As Access.TextBox _
                         , Optional TargetControl As Access.TextBox) As Boolean

    'Hier wird auf das Formular vom Steuerelement MyTextBox referenziert
    Set cFrm = MyTextBox.Parent
    'Aktivierung der gewünschten Event-Prozeduren vom Formular
    cFrm.AfterUpdate = "[Event Procedure]"
    cFrm.OnUndo = "[Event Procedure]"
   
    'Hier wird auf das Steuerelement MyTextBox referenziert
    Set cTxtBox = MyTextBox
    'Aktivierung der gewünschten Event-Prozedur vom Steuerelement
    cTxtBox.AfterUpdate = "[Event Procedure]"
   
    Set cTargetControl = TargetControl
    Ini = True
End Function

Private Sub cTxtBox_AfterUpdate()
    If Not cTargetControl Is Nothing Then cTargetControl = cTargetControl + 1
End Sub

Private Sub cFrm_AfterUpdate()
  'Zähler auf 0 setzen,da alle Änderungen im Datensatz gerade gespeichert wurden 
  If Not cTargetControl Is Nothing Then cTargetControl = 0
End Sub

Private Sub cFrm_Undo(Cancel As Integer)
    'Zähler auf 0 setzen, weil alle Änderugen im Datensatz abgebrochen wurden
    If Not cTargetControl Is Nothing Then cTargetControl = 0
End Sub


In der Objektvariable cTargetControl ist das Ziel-Textfeld (txtAnzAenderungen) von unserem Formular gespeichert, im welchen die Anzahl der Änderungen ausgegeben wird.
In der Objektvariable cTextBox ist das Textfeld (Suchname) von unserem Formular in dem die Änderungen gemacht werden gespeichert. Damit diese mit Events belegt werden kann, wurde diese mit Private WithEvents dimensioniert.
In der Objektvariable cFrm ist das Formular in dem die Änderungen gemacht werden gespeichert. Diese wurde auch mit Private WithEvents dimensioniert, da wir auch hier Events benötigen.
Die Aktivierung der Events über die Objektvariablen findet mit dem Eintrag ...= "[Event Procedure]" statt.
Das ist im Prinzip das Entscheidende, um Events auslagern zu können.

In unserem Formular muss jetzt nur noch die Klasse aktiviert (instanziiert) werden und dabei das gewünschte Textfeld (über die Funktion Ini) übergeben werden: 
Option Compare Database
Option Explicit 
    Dim EM As clsEventManager

Private Sub Form_Load()
    'Klasse wird instanziiert und in der Objektvariable EM gespeichert
    Set EM = New clsEventManager 
    
    'Die betreffenden Steuerelemente werden der Klasse bekannt gemacht 
    EM.Ini Me.Suchname, Me.txtAnzAenderungen
End Sub 
Wir stellen fest, in unserem Formular werden keine Event-Prozeduren mehr benötigt, die Klasse muss nur einmal instanziiert werden (geschieht hier beim Öffnen des Forms) und alles anderen übernimmt die Klasse inkl. Events! 

Möchten wir jetzt mehrere Textfelder mit dieser Funktionalität belegen, dann brauchen wir einfach nur weitere Klassen zu instanziieren. Dies würde dann so aussehen: 
Option Compare Database
Option Explicit

     Dim EM1 As clsEventManager
    Dim EM2 As clsEventManager
    Dim EM3 As clsEventManager

Private Sub Form_Load()
    Set EM1 = New clsEventManager
    EM1.Ini Me.Suchname, Me.txtAnzAenderungen
   
    Set EM2 = New clsEventManager
    EM2.Ini Me.Bemerkung, Me.txtAnzAenderungen
   
    Set EM3 = New clsEventManager
   EM3.Ini Me.Hobbies, Me.txtAnzAenderungen
End Sub 
Und wir sind nun sogar in der Lage diese Funktionalität inkl. Events in weiteren Formularen zu integrieren, lediglich die Textfelder müssen angepasst werden. Cool Sache, oder?!

Was ist aber wenn wir diese Funktionalität auf sehr viele Textfelder setzen möchten, ggf. sogar alle Textfelder vom Formular.
Dafür benötigen wir abermals eine elegante Lösung, und hier kommt die Collection ins Spiel.
Das Prinzip ist recht einfach, über eine Schleife finden wir alle Textfelder vom Formular heraus, und instanziieren für jedes eine Klasse. Die instanziiert Klasse wird dann in einer Collection gespeichert, so wie im nachfolgenden Code beschrieben ist: 
Option Compare Database
Option Explicit
    'Diese Variable speichert alle Klassenmodule die instanziiert werden
    Dim colEM As New Collection

Private Sub Form_Load()
    Dim EM As clsEventManager
    Dim ctl As Access.Control
   
    'Schleife durch alle Controls im aktuellen Formular
    For Each ctl In Me.Controls
        If ctl.ControlType = acTextBox Then
            'Es handelt sich um ein Textfeld-Steuerelement
            Set EM = New clsEventManager
            If EM.Ini(ctl, Me.txtAnzAenderungen) Then
                'Die instanziierte Klasse wird der Collection gespeichert
                colEM.Add Item:=EM, Key:=ctl.Name
            End If
            Set EM = Nothing 'EM wird nicht mehr benötigt, da in Collection
        End If
    Next
End sub
Dieser Code kann nun in jedes Formular integriert werden, lediglich das Textfeld
txtAnzAenderungen muss angepasst werden.

Klasse das bis hierhin durchgehalten wurde :-), an dieser Stelle kann die Beispiel-Datei heruntergeladen werden