Im Beitrag „Serienmails versenden mit CDO“ haben wir einige Prozeduren und Funktionen vorgestellt, mit denen Sie Serien-E-Mails über die CDO-Bibliothek von Windows versenden können. Das macht natürlich nur halb soviel Spaß, wenn nur die nackten Routinen vorliegen. Also zeigen wir im vorliegenden Beitrag auch noch, wie Sie eine praktische Benutzeroberfläche zum Verwalten der für den Versand einer Serienmail benötigten Daten programmieren.
Datenmodell der Anwendung
Die Anwendung zum Versenden von Serienmails enthält einige Tabellen, die wie im Folgenden vorstellen.
Im Beziehungen-Fenster aus Bild 1 sehen Sie die benötigten Tabellen. Die ersten beiden heißen tblKunden sowie tblAnreden. Die Tabelle tblConfigurations enthält die Daten für die Konfiguration des Mailservers und die Tabelle tblMailings die Daten zum Mailing selbst, also eine Bezeichnung, die Absenderadresse, den Betreff, den Inhalt und die Anlagen. Der Tabelle tblMailings haben wir ein Fremdschlüsselfeld namens ConfigurationID hinzugefügt, mit dem wir die für das jeweilige Mailing verwendete Konfiguration speichern können. Außerdem enthält die Tabelle tblMailings zwei Felder, mit denen die Adressaten des Mailings definiert werden. SQLSource nimmt den SQL-Ausdruck auf, der die Empfängeradressen liefert und Mailfield den Namen des Feldes aus diesem Ausdruck, der festlegt, welches Feld die Mailadresse enthält.
Bild 1: Datenmodell der Anwendung
Zu erstellende Formulare
Wir wollen der Lösung einige Formulare hinzufügen, mit der Sie diese komfortabel steuern können. Dazu gehören die folgenden:
- Formular zum Verwalten der Konfigurationen
- Formular zum Verwalten der Mailings
- Formular zum Auswählen der Empfänger des Mailings
Formular zum Verwalten der Konfigurationen
Wir beginnen mit dem Formular frmConfigurations. Das Formular verwendet die Tabelle tblConfigurations als Datensatzquelle. Ziehen Sie alle Felder aus der Feldliste in das Formular und ordnen Sie diese so an wie in Bild 2. Dann fügen Sie noch einige weitere Steuerelemente hinzu:
Bild 2: Entwurf des Formulars frmConfigurations
- Kombinationsfeld cboSchnellauswahl (in den Formularkopf)
- Schaltfläche cmdOK (mit den anderen Schaltflächen in den Formularfuß)
- Schaltfläche cmdNeu
- Schaltfläche cmdLoeschen
Außerdem passen wir den Namen des Kontrollkästchens für das Feld Standard auf chkDefault an.
Schnellauswahl für die Konfiguration
Das Kombinationsfeld cboSchnellauswahl verwendet eine Abfrage auf Basis der Tabelle tblConfigurations als Datensatzherkunft. Dabei verwenden wir nur die ersten beiden Felder ConfigurationID und ConfigurationName, wobei wir diese nach dem Feld ConfigurationName sortieren wollen:
SELECT ConfigurationID, ConfigurationName FROM tblConfigurations ORDER BY ConfigurationName;
Damit das Kombinationsfeld nur die Bezeichnung der Konfiguration anzeigt, aber nicht den Inhalt des Autowertfeldes, stellen wir die Eigenschaft Spaltenanzahl auf 2 und Spaltenbreiten auf 0cm ein. Damit das Kombinationsfeld nach dem Wechsel zu einem anderen Datensatz im Formular den Namen der aktuellen Konfiguration anzeigt, fügen wir die folgende Prozedur hinzu, die durch das Ereignis Beim Anzeigen ausgelöst wird:
Private Sub Form_Current() Me!cboSchnellauswahl = Me!ConfigurationID End Sub
Gegebenenfalls ändert der Benutzer die Bezeichnung der Konfiguration. Damit sich dies direkt im Kombinationsfeld zur Schnellauswahl wiederspiegelt, aktualisieren wir dieses, wenn der Benutzer Daten im Formular aktualisiert und gespeichert hat:
Private Sub Form_AfterUpdate() Me!cboSchnellauswahl.Requery End Sub
Aktionen beim Laden des Formulars
Beim Öffnen des Formulars soll dieses den ersten Datensatz anzeigen, der im Feld Default den Wert True enthält – der also als Standardkonfiguration definiert ist. Damit dies geschieht, suchen wir direkt in der Prozedur, die durch das Ereignis Beim Laden ausgelöst wird, mit der FindFirst-Methode des Recordsets des Formulars nach diesem Datensatz. Unabhängig davon, ob dies einen Datensatz findet, soll das Kombinationsfeld cboSchnellauswahl auf den gleichen Datensatz eingestellt werden, den auch das Formular anzeigt. Dafür stellen wir den Wert von Me!cboSchnellauswahl anschließend auf Me!ConfigurationID ein:
Private Sub Form_Load() Me.Recordset.FindFirst "Default = True" Me!cboSchnellauswahl = Me!ConfigurationID End Sub
Auswahl einer anderen Konfiguration per Kombinationsfeld
Damit das Formular nach der Auswahl eines anderen Datensatzes über das Kombinationsfeld cboSchnellauswahl den gewünschte Datensatz anzeigt, fügen wir dem Kombinationsfeld eine Prozedur für das Ereignis Nach Aktualisierung hinzu. Diese sieht wie folgt aus und prüft zunächst, ob im Kombinationsfeld überhaupt ein Datensatz ausgewählt ist. Falls ja, sucht die Prozedur mit der FindFirst-Methode des Recordsets des Formulars nach dem betroffenen Datensatz und stellt diesen ein:
Private Sub cboSchnellauswahl_AfterUpdate() If Not Nz(Me!cboSchnellauswahl, 0) = 0 Then Me.Recordset.FindFirst "ConfigurationID = " & Me!cboSchnellauswahl End If End Sub
Einstellen der Standardkonfiguration
Das Feld Default der Tabelle tblConfigurations legt fest, welche Konfiguration standardmäßig verwendet werden soll. Das bedeutet schlicht und einfach, dass diese Konfiguration beim Anlegen neuer Mailings für das Fremdschlüsselfeld ConfigurationID der Tabelle tblMailings festgelegt wird. Außerdem soll diese Konfiguration beim Öffnen des Formulars frmConfigurations standardmäßig angezeigt werden, was wir weiter oben bereits realisiert haben.
Mit dem Kontrollkästchen wollen wir dem Benutzer die Möglichkeit bieten, eine andere Konfiguration als Standardkonfiguration festzulegen. Dabei nutzen wir als Erstes die Prozedur, die durch das Ereignis Vor Aktualisierung des Kontrollkästchens chkDefault ausgelöst wird. Diese prüft, ob der Benutzer soeben den Haken aus dem Kontrollkästchen entfernt hat. Falls ja, soll eine Meldung erscheinen, die den Benutzer darauf hinweist, dass er zum Ändern der Standardkonfiguration erst zu der gewünschten Standardkonfiguration wechseln muss und diese dann mit einem Klick auf das Kontrollkästchen mit dem Text Standard festlegen kann. Das Ereignis Vor Aktualisierung nutzen wir deshalb, weil wir hier mit dem Cancel-Parameter einstellen können, dass die Änderung rückgängig gemacht wird, wenn der Benutzer das Kontrollkästchen deaktiviert hat:
Private Sub chkDefault_BeforeUpdate(Cancel As Integer) If Not Me!chkDefault Then MsgBox "Um eine andere Konfiguration zur Standardkonfiguration zu machen, aktivieren Sie diese Option für die gewünschte Konfiguration." Cancel = True End If End Sub
Wenn der Benutzer hingegen das Kontrollkästchen chkDefault aktiviert hat, soll die aktuell angezeigte Konfiguration als Standardkonfiguration eingestellt werden. In diesem Fall feuert auch noch das Ereignis Nach Aktualisierung des Kontrollkästchens, was die folgende Prozedur auslöst:
Private Sub chkDefault_AfterUpdate() Dim db As DAO.Database If Me!chkDefault Then Set db = CurrentDb db.Execute "UPDATE tblConfigurations SET Default = 0 WHERE NOT ConfigurationID = " & Me!ConfigurationID, dbFailOnError End If End Sub
Diese Prozedur prüft, ob chkDefault den Wert True hat, also ob der Benutzer das Kontrollkästchen für diesen Datensatz aktiviert hat. Falls ja, führt die Prozedur mit der Execute-Methode des Database-Objekts eine Abfrage aus, die den Wert des Feldes Default für alle anderen Datensätze der Tabelle tblConfigurations auf 0 einstellt. Damit ist sichergestellt, dass das Feld Default nur für den aktuell angezeigten Datensatz den Wert True aufweist.
Löschen einer Konfiguration
Mit einem Klick auf die Schaltfläche cmdLoeschen kann der Benutzer die aktuelle Konfiguration löschen. Dazu löst die Schaltfläche die folgende Prozedur aus. Diese löscht den aktuellen Datensatz und versucht danach, den Datensatzzeiger auf den ersten Datensatz einzustellen, dessen Feld Default den Wert True aufweist:
Private Sub cmdLoeschen_Click() RunCommand acCmdDeleteRecord Me.Recordset.FindFirst "Default = True" End Sub
Anlegen einer neuen Konfiguration
Um eine neue Konfiguration anzulegen, muss der Benutzer nur auf die Schaltfläche cmdNeu klicken. Diese springt dann zu einem neuen, leeren Datensatz:
Private Sub cmdNeu_Click() DoCmd.GoToRecord Record:=acNewRec End Sub
Schließen des Formulars
Ein Klick auf die Schaltfläche cmdOK ruft die DoCmd.Close-Methode auf und schließt das aktuelle Formular:
Private Sub cmdOK_Click() DoCmd.Close acForm, Me.Name End Sub
Wenn Sie in die Formularansicht wechseln und einen Datensatz eingegeben haben, sieht dieses wie in Bild 3 aus.
Bild 3: Das Formular frmConfigurations in der Formularansicht
Kennwort verbergen
Damit das Textfeld Password das Kennwort nicht direkt anzeigt, sondern Sternchen als Platzhalter verwendet, stellen wir die Eigenschaft Eingabeformat auf Kennwort ein.
Formular zum Verwalten der Mailings
Das Formular frmMailings dient zum Verwalten der Mailings. Es verwendet die Tabelle tblMailings als Datensatzquelle. Aus dieser Tabelle haben wir alle Felder in den Detailbereich der Entwurfsansicht gezogen.
Für die in Bild 4 markierten Felder stellen wir die Eigenschaft Horizontaler Anker auf den Wert Beide ein. So können wir die Felder vergrößern, indem wir die Breite des Formulars vergrößern. Anschließend müssen Sie die Bezeichnungsfelder dieser Steuerelemente markieren und für diese die Eigenschaft Horizontaler Anker auf Links einstellen, da diese durch die vorherige Einstellung auf Rechts festgelegt wurde.
Bild 4: Das Formulars frmMailings in der Entwurfsansicht
Eines der Felder können wir außerdem in der Höhe anpassbar machen. Das ist für das Feld TextBody am sinnvollsten. Daher stellen wir seine Eigenschaft Vertikaler Anker auf Beide ein. Auch hier müssen wir die Eigenschaft Vertikaler Anker für das entsprechende Bezeichnungsfeld wieder auf Oben festlegen. Außerdem müssen wir die Eigenschaft Vertikaler Anker für alle Felder, die sich unterhalb des Textfeldes TextBody befinden, auf Unten einstellen, da diese sonst beim Vergrößern der Höhe des Formulars vom Textfeld TextBody überlappt werden.
Die Beschriftungen der Bezeichnungsfelder wurden bisher von den Feldnamen übernommen, diese passen wir jedoch auch noch an (noch nicht im Screenshot sichtbar).
Listenfeld für die Anhänge
Sie sehen die geänderten Bezeichnungen in Bild 5. Hier sehen Sie auch, wie Sie das Textfeld für die Anzeige der Daten des Feldes Attachments in ein Listenfeld umwandeln können – und zwar über das Kontextmenü. Anschließend ändern Sie noch den Namen des Listenfeldes in lstAttachments. Leider können wir die Bindung des Listenfeldes an das Feld Attachments danach nicht mehr nutzen, denn ein Listenfeld bindet man in der Regel an eine Datensatzherkunft wie eine Tabelle oder Abfrage. Deshalb bauen wir das Listenfeld ein wenig um. Als Erstes entfernen Sie den Inhalt der Eigenschaft Steuerelementinhalt. Dann stellen Sie die Eigenschaft Herkunftstyp auf Wertliste ein. Schließlich sorgen wir dafür, dass beim Wechseln des Datensatzes im Formular der Inhalt des Feldes Attachments als Wert des Listenfeldes eingestellt wird.
Bild 5: Ändern des Textfeldes für die Anhänge in ein Listenfeld
Dazu legen wir die Prozedur Form_Current mit der folgenden Anweisung an:
Private Sub Form_Current() Me!lstAttachments.RowSource = Me!Attachments End Sub
Damit zeigt das Listenfeld bei jedem Datensatzwechsel automatisch die enthaltenen Attachments an.
Attachments hinzufügen
Dem Listenfeld lstAttachments stellen wir eine Schaltfläche namens cmdHinzufuegen zur Seite, welche die Ereignisprozedur aus Listing 1 auslöst. Diese stellen wir in ähnlicher Form im Beitrag E-Mails mit Anlagen mit Outlook versenden (www.access-im-unternehmen.de/1357) vor. Wir mussten allerdings einige Anpassungen vornehmen, da unser Listenfeld wie oben beschrieben nicht die Daten einer eigenen Tabelle oder Abfrage anzeigt, sondern den Inhalt des Feldes Attachment als Wertliste. Deshalb arbeiten wir beim Hinzufügen nicht mit der AddItem-Methode des Listenfeldes, sondern fügen die im Dateiauswahl-Dialog selektierten Daten dem Text aus dem Feld Attachments der Tabelle tblMailings hinzu, sofern die jeweilige Datei dort noch nicht enthalten ist. Für die Nutzung des Dateiauswahl-Dialogs fügen Sie dem VBA-Projekt noch einen Verweis auf die Bibliothek Microsoft Office x.0 Object Library hinzu.
Private Sub cmdHinzufuegen_Click() Dim objFileDialog As FileDialog Dim l As Long, Dim m As Long Dim bolVorhanden As Boolean Dim strAttachments As String Dim varAttachment As Variant strAttachments = Me!lstAttachments.RowSource Set objFileDialog = FileDialog(msoFileDialogFilePicker) With objFileDialog .AllowMultiSelect = True .Title = "Anlagen auswählen" .Filters.Clear .Filters.Add "Alle Dateien", "*.*" .ButtonName = "Hinzufügen" If .Show = True Then For l = 1 To .SelectedItems.Count If Not Len(Me!lstAttachments.RowSource + .SelectedItems(l)) > 32750 Then bolVorhanden = False For Each varAttachment In Split(strAttachments, ";") If varAttachment = .SelectedItems(l) Then bolVorhanden = True Exit For End If Next varAttachment If Not bolVorhanden Then strAttachments = strAttachments & ";" & .SelectedItems(l) End If If Left(strAttachments, 1) = ";" Then strAttachments = Mid(strAttachments, 2) End If Else MsgBox "Es können keine weiteren Dateien hinzugefügt werden." Exit Sub End If Next l End If End With Me!lstAttachments.RowSource = strAttachments Me!Attachments = strAttachments End Sub
Listing 1: Prozedur zum Hinzufügen von Anlagen
Attachments leeren
Im Gegensatz zu dem Listenfeld für Anlagen aus dem soeben genannten Beitrag wollen wir hier nur eine Schaltfläche zum Leeren des Listenfeldes hinzufügen und nicht das Selektieren einzelner zu entfernender Einträge erlauben. Dazu hinterlegen wir für die Schaltfläche cmdLeeren die folgende Ereignisprozedur:
Private Sub cmdLeeren_Click() Me!Attachments = Null Me!lstAttachments. RowSource = "" End Sub
Diese leert sowohl das Feld Attachments der Datensatzquelle als auch das Listenfeld lstAttachments.
Konfiguration automatisch auswählen
Damit bei einem neuen Mailing direkt die als Standard markierte Konfiguration im Feld cboConfigurationID ausgewählt wird, legen wir die folgende Prozedur für das Ereignis Beim Anzeigen des Formulars fest:
Private Sub Form_Current() Dim lngConfigurationID As Long If Me.NewRecord Then lngConfigurationID = Nz(DLookup("ConfigurationID", "tblConfigurations", "Default = True"), 0) If Not lngConfigurationID = 0 Then Me!cboConfigurationID.DefaultValue = lngConfigurationID End If End If Me!lstAttachments.RowSource = Me!Attachments End Sub
Die Prozedur prüft zunächst, ob das Formular gerade einen neuen, leeren Datensatz anzeigt. Falls ja, ermittelt Sie den Primärschlüsselwert des Datensatzes der Tabelle tblConfigurations, dessen Feld Default den Wert True aufweist und schreibt diesen in die Variable lngConfigurationID. Ist dieser nicht 0, stellt die Prozedur den Wert des Kombinationsfeldes cboConfigurationID aus lngConfigurationID ein.
Aktuelle Konfiguration bearbeiten
Das Formular für die Verwaltung der Mailings soll neben einem Kombinationsfeld zur Auswahl der zu verwendenden Konfiguration auch noch die Möglichkeit bieten, die Konfiguration zu öffnen und zu bearbeiten beziehungsweise eine neue Konfiguration anzulegen. Deshalb fügen wir neben dem Kombinationsfeld cboConfigurationID noch eine Schaltfläche namens cmdKonfigurationBearbeiten hinzu.
Damit diese Schaltfläche beim Ändern der Größe des Formulars nicht hinter anderen Steuerelementen verschwindet, deren Größe ebenfalls angepasst wird, stellen Sie die Eigenschaft Horizontaler Anker auf Rechts und Vertikaler Anker auf Unten ein. Die Platzierung der Schaltfläche können Sie Bild 6 entnehmen.
Bild 6: Schaltfläche zum Bearbeiten der Konfiguration
Um die Konfiguration bearbeiten und eventuelle Änderungen direkt in das Kombinationsfeld übernehmen zu können, öffnen wir das Formular über die Schaltfläche mit dem Wert acDialog für den Parameter WindowMode. Außerdem übergeben wir mit dem Parameter OpenArgs den Primärschlüsselwert des aktuell ausgewählten Datensatzes der Tabelle tblConfigurations.
Durch das Öffnen des Formulars als modalen Dialog wird der aufrufende Code solange angehalten, bis der Benutzer das Formular schließt oder dieses unsichtbar geschaltet wird. Wir wollen dafür sorgen, dass das Formular dann unsichtbar wird, damit wir anschließend noch die ConfigurationID der aktuell im Formular selektierten Konfiguration ermitteln können. Danach wollen wir das Formular dann schließen. Dies alles erledigen wir mit der Prozedur aus Listing 2. Wichtig ist hier erst einmal, dass wir das Formular wie angegeben als modalen Dialog öffnen und mit dem aktuellen Wert für ConfigurationID als Öffnungsargument anzeigen – den Rest sehen wir uns gleich an.
Private Sub cmdKonfigurationBearbeiten_Click() Dim lngConfigurationID As Long DoCmd.OpenForm "frmConfigurations", OpenArgs:=Nz(Me!cboConfigurationID), WindowMode:=acDialog If IstFormularGeoeffnet("frmConfigurations") Then Me!cboConfigurationID.Requery lngConfigurationID = Nz(Forms!frmConfigurations!ConfigurationID, 0) If Not lngConfigurationID = 0 Then Me!cboConfigurationID = lngConfigurationID End If DoCmd.Close acForm, "frmConfiguration" End If End Sub
Listing 2: Prozedur zum Öffnen des Formulars frmConfigurations
Das Formular frmConfigurations soll beim Laden prüfen, ob ein Öffnungsargument übergeben wurde. Dazu prüfen wir den Wert der Eigenschaft OpenArgs, welche das Öffnungsargument gegebenenfalls enthält, auf eine leere Zeichenkette. Ist die Zeichenkette nicht leer, soll das Formular frmConfigurations beim Öffnen direkt auf den Datensatz mit der übergebenen ConfigurationID eingestellt werden. Anderenfalls verfahren wir so, wie wir es bereits mit der bisherigen Methode getan haben:
Private Sub Form_Load() If Not Nz(Me.OpenArgs, "") = "" Then Me.Recordset.FindFirst "ConfigurationID = " & Me.OpenArgs Else Me.Recordset.FindFirst "Default = True" End If Me!cboSchnellauswahl = Me!ConfigurationID End Sub
Damit das Formular frmConfigurations bei unserem Plan mitspielt, müssen wir noch dafür sorgen, dass es beim Klick auf OK nicht geschlossen, sondern nur ausgeblendet wird. Auch dafür wollen wir herausfinden, ob es vom Formular frmMailings aus geöffnet wurde. Das ist wieder der Fall, wenn das Öffnungsargument in der Eigenschaft OpenArgs nicht leer ist. Klickt der Benutzer in diesem Fall auf cmdOK, soll aber nicht nur das Formular mit der Einstellung Visible = False ausgeblendet werden. In diesem Fall wäre ein eventuell neu angelegter Konfigurationsdatensatz nämlich noch nicht gespeichert. Das holen wir durch das Einstellen der Dirty-Eigenschaft auf den Wert False nach, bevor wir das Formular ausblenden:
Private Sub cmdOK_Click() If Not Nz(Me.OpenArgs, "") = "" Then Me.Dirty = False Me.Visible = False Else DoCmd.Close acForm, Me.Name End If End Sub
Nachdem das Formular frmConfiguration auf diese Weise ausgeblendet wurde, prüft die aufrufende Prozedur zunächst mit der Hilfsfunktion IstFormularGeoeffnet, ob das Formular frmConfigurations noch geöffnet ist. Ist das der Fall, aktualisiert sie zuerst den Inhalt des Kombinationsfeldes cboConfigurationID, da ja gegebenenfalls ein neuer Datensatz zur Tabelle tblConfigurations hinzugekommen sein kann.
Dann liest sie den Wert des Feldes ConfigurationID des Formulars frmConfigurations ein und schreibt diesen in die Variable lngConfigurationID. Ist ConfigurationID gleich Null, erhält die Variable den Wert 0. Ist der Wert nicht 0, stellt die Prozedur das Kombinationsfeld auf den neuen Wert ein.
Außerdem folgt noch ein wichtiger Schritt, nämlich das Schließen des Formulars frmConfiguration. Geschieht dies nicht, bleibt das Formular unsichtbar und geöffnet. Würden Sie es dann nochmals wie oben beschrieben öffnen, würde beispielsweise das Öffnungsargument nicht funktionieren.
Schließen-Schaltfläche ausblenden
Es fehlt noch ein wichtiger Schritt, denn noch könnte der Benutzer das Formular frmConfigurations auch über die Schließen-Schaltfläche des Formulars schließen. Dann würden eventuelle Änderungen an der Konfiguration oder eine neu angelegte oder ausgewählte Konfiguration nicht erkannt werden, denn das Formular wird dann geschlossen und nicht nur unsichtbar gemacht. Der Code stellt dann mit der Funktion IstFormularGeoeffnet fest, dass das Formular nicht mehr geöffnet ist und die Anweisungen innerhalb der If…Then-Bedingung werden nicht ausgeführt. Somit erfolgt auch keine Auswertung der Daten im Formular frmConfigurations. Deshalb stellen wir die Eigenschaft Schließen Schaltfläche auf den Wert Nein ein. Die Schaltfläche wird dann ausgegraut dargestellt (siehe Bild 7).
Bild 7: Einstellen der Konfiguration über das Formular frmConfigurations
Tabelle zum Speichern der Empfänger eines Mailings
Nun kommen wir zu den Empfängern der Serienmail. Wir haben eine Tabelle namens tblKunden vorliegen, aus der wir die Empfänger selektieren wollen. Außerdem verwenden wir die Tabelle tblMailings zum Speichern der Daten der einzelnen Mailings. Allerdings haben wir noch keine Möglichkeit, um zu speichern, wer welches Mailing erhält. Dazu legen wir nun eine neue Tabelle namens tblVerteiler an. Diese enthält neben dem Primärschlüsselfeld VerteilerID lediglich zwei Fremdschlüsselfelder namens Mailing-ID und KundeID. Sie sehen schon: Wir legen hier eine Verknüpfungstabelle für das Herstellen einer Beziehung zwischen den beiden Tabellen tblMailings und tblKunden an. Diese Tabelle sieht in der Entwurfsansicht wie in Bild 8 aus.
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