Outlook-Termine programmieren

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

Ereignisse des AppointmentItem-Elements im Objektkatalog

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:

Anlegen des Ereignisses für die Inspectors-Auflistung

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:

Ereignisse eines Outlook-Inspectors

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.

Wechseln zum benutzerdefinierten Formularbereich

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

Schreibe einen Kommentar