Wie wir im Beitrag „Benutzerdefinierte Felder in Outlook“ gezeigt haben, können Sie beispielsweise einem Outlook-Termin weitere Seiten mit benutzerdefinierten Steuerelementen hinzufügen. Wie nicht anders zu erwarten, lassen sich diese Elemente auch programmieren. Ein Einsatzzweck ist, die benutzerdefinierten Steuer-elemente beziehungsweise die dahinter stehenden Felder vorab mit Standardwerten zu füllen – beispielsweise das Feld „Mitarbeiter“ mit dem Namen des aktuellen Mitarbeiters.
Ereignisse von Terminen und Steuerelementen
Uns als Access-Entwickler interessiert neben den Objekten und Eigenschaften auch, welche Ereignisse die Objekte auslösen, mit denen wir arbeiten. Und natürlich lösen auch Outlook-Termine Ereignisse aus und auch die durch den Entwickler hinzugefügten Steuer-elemente.
Schauen wir in den Objektkatalog, finden wir allein für das AppointmentItem-Objekt, um das es hier im Wesentlichen geht, einige Ereignisse (siehe Bild 1).
Bild 1: Ereignisse des AppointmentItem-Elements im Objektkatalog
Um ein AppointmentItem, in diesem Beitrag auch Termin genannt, so zu programmieren, dass wir die hier aufgeführten Ereignisse implementieren, benötigen wir eine andere Herangehensweise als beispielsweise bei der Programmierung eines Access-Formulars oder seiner Steuer-elemente.
Dort brauchen wir nur explizit die gewünschten Ereignisse in das jeweilige Klassenmodul zu schreiben und für die Ereigniseigenschaft den Wert [Ereignisprozedur] zu hinterlegen. Unter Outlook sieht das etwas anders aus – wie genau, schauen wir uns nun im Detail an.
Ereignisse beim Bearbeiten
Als Erstes schauen wir uns an, wie wir Ereignisse abfangen können, die beim Bearbeiten eines Termins ausgelöst werden. Dazu brauchen wir eine Instanz des Termins. Wie kommen wir an diese heran Dazu gehen wir den Weg über das sogenannte Inspector-Element. Inspector ist das Fenster, in dem ein Outlook-Objekt – wie in diesem Fall ein Termin – angezeigt wird. Über das Inspector-Objekt können wir schließlich auf den enthaltenen Termin zugreifen, wenn der Benutzer diesen geöffnet hat.
Neu geöffneten Inspector referenzieren
Um auf ein frisch geöffnetes Termin-Objekt zugreifen zu können, benötigen wir das Inspector-Objekt. Dieses erhalten wir über die Inspectors-Auflistung. Dazu deklarieren wir eine Objektvariable wie die folgende im Klassenmodul ThisOutlookSession des VBA-Projekts von Outlook:
Private WithEvents objInspectors As Inspectors
Wir verwenden das Schlüsselwort WithEvents, um die Ereignisse dieses Objekts implementieren zu können. Die Inspectors-Auflistung hat nur eine Ereigniseigenschaft, die wir implementieren können, indem wir im linken Kombinationsfeld im VBA-Fenster des Moduls ThisOutlookSession den Eintrag objInspectors auswählen. Im rechten Kombinationsfeld wird dann automatisch das einzige Ereignis ausgewählt und im VBA-Fenster die Ereignisprozedur objInspectors_NewInspector angelegt (siehe Bild 2). Diese Prozedur sieht zunächst wie folgt aus:
Bild 2: Anlegen des Ereignisses für die Inspectors-Auflistung
Private Sub objInspectors_NewInspector(ByVal Inspector As Inspector) End Sub
Diese Ereignisprozedur wird im aktuellen Zustand des Moduls niemals ausgelöst, denn die Objektvariable objInspectors wurde noch nicht gefüllt. Das holen wir nun nach, indem wir ihr in der Ereignisprozedur Application_Startup die Auflistung zuweisen:
Private Sub Application_Startup() Set objInspectors = Outlook.Application.Inspectors End Sub
Die Prozedur Application_Startup wird bei jedem Start von Outlook aufgerufen. Sie können nun Outlook neu starten, um diese Prozedur auszulösen, oder Sie rufen diese einfach durch Platzieren der Einfügemarke in der Prozedur und Betätigen der Taste F5 auf. Setzen Sie außerdem einen Haltepunkt in die Prozedur objInspectors_New-Inspector. Wenn Sie nun zum Anwendungsfenster von Outlook wechseln und durch einen Doppelklick in den Kalender einen neuen Termin öffnen, wird die Prozedur objInspectors_NewInspector ausgelöst und stoppt am Haltepunkt.
Neues Inspector-Fenster referenzieren
Damit kommen wir zum nächsten Schritt: Der Parameter Inspector der Prozedur objInspectors_NewInspector liefert einen Verweis auf das neue Inspector-Fenster. Dieses können wir nun wiederum mit einer Objektvariablen referenzieren, um seine Ereignisse implementieren zu können. Dazu fügen wir dem Modul ThisOutlookSession die folgende Objektvariable hinzu, wiederum mit dem Schlüsselwort WithEvents ausgestattet:
Private WithEvents objInspector As Inspector
Wenn wir das neue Objekt im linken Kombinationsfeld des VBA-Fensters auswählen, finden wir im rechten Kombinationsfeld alle dafür zur Verfügung stehenden Ereignisse vor – zum Beispiel eines, das beim Wechseln der Seite ausgelöst wird (siehe Bild 3). Um dieses auszuprobieren, stellen wir in der Ereignisprozedur objInspectors_NewInspector die Objektvariable objInspector auf das mit dem Parameter Inspector gelieferte Objekt ein:
Bild 3: Ereignisse eines Outlook-Inspectors
Private Sub objInspectors_NewInspector( ByVal Inspector As Inspector) Set objInspector = Inspector End Sub
Dann implementieren wir das Ereignis PageChange der neuen Objektvariablen und geben darin den Wert des Parameters ActivePageName aus:
Private Sub objInspector_PageChange( ActivePageName As String) Debug.Print "Aktuelle Seite: " & ActivePageName End Sub
Damit können wir nun zwischen den Seiten Termin und Terminplanung hin- und herwechseln und erhalten jeweils den Titel der aktiven Seite im Direktbereich. Wenn Sie, wie im Beitrag Benutzerdefinierte Felder in Outlook (www.access-im-unternehmen.de/1221) beschrieben, bereits eine weitere Seite namens Termindetails angelegt haben, können Sie auch diese aufrufen und den jeweiligen Namen ausgeben.
Unterscheidung zwischen verschiedenen Inspector-Objekten
Wir haben nun bewusst immer Termin-Einträge im Inspector-Objekt geöffnet. Aber auch beim Öffnen von E-Mails oder Kontakten wird das Ereignis objInspectors_NewInspector ausgelöst. Wenn wir nun nicht mehr nur mit objInspector das geöffnete Inspector-Objekt referenzieren wollen, sondern auch mit einer weiteren Objektvariablen namens objAppointmentItem den im Inspector enthaltenen Termin, kann es zu Problemen kommen – beispielsweise, wenn wir gar keinen Termin-Inspector geöffnet haben.
In diesem Fall müssen wir also zuvor prüfen, ob überhaupt ein Inspector mit einem Termin geöffnet wurde.
Dazu deklarieren wir wieder eine Objektvariable mit dem Schlüsselwort WithEvents für das AppointmentItem-Objekt im neu geöffneten Inspector:
Private WithEvents objAppointmentItem As AppointmentItem
Dann erweitern wir die Prozedur objInspectors_NewInspector so, dass wir den Typ des enthaltenen Elements prüfen und nur im Falle eines Appointments einen Verweis auf das Element in die Variable objAppointmentItem schreiben:
Private Sub objInspectors_NewInspector( ByVal Inspector As Inspector) Set objInspector = Inspector Select Case Inspector.CurrentItem.Class Case olAppointment Set objAppointmentItem = Inspector.CurrentItem End Select End Sub
Interessant sind zum Beispiel die beiden Ereignisse PropertyChange und CustomPropertyChange, die jeweils beim Ändern von eingebauten und benutzerdefinierten Ereignissen ausgelöst werden. Das Ereignis PropertyChange können Sie so implementieren:
Private Sub objAppointmentItem_PropertyChange( ByVal Name As String) Debug.Print "Eigenschaft geändert: " & Name End Sub
Hier können wir leider nicht über die Properties-Auflistung auf die Eigenschaft mit dem geänderten Wert zugreifen, denn diese Auflistung gibt es schlicht nicht. Sie könnten aber per Select Case auf die Änderung bestimmter Eigenschaften reagieren, die für den Anwendungsfall interessant sind. Im Beispiel aus Listing 1 geben wir die Werte für die Eigenschaften Subject und Start aus, wenn diese geändert wurden.
Private Sub objAppointmentItem_PropertyChange(ByVal Name As String) Select Case Name Case "Subject" Debug.Print "Die Eigenschaft '" & Name & "' wurde geändert." Debug.Print "Neuer Wert: " & objAppointmentItem.Subject Case "Start" Debug.Print "Die Eigenschaft '" & Name & "' wurde geändert." Debug.Print "Neuer Wert: " & objAppointmentItem.Start End Select Debug.Print "Eigenschaft geändert: " & Name End Sub
Listing 1: Ereignisprozedur beim Ändern einer eingebauten Eigenschaft
Objektvariablen erneut initialisieren
Wenn Sie mit den hier vorgestellten Ereignisprozeduren experimentieren, werden Sie gelegentlich feststellen, dass diese nicht mehr wie gewünscht funktionieren. Es kann sein, dass die Objektvariablen bei Codeänderungen zur Laufzeit oder durch Laufzeitfehler geleert werden. Damit werden dann natürlich auch nicht mehr die für diese Objektvariablen implementierten Ereignisse ausgelöst. Sie sollten also gelegentlich die Prozedur Application_Startup erneut ausführen, um die Objektvariablen zu initialisieren.
Änderungen von benutzerdefinierten Eigenschaften
Im oben genannten Beitrag Benutzerdefinierte Felder in Outlook haben wir einem Termin eine neue Seite mit einem Feld namens Mitarbeiter hinzugefügt. Eine Änderungen an einem solchen Feld können wir nicht über die Ereignisprozedur PropertyChange erfassen, sondern wir benötigen dazu die Ereignisprozedur CustomPropertyChange. Diese liefert ebenfalls mit dem Name-Parameter den Namen der geänderten Eigenschaft. Im Gegensatz zu den eingebauten Eigenschaften können wir hier allerdings über die Auflistung UserProperties des AppointmentItem-Elements auf die Eigenschaft und ihren Wert zugreifen. Das erledigen wir wie folgt:
Private Sub objAppointmentItem_CustomPropertyChange( ByVal Name As String) Debug.Print "Benutzerdefinierte Eigenschaft geändert: " & Name Debug.Print "Neuer Wert: " & objAppointmentItem.UserProperties(Name) End Sub
Schließen von Termin-Objekten
Mit den oben vorgestellten Ereignissen können Sie bereits auf Änderungen an den eingebauten und benutzerdefinierten Eigenschaften von Outlook-Terminen zugreifen. Damit könnten Sie die geänderten Eigenschaften beispielsweise direkt mit einer Tabelle in einer Access-Datenbank synchronisieren. Allerdings wäre es ressourcenschonender, wenn wir dies erst beim Schließen des Termin-Elements erledigen.
Dazu können wir beispielsweise eines der Ereignisse Close oder Unload nutzen. Wir legen beide Ereignisprozeduren an und versehen diese mit Haltepunkten, um zu prüfen, zu welchem Zeitpunkt diese beiden Ereignisse ausgelöst werden und in welcher Reihenfolge. In unseren Tests haben wir festgestellt, dass nur Close ausgelöst wird, Unload jedoch nicht.
Für unsere Zwecke würde es jedoch reichen, wenn die Close-Ereignisprozedur ausgelöst wird – in dieser können wir noch auf alle Eigenschaften des Termin-Elements zugreifen und wir können das Schließen gegebenenfalls auch noch abbrechen, wenn Informationen fehlen. Das gestalten wir dann beispielsweise so:
Private Sub objAppointmentItem_Close(Cancel As Boolean) If objAppointmentItem.UserProperties("Mitarbeiter") = "" Then MsgBox "Tragen Sie den Mitarbeiter ein." Cancel = True End If End Sub
Das setzt natürlich voraus, dass Sie wie im Beitrag Benutzerdefinierte Felder in Outlook beschrieben, ein benutzerdefiniertes Feld namens Mitarbeiter angelegt und auch über die Benutzeroberfläche verfügbar gemacht haben. Praktisch wäre es, wenn wir den Bereich, in dem sich das Steuer-element zur Eingabe des benutzerdefinierten Feldes befindet, noch einblenden könnten. Dazu erweitern wir die Prozedur wie in Listing 2. Hier fragen wir den Benutzer noch, ob er den Mitarbeiter eintragen möchte. Falls ja, wechseln wir mit der Methode SetCurrentFormPage des Inspector-Objekts zum Formularbereich Termindetails.
Private Sub objAppointmentItem_Close(Cancel As Boolean) Dim intResult As VbMsgBoxResult If objAppointmentItem.UserProperties("Mitarbeiter") = "" Then intResult = MsgBox("Es ist kein Mitarbeiter zugeordnet. Noch eintragen", vbYesNo) If intResult = vbYes Then objInspector.SetCurrentFormPage "Termindetails" Cancel = True End If End If End Sub
Listing 2: Abbrechen des Schließens, wenn noch kein Mitarbeiter eingetragen wurde
Damit landen wir dann auf der Seite, die in Bild 4 abgebildet ist. Nach der Eingabe eines Wertes in das Textfeld können Sie das Termin-Element allerdings noch nicht schließen – es erscheint die gleiche Meldung wie zuvor. Der Grund ist, dass der eingegebene Wert nun zwar im Textfeld vorliegt, aber noch nicht in der zugrundeliegenden benutzerdefinierten Eigenschaft. Aber wann landet der Wert im benutzerdefinierten Feld und wie können wir nicht nur auf das benutzerdefinierte Feld, sondern auch auf das Steuer-element und seinen Inhalt zugreifen Das schauen wir uns im nächsten Abschnitt an.
Bild 4: Wechseln zum benutzerdefinierten Formularbereich
Steuer-elemente im Formularbereich referenzieren
Um Steuer-elemente in einem Formularbereich zu referenzieren, benötigen wir zunächst einen Verweis auf den Formularbereich. Aber welchen Objekttyp hat der Formularbereich eigentlich Das finden wir im Zweifel mit der Typename-Funktion heraus. Und wie kommen wir überhaupt an den Formularbereich heran Dazu haben wir im Objektkatalog die Eigenschaften der beteiligten Objekte durchgesehen. Für das Inspector-Element haben wir schließlich die Eigenschaft ModifiedFormPages gefunden, deren Inhalt wir wie nachfolgend der Variablen obj-Pages vom Typ Pages zuweisen. Die Count-Eigenschaft liefert den Wert 1 und die Funktion Typename für diesen einen Eintrag liefert den Wert UserForm:
Private Sub objInspectors_NewInspector( ByVal Inspector As Inspector) Dim objPages As Outlook.Pages Set objInspector = Inspector Select Case Inspector.CurrentItem.Class Case olAppointment Set objAppointmentItem = Inspector.CurrentItem Set objPages = objInspector.ModifiedFormPages Debug.Print objPages.Count Debug.Print TypeName(objPages(1)) End Select End Sub
Damit können wir nun im Kopf des Klassenmoduls die folgende Deklaration vornehmen:
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