{"id":55001372,"date":"2022-06-01T00:00:00","date_gmt":"2022-05-31T20:20:18","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1372"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Benutzeroberflaeche_fuer_CDOSerienmails","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Benutzeroberflaeche_fuer_CDOSerienmails\/","title":{"rendered":"Benutzeroberfl&auml;che f&uuml;r CDO-Serienmails"},"content":{"rendered":"<p><b>Im Beitrag &#8222;Serienmails versenden mit CDO&#8220; haben wir einige Prozeduren und Funktionen vorgestellt, mit denen Sie Serien-E-Mails &uuml;ber die CDO-Bibliothek von Windows versenden k&ouml;nnen. Das macht nat&uuml;rlich nur halb soviel Spa&szlig;, wenn nur die nackten Routinen vorliegen. Also zeigen wir im vorliegenden Beitrag auch noch, wie Sie eine praktische Benutzeroberfl&auml;che zum Verwalten der f&uuml;r den Versand einer Serienmail ben&ouml;tigten Daten programmieren.<\/b><\/p>\n<h2>Datenmodell der Anwendung<\/h2>\n<p>Die Anwendung zum Versenden von Serienmails enth&auml;lt einige Tabellen, die wie im Folgenden vorstellen.<\/p>\n<p>Im <b>Beziehungen<\/b>-Fenster aus Bild 1 sehen Sie die ben&ouml;tigten Tabellen. Die ersten beiden hei&szlig;en <b>tblKunden <\/b>sowie <b>tblAnreden<\/b>. Die Tabelle <b>tblConfigurations<\/b> enth&auml;lt die Daten f&uuml;r die Konfiguration des Mailservers und die Tabelle <b>tblMailings <\/b>die Daten zum Mailing selbst, also eine Bezeichnung, die Absenderadresse, den Betreff, den Inhalt und die Anlagen. Der Tabelle <b>tblMailings <\/b>haben wir ein Fremdschl&uuml;sselfeld namens <b>ConfigurationID <\/b>hinzugef&uuml;gt, mit dem wir die f&uuml;r das jeweilige Mailing verwendete Konfiguration speichern k&ouml;nnen. Au&szlig;erdem enth&auml;lt die Tabelle <b>tblMailings <\/b>zwei Felder, mit denen die Adressaten des Mailings definiert werden. <b>SQLSource <\/b>nimmt den SQL-Ausdruck auf, der die Empf&auml;ngeradressen liefert und <b>Mailfield <\/b>den Namen des Feldes aus diesem Ausdruck, der festlegt, welches Feld die Mailadresse enth&auml;lt.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_03\/pic_1372_001.png\" alt=\"Datenmodell der Anwendung\" width=\"599,559\" height=\"443,6104\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Datenmodell der Anwendung<\/span><\/b><\/p>\n<h2>Zu erstellende Formulare<\/h2>\n<p>Wir wollen der L&ouml;sung einige Formulare hinzuf&uuml;gen, mit der Sie diese komfortabel steuern k&ouml;nnen. Dazu geh&ouml;ren die folgenden:<\/p>\n<ul>\n<li>Formular zum Verwalten der Konfigurationen<\/li>\n<li>Formular zum Verwalten der Mailings<\/li>\n<li>Formular zum Ausw&auml;hlen der Empf&auml;nger des Mailings<\/li>\n<\/ul>\n<h2>Formular zum Verwalten der Konfigurationen<\/h2>\n<p>Wir beginnen mit dem Formular <b>frmConfigurations<\/b>. Das Formular verwendet die Tabelle <b>tblConfigurations <\/b>als Datensatzquelle. Ziehen Sie alle Felder aus der Feldliste in das Formular und ordnen Sie diese so an wie in Bild 2. Dann f&uuml;gen Sie noch einige weitere Steuerelemente hinzu:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_03\/pic_1372_002.png\" alt=\"Entwurf des Formulars frmConfigurations\" width=\"424,5589\" height=\"359,1768\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Entwurf des Formulars frmConfigurations<\/span><\/b><\/p>\n<ul>\n<li>Kombinationsfeld <b>cboSchnellauswahl<\/b> (in den Formularkopf)<\/li>\n<li>Schaltfl&auml;che <b>cmdOK<\/b> (mit den anderen Schaltfl&auml;chen in den Formularfu&szlig;)<\/li>\n<li>Schaltfl&auml;che <b>cmdNeu<\/b><\/li>\n<li>Schaltfl&auml;che <b>cmdLoeschen<\/b><\/li>\n<\/ul>\n<p>Au&szlig;erdem passen wir den Namen des Kontrollk&auml;stchens f&uuml;r das Feld <b>Standard <\/b>auf <b>chkDefault <\/b>an.<\/p>\n<h2>Schnellauswahl f&uuml;r die Konfiguration<\/h2>\n<p>Das Kombinationsfeld <b>cboSchnellauswahl <\/b>verwendet eine Abfrage auf Basis der Tabelle <b>tblConfigurations <\/b>als Datensatzherkunft. Dabei verwenden wir nur die ersten beiden Felder <b>ConfigurationID <\/b>und <b>ConfigurationName<\/b>, wobei wir diese nach dem Feld <b>ConfigurationName <\/b>sortieren wollen:<\/p>\n<pre>SELECT ConfigurationID, ConfigurationName FROM tblConfigurations ORDER BY ConfigurationName;<\/pre>\n<p>Damit das Kombinationsfeld nur die Bezeichnung der Konfiguration anzeigt, aber nicht den Inhalt des Autowertfeldes, stellen wir die Eigenschaft <b>Spaltenanzahl <\/b>auf <b>2 <\/b>und <b>Spaltenbreiten <\/b>auf <b>0cm <\/b>ein. Damit das Kombinationsfeld nach dem Wechsel zu einem anderen Datensatz im Formular den Namen der aktuellen Konfiguration anzeigt, f&uuml;gen wir die folgende Prozedur hinzu, die durch das Ereignis <b>Beim Anzeigen <\/b>ausgel&ouml;st wird:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Current()\r\n     Me!cboSchnellauswahl = Me!ConfigurationID\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Gegebenenfalls &auml;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:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_AfterUpdate()\r\n     Me!cboSchnellauswahl.Requery\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Aktionen beim Laden des Formulars<\/h2>\n<p>Beim &Ouml;ffnen des Formulars soll dieses den ersten Datensatz anzeigen, der im Feld <b>Default <\/b>den Wert <b>True <\/b>enth&auml;lt &#8211; der also als Standardkonfiguration definiert ist. Damit dies geschieht, suchen wir direkt in der Prozedur, die durch das Ereignis <b>Beim Laden <\/b>ausgel&ouml;st wird, mit der <b>FindFirst<\/b>-Methode des Recordsets des Formulars nach diesem Datensatz. Unabh&auml;ngig davon, ob dies einen Datensatz findet, soll das Kombinationsfeld <b>cboSchnellauswahl <\/b>auf den gleichen Datensatz eingestellt werden, den auch das Formular anzeigt. Daf&uuml;r stellen wir den Wert von <b>Me!cboSchnellauswahl <\/b>anschlie&szlig;end auf <b>Me!ConfigurationID <\/b>ein:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     Me.Recordset.FindFirst \"Default = True\"\r\n     Me!cboSchnellauswahl = Me!ConfigurationID\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Auswahl einer anderen Konfiguration per Kombinationsfeld<\/h2>\n<p>Damit das Formular nach der Auswahl eines anderen Datensatzes &uuml;ber das Kombinationsfeld <b>cboSchnellauswahl <\/b>den gew&uuml;nschte Datensatz anzeigt, f&uuml;gen wir dem Kombinationsfeld eine Prozedur f&uuml;r das Ereignis <b>Nach Aktualisierung <\/b>hinzu. Diese sieht wie folgt aus und pr&uuml;ft zun&auml;chst, ob im Kombinationsfeld &uuml;berhaupt ein Datensatz ausgew&auml;hlt ist. Falls ja, sucht die Prozedur mit der <b>FindFirst<\/b>-Methode des Recordsets des Formulars nach dem betroffenen Datensatz und stellt diesen ein:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cboSchnellauswahl_AfterUpdate()\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> Nz(Me!cboSchnellauswahl, 0) = 0<span style=\"color:blue;\"> Then<\/span>\r\n         Me.Recordset.FindFirst \"ConfigurationID = \"  & Me!cboSchnellauswahl\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Einstellen der Standardkonfiguration<\/h2>\n<p>Das Feld <b>Default <\/b>der Tabelle <b>tblConfigurations <\/b>legt fest, welche Konfiguration standardm&auml;&szlig;ig verwendet werden soll. Das bedeutet schlicht und einfach, dass diese Konfiguration beim Anlegen neuer Mailings f&uuml;r das Fremdschl&uuml;sselfeld <b>ConfigurationID <\/b>der Tabelle <b>tblMailings <\/b>festgelegt wird. Au&szlig;erdem soll diese Konfiguration beim &Ouml;ffnen des Formulars <b>frmConfigurations <\/b>standardm&auml;&szlig;ig angezeigt werden, was wir weiter oben bereits realisiert haben.<\/p>\n<p>Mit dem Kontrollk&auml;stchen wollen wir dem Benutzer die M&ouml;glichkeit bieten, eine andere Konfiguration als Standardkonfiguration festzulegen. Dabei nutzen wir als Erstes die Prozedur, die durch das Ereignis <b>Vor Aktualisierung <\/b>des Kontrollk&auml;stchens <b>chkDefault <\/b>ausgel&ouml;st wird. Diese pr&uuml;ft, ob der Benutzer soeben den Haken aus dem Kontrollk&auml;stchen entfernt hat. Falls ja, soll eine Meldung erscheinen, die den Benutzer darauf hinweist, dass er zum &Auml;ndern der Standardkonfiguration erst zu der gew&uuml;nschten Standardkonfiguration wechseln muss und diese dann mit einem Klick auf das Kontrollk&auml;stchen mit dem Text <b>Standard <\/b>festlegen kann. Das Ereignis <b>Vor Aktualisierung <\/b>nutzen wir deshalb, weil wir hier mit dem <b>Cancel<\/b>-Parameter einstellen k&ouml;nnen, dass die &Auml;nderung r&uuml;ckg&auml;ngig gemacht wird, wenn der Benutzer das Kontrollk&auml;stchen deaktiviert hat:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>chkDefault_BeforeUpdate(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> Me!chkDefault<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Um eine andere Konfiguration zur  Standardkonfiguration zu machen, aktivieren Sie  diese Option f&uuml;r die gew&uuml;nschte Konfiguration.\"\r\n         Cancel = <span style=\"color:blue;\">True<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Wenn der Benutzer hingegen das Kontrollk&auml;stchen <b>chkDefault <\/b>aktiviert hat, soll die aktuell angezeigte Konfiguration als Standardkonfiguration eingestellt werden. In diesem Fall feuert auch noch das Ereignis <b>Nach Aktualisierung <\/b>des Kontrollk&auml;stchens, was die folgende Prozedur ausl&ouml;st:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>chkDefault_AfterUpdate()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">If <\/span>Me!chkDefault<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n         db.Execute \"UPDATE tblConfigurations  SET Default = 0 WHERE NOT ConfigurationID = \"  & Me!ConfigurationID, dbFailOnError\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Diese Prozedur pr&uuml;ft, ob <b>chkDefault <\/b>den Wert <b>True <\/b>hat, also ob der Benutzer das Kontrollk&auml;stchen f&uuml;r diesen Datensatz aktiviert hat. Falls ja, f&uuml;hrt die Prozedur mit der <b>Execute<\/b>-Methode des <b>Database<\/b>-Objekts eine Abfrage aus, die den Wert des Feldes <b>Default <\/b>f&uuml;r alle anderen Datens&auml;tze der Tabelle <b>tblConfigurations <\/b>auf <b>0 <\/b>einstellt. Damit ist sichergestellt, dass das Feld <b>Default <\/b>nur f&uuml;r den aktuell angezeigten Datensatz den Wert <b>True <\/b>aufweist. <\/p>\n<h2>L&ouml;schen einer Konfiguration<\/h2>\n<p>Mit einem Klick auf die Schaltfl&auml;che <b>cmdLoeschen <\/b>kann der Benutzer die aktuelle Konfiguration l&ouml;schen. Dazu l&ouml;st die Schaltfl&auml;che die folgende Prozedur aus. Diese l&ouml;scht den aktuellen Datensatz und versucht danach, den Datensatzzeiger auf den ersten Datensatz einzustellen, dessen Feld <b>Default <\/b>den Wert <b>True <\/b>aufweist:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdLoeschen_Click()\r\n     RunCommand acCmdDeleteRecord\r\n     Me.Recordset.FindFirst \"Default = True\"\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Anlegen einer neuen Konfiguration<\/h2>\n<p>Um eine neue Konfiguration anzulegen, muss der Benutzer nur auf die Schaltfl&auml;che <b>cmdNeu <\/b>klicken. Diese springt dann zu einem neuen, leeren Datensatz:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdNeu_Click()\r\n     DoCmd.GoToRecord Record:=acNewRec\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Schlie&szlig;en des Formulars<\/h2>\n<p>Ein Klick auf die Schaltfl&auml;che <b>cmdOK <\/b>ruft die <b>DoCmd.Close<\/b>-Methode auf und schlie&szlig;t das aktuelle Formular:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdOK_Click()\r\n     DoCmd.Close acForm, Me.Name\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Wenn Sie in die Formularansicht wechseln und einen Datensatz eingegeben haben, sieht dieses wie in Bild 3 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_03\/pic_1372_004.png\" alt=\"Das Formular frmConfigurations in der Formularansicht\" width=\"424,5589\" height=\"301,361\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Das Formular frmConfigurations in der Formularansicht<\/span><\/b><\/p>\n<h2>Kennwort verbergen<\/h2>\n<p>Damit das Textfeld <b>Password <\/b>das Kennwort nicht direkt anzeigt, sondern Sternchen als Platzhalter verwendet, stellen wir die Eigenschaft <b>Eingabeformat <\/b>auf <b>Kennwort <\/b>ein.<\/p>\n<h2>Formular zum Verwalten der Mailings<\/h2>\n<p>Das Formular <b>frmMailings <\/b>dient zum Verwalten der Mailings. Es verwendet die Tabelle <b>tblMailings <\/b>als Datensatzquelle. Aus dieser Tabelle haben wir alle Felder in den Detailbereich der Entwurfsansicht gezogen.<\/p>\n<p>F&uuml;r die in Bild 4 markierten Felder stellen wir die Eigenschaft <b>Horizontaler Anker <\/b>auf den Wert <b>Beide <\/b>ein. So k&ouml;nnen wir die Felder vergr&ouml;&szlig;ern, indem wir die Breite des Formulars vergr&ouml;&szlig;ern. Anschlie&szlig;end m&uuml;ssen Sie die Bezeichnungsfelder dieser Steuerelemente markieren und f&uuml;r diese die Eigenschaft <b>Horizontaler Anker <\/b>auf <b>Links <\/b>einstellen, da diese durch die vorherige Einstellung auf <b>Rechts <\/b>festgelegt wurde.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_03\/pic_1372_003.png\" alt=\"Das Formulars frmMailings in der Entwurfsansicht\" width=\"574,559\" height=\"449,9238\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Das Formulars frmMailings in der Entwurfsansicht<\/span><\/b><\/p>\n<p>Eines der Felder k&ouml;nnen wir au&szlig;erdem in der H&ouml;he anpassbar machen. Das ist f&uuml;r das Feld <b>TextBody<\/b> am sinnvollsten. Daher stellen wir seine Eigenschaft <b>Vertikaler Anker <\/b>auf <b>Beide <\/b>ein. Auch hier m&uuml;ssen wir die Eigenschaft <b>Vertikaler Anker <\/b>f&uuml;r das entsprechende Bezeichnungsfeld wieder auf <b>Oben <\/b>festlegen. Au&szlig;erdem m&uuml;ssen wir die Eigenschaft <b>Vertikaler Anker <\/b>f&uuml;r alle Felder, die sich unterhalb des Textfeldes <b>TextBody <\/b>befinden, auf <b>Unten <\/b>einstellen, da diese sonst beim Vergr&ouml;&szlig;ern der H&ouml;he des Formulars vom Textfeld <b>TextBody <\/b>&uuml;berlappt werden.<\/p>\n<p>Die Beschriftungen der Bezeichnungsfelder wurden bisher von den Feldnamen &uuml;bernommen, diese passen wir jedoch auch noch an (noch nicht im Screenshot sichtbar).<\/p>\n<h2>Listenfeld f&uuml;r die Anh&auml;nge<\/h2>\n<p>Sie sehen die ge&auml;nderten Bezeichnungen in Bild 5. Hier sehen Sie auch, wie Sie das Textfeld f&uuml;r die Anzeige der Daten des Feldes <b>Attachments <\/b>in ein Listenfeld umwandeln k&ouml;nnen &#8211; und zwar &uuml;ber das Kontextmen&uuml;. Anschlie&szlig;end &auml;ndern Sie noch den Namen des Listenfeldes in <b>lstAttachments<\/b>. Leider k&ouml;nnen wir die Bindung des Listenfeldes an das Feld <b>Attachments<\/b> 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 <b>Steuerelementinhalt<\/b>. Dann stellen Sie die Eigenschaft <b>Herkunftstyp <\/b>auf <b>Wertliste <\/b>ein. Schlie&szlig;lich sorgen wir daf&uuml;r, dass beim Wechseln des Datensatzes im Formular der Inhalt des Feldes <b>Attachments <\/b>als Wert des Listenfeldes eingestellt wird.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_03\/pic_1372_005.png\" alt=\"&Auml;ndern des Textfeldes f&uuml;r die Anh&auml;nge in ein Listenfeld\" width=\"499,5589\" height=\"404,6706\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: &Auml;ndern des Textfeldes f&uuml;r die Anh&auml;nge in ein Listenfeld<\/span><\/b><\/p>\n<p>Dazu legen wir die Prozedur <b>Form_Current <\/b>mit der folgenden Anweisung an:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Current()\r\n     Me!lstAttachments.RowSource = Me!Attachments\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Damit zeigt das Listenfeld bei jedem Datensatzwechsel automatisch die enthaltenen Attachments an.<\/p>\n<h2>Attachments hinzuf&uuml;gen<\/h2>\n<p>Dem Listenfeld <b>lstAttachments <\/b>stellen wir eine Schaltfl&auml;che namens <b>cmdHinzufuegen <\/b>zur Seite, welche die Ereignisprozedur aus Listing 1 ausl&ouml;st. Diese stellen wir in &auml;hnlicher Form im Beitrag <b>E-Mails mit Anlagen mit Outlook versenden <\/b>(<b>www.access-im-unternehmen.de\/1357<\/b>) 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 <b>Attachment <\/b>als Wertliste. Deshalb arbeiten wir beim Hinzuf&uuml;gen nicht mit der <b>AddItem<\/b>-Methode des Listenfeldes, sondern f&uuml;gen die im Dateiauswahl-Dialog selektierten Daten dem Text aus dem Feld <b>Attachments <\/b>der Tabelle <b>tblMailings<\/b> hinzu, sofern die jeweilige Datei dort noch nicht enthalten ist. F&uuml;r die Nutzung des Dateiauswahl-Dialogs f&uuml;gen Sie dem VBA-Projekt noch einen Verweis auf die Bibliothek <b>Microsoft Office x.0 Object Library <\/b>hinzu.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdHinzufuegen_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>objFileDialog<span style=\"color:blue;\"> As <\/span>FileDialog\r\n     <span style=\"color:blue;\">Dim <\/span>l<span style=\"color:blue;\"> As Long<\/span>, Dim m<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>bolVorhanden<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strAttachments<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>varAttachment<span style=\"color:blue;\"> As Variant<\/span>\r\n     strAttachments = Me!lstAttachments.RowSource\r\n     <span style=\"color:blue;\">Set<\/span> objFileDialog = FileDialog(msoFileDialogFilePicker)\r\n     <span style=\"color:blue;\">With<\/span> objFileDialog\r\n         .AllowMultiSelect = <span style=\"color:blue;\">True<\/span>\r\n         .Title = \"Anlagen ausw&auml;hlen\"\r\n         .Filters.Clear\r\n         .Filters.Add \"Alle Dateien\", \"*.*\"\r\n         .ButtonName = \"Hinzuf&uuml;gen\"\r\n         <span style=\"color:blue;\">If <\/span>.Show = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n             For l = 1 To .SelectedItems.Count\r\n                 <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> <span style=\"color:blue;\">Len<\/span>(Me!lstAttachments.RowSource + .SelectedItems(l)) &gt; 32750<span style=\"color:blue;\"> Then<\/span>\r\n                     bolVorhanden = <span style=\"color:blue;\">False<\/span>\r\n                     For Each varAttachment In <span style=\"color:blue;\">Split<\/span>(strAttachments, \";\")\r\n                         <span style=\"color:blue;\">If <\/span>varAttachment = .SelectedItems(l)<span style=\"color:blue;\"> Then<\/span>\r\n                             bolVorhanden = <span style=\"color:blue;\">True<\/span>\r\n                             <span style=\"color:blue;\">Exit For<\/span>\r\n                         <span style=\"color:blue;\">End If<\/span>\r\n                     <span style=\"color:blue;\">Next<\/span> varAttachment\r\n                     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> bolVorhanden<span style=\"color:blue;\"> Then<\/span>\r\n                         strAttachments = strAttachments & \";\" & .SelectedItems(l)\r\n                     <span style=\"color:blue;\">End If<\/span>\r\n                     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Left<\/span>(strAttachments, 1) = \";\"<span style=\"color:blue;\"> Then<\/span>\r\n                         strAttachments = <span style=\"color:blue;\">Mid<\/span>(strAttachments, 2)\r\n                     <span style=\"color:blue;\">End If<\/span>\r\n                 <span style=\"color:blue;\">Else<\/span>\r\n                     <span style=\"color:blue;\">MsgBox<\/span> \"Es k&ouml;nnen keine weiteren Dateien hinzugef&uuml;gt werden.\"\r\n                     <span style=\"color:blue;\">Exit Sub<\/span>\r\n                 <span style=\"color:blue;\">End If<\/span>\r\n             <span style=\"color:blue;\">Next<\/span> l\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     Me!lstAttachments.RowSource = strAttachments\r\n     Me!Attachments = strAttachments\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><!--30percent--><\/p>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Prozedur zum Hinzuf&uuml;gen von Anlagen<\/span><\/b><\/p>\n<h2>Attachments leeren<\/h2>\n<p>Im Gegensatz zu dem Listenfeld f&uuml;r Anlagen aus dem soeben genannten Beitrag wollen wir hier nur eine Schaltfl&auml;che zum Leeren des Listenfeldes hinzuf&uuml;gen und nicht das Selektieren einzelner zu entfernender Eintr&auml;ge erlauben. Dazu hinterlegen wir f&uuml;r die Schaltfl&auml;che <b>cmdLeeren <\/b>die folgende Ereignisprozedur:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span> cmdLeeren_Click()\r\n     Me!Attachments = Null\r\n     Me!lstAttachments. RowSource = \"\"\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Diese leert sowohl das Feld <b>Attachments <\/b>der Datensatzquelle als auch das Listenfeld <b>lstAttachments<\/b>.<\/p>\n<h2>Konfiguration automatisch ausw&auml;hlen<\/h2>\n<p>Damit bei einem neuen Mailing direkt die als Standard markierte Konfiguration im Feld <b>cboConfigurationID <\/b>ausgew&auml;hlt wird, legen wir die folgende Prozedur f&uuml;r das Ereignis <b>Beim Anzeigen<\/b> des Formulars fest:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Current()\r\n     <span style=\"color:blue;\">Dim <\/span>lngConfigurationID<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">If <\/span>Me.NewRecord<span style=\"color:blue;\"> Then<\/span>\r\n         lngConfigurationID = Nz(DLookup(\"ConfigurationID\",  \"tblConfigurations\", \"Default = True\"), 0)\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> lngConfigurationID = 0<span style=\"color:blue;\"> Then<\/span>\r\n             Me!cboConfigurationID.DefaultValue =  lngConfigurationID\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     Me!lstAttachments.RowSource = Me!Attachments\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die Prozedur pr&uuml;ft zun&auml;chst, ob das Formular gerade einen neuen, leeren Datensatz anzeigt. Falls ja, ermittelt Sie den Prim&auml;rschl&uuml;sselwert des Datensatzes der Tabelle <b>tblConfigurations<\/b>, dessen Feld <b>Default <\/b>den Wert <b>True <\/b>aufweist und schreibt diesen in die Variable <b>lngConfigurationID<\/b>. Ist dieser nicht <b>0<\/b>, stellt die Prozedur den Wert des Kombinationsfeldes <b>cboConfigurationID <\/b>aus <b>lngConfigurationID <\/b>ein.<\/p>\n<h2>Aktuelle Konfiguration bearbeiten<\/h2>\n<p>Das Formular f&uuml;r die Verwaltung der Mailings soll neben einem Kombinationsfeld zur Auswahl der zu verwendenden Konfiguration auch noch die M&ouml;glichkeit bieten, die Konfiguration zu &ouml;ffnen und zu bearbeiten beziehungsweise eine neue Konfiguration anzulegen. Deshalb f&uuml;gen wir neben dem Kombinationsfeld <b>cboConfigurationID <\/b>noch eine Schaltfl&auml;che namens <b>cmdKonfigurationBearbeiten <\/b>hinzu.<\/p>\n<p>Damit diese Schaltfl&auml;che beim &Auml;ndern der Gr&ouml;&szlig;e des Formulars nicht hinter anderen Steuerelementen verschwindet, deren Gr&ouml;&szlig;e ebenfalls angepasst wird, stellen Sie die Eigenschaft <b>Horizontaler Anker <\/b>auf <b>Rechts <\/b>und <b>Vertikaler Anker <\/b>auf <b>Unten <\/b>ein. Die Platzierung der Schaltfl&auml;che k&ouml;nnen Sie Bild 6 entnehmen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_03\/pic_1372_006.png\" alt=\"Schaltfl&auml;che zum Bearbeiten der Konfiguration\" width=\"424,5589\" height=\"347,6132\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Schaltfl&auml;che zum Bearbeiten der Konfiguration<\/span><\/b><\/p>\n<p>Um die Konfiguration bearbeiten und eventuelle &Auml;nderungen direkt in das Kombinationsfeld &uuml;bernehmen zu k&ouml;nnen, &ouml;ffnen wir das Formular &uuml;ber die Schaltfl&auml;che mit dem Wert <b>acDialog <\/b>f&uuml;r den Parameter <b>WindowMode<\/b>. Au&szlig;erdem &uuml;bergeben wir mit dem Parameter <b>OpenArgs <\/b>den Prim&auml;rschl&uuml;sselwert des aktuell ausgew&auml;hlten Datensatzes der Tabelle <b>tblConfigurations<\/b>.<\/p>\n<p>Durch das &Ouml;ffnen des Formulars als modalen Dialog wird der aufrufende Code solange angehalten, bis der Benutzer das Formular schlie&szlig;t oder dieses unsichtbar geschaltet wird. Wir wollen daf&uuml;r sorgen, dass das Formular dann unsichtbar wird, damit wir anschlie&szlig;end noch die <b>ConfigurationID<\/b> der aktuell im Formular selektierten Konfiguration ermitteln k&ouml;nnen. Danach wollen wir das Formular dann schlie&szlig;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 &ouml;ffnen und mit dem aktuellen Wert f&uuml;r <b>ConfigurationID<\/b> als &Ouml;ffnungsargument anzeigen &#8211; den Rest sehen wir uns gleich an.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdKonfigurationBearbeiten_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>lngConfigurationID<span style=\"color:blue;\"> As Long<\/span>\r\n     DoCmd.OpenForm \"frmConfigurations\", OpenArgs:=Nz(Me!cboConfigurationID), WindowMode:=acDialog\r\n     <span style=\"color:blue;\">If <\/span>IstFormularGeoeffnet(\"frmConfigurations\")<span style=\"color:blue;\"> Then<\/span>\r\n         Me!cboConfigurationID.Requery\r\n         lngConfigurationID = Nz(Forms!frmConfigurations!ConfigurationID, 0)\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> lngConfigurationID = 0<span style=\"color:blue;\"> Then<\/span>\r\n             Me!cboConfigurationID = lngConfigurationID\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         DoCmd.Close acForm, \"frmConfiguration\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Prozedur zum &Ouml;ffnen des Formulars frmConfigurations<\/span><\/b><\/p>\n<p>Das Formular <b>frmConfigurations <\/b>soll beim Laden pr&uuml;fen, ob ein &Ouml;ffnungsargument &uuml;bergeben wurde. Dazu pr&uuml;fen wir den Wert der Eigenschaft <b>OpenArgs<\/b>, welche das &Ouml;ffnungsargument gegebenenfalls enth&auml;lt, auf eine leere Zeichenkette. Ist die Zeichenkette nicht leer, soll das Formular <b>frmConfigurations <\/b>beim &Ouml;ffnen direkt auf den Datensatz mit der &uuml;bergebenen <b>ConfigurationID <\/b>eingestellt werden. Anderenfalls verfahren wir so, wie wir es bereits mit der bisherigen Methode getan haben:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> Nz(Me.OpenArgs, \"\") = \"\"<span style=\"color:blue;\"> Then<\/span>\r\n         Me.Recordset.FindFirst \"ConfigurationID = \"  & Me.OpenArgs\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         Me.Recordset.FindFirst \"Default = True\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     Me!cboSchnellauswahl = Me!ConfigurationID\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Damit das Formular <b>frmConfigurations <\/b>bei unserem Plan mitspielt, m&uuml;ssen wir noch daf&uuml;r sorgen, dass es beim Klick auf <b>OK <\/b>nicht geschlossen, sondern nur ausgeblendet wird. Auch daf&uuml;r wollen wir herausfinden, ob es vom Formular <b>frmMailings <\/b>aus ge&ouml;ffnet wurde. Das ist wieder der Fall, wenn das &Ouml;ffnungsargument in der Eigenschaft <b>OpenArgs <\/b>nicht leer ist. Klickt der Benutzer in diesem Fall auf <b>cmdOK<\/b>, soll aber nicht nur das Formular mit der Einstellung <b>Visible = False <\/b>ausgeblendet werden. In diesem Fall w&auml;re ein eventuell neu angelegter Konfigurationsdatensatz n&auml;mlich noch nicht gespeichert. Das holen wir durch das Einstellen der <b>Dirty<\/b>-Eigenschaft auf den Wert False nach, bevor wir das Formular ausblenden:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdOK_Click()\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> Nz(Me.OpenArgs, \"\") = \"\"<span style=\"color:blue;\"> Then<\/span>\r\n         Me.Dirty = <span style=\"color:blue;\">False<\/span>\r\n         Me.Visible = <span style=\"color:blue;\">False<\/span>\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         DoCmd.Close acForm, Me.Name\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Nachdem das Formular <b>frmConfiguration <\/b>auf diese Weise ausgeblendet wurde, pr&uuml;ft die aufrufende Prozedur zun&auml;chst mit der Hilfsfunktion <b>IstFormularGeoeffnet<\/b>, ob das Formular <b>frmConfigurations <\/b>noch ge&ouml;ffnet ist. Ist das der Fall, aktualisiert sie zuerst den Inhalt des Kombinationsfeldes <b>cboConfigurationID<\/b>, da ja gegebenenfalls ein neuer Datensatz zur Tabelle <b>tblConfigurations <\/b>hinzugekommen sein kann.<\/p>\n<p>Dann liest sie den Wert des Feldes <b>ConfigurationID <\/b>des Formulars <b>frmConfigurations <\/b>ein und schreibt diesen in die Variable <b>lngConfigurationID<\/b>. Ist <b>ConfigurationID <\/b>gleich <b>Null<\/b>, erh&auml;lt die Variable den Wert <b>0<\/b>. Ist der Wert nicht <b>0<\/b>, stellt die Prozedur das Kombinationsfeld auf den neuen Wert ein.<\/p>\n<p>Au&szlig;erdem folgt noch ein wichtiger Schritt, n&auml;mlich das Schlie&szlig;en des Formulars <b>frmConfiguration<\/b>. Geschieht dies nicht, bleibt das Formular unsichtbar und ge&ouml;ffnet. W&uuml;rden Sie es dann nochmals wie oben beschrieben &ouml;ffnen, w&uuml;rde beispielsweise das &Ouml;ffnungsargument nicht funktionieren.<\/p>\n<h2>Schlie&szlig;en-Schaltfl&auml;che ausblenden<\/h2>\n<p>Es fehlt noch ein wichtiger Schritt, denn noch k&ouml;nnte der Benutzer das Formular <b>frmConfigurations <\/b>auch &uuml;ber die <b>Schlie&szlig;en<\/b>-Schaltfl&auml;che des Formulars schlie&szlig;en. Dann w&uuml;rden eventuelle &Auml;nderungen an der Konfiguration oder eine neu angelegte oder ausgew&auml;hlte Konfiguration nicht erkannt werden, denn das Formular wird dann geschlossen und nicht nur unsichtbar gemacht. Der Code stellt dann mit der Funktion <b>IstFormularGeoeffnet <\/b>fest, dass das Formular nicht mehr ge&ouml;ffnet ist und die Anweisungen innerhalb der <b>If&#8230;Then<\/b>-Bedingung werden nicht ausgef&uuml;hrt. Somit erfolgt auch keine Auswertung der Daten im Formular <b>frmConfigurations<\/b>. Deshalb stellen wir die Eigenschaft <b>Schlie&szlig;en Schaltfl&auml;che <\/b>auf den Wert <b>Nein <\/b>ein. Die Schaltfl&auml;che wird dann ausgegraut dargestellt (siehe Bild 7).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_03\/pic_1372_007.png\" alt=\"Einstellen der Konfiguration &uuml;ber das Formular frmConfigurations\" width=\"599,559\" height=\"536,9815\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: Einstellen der Konfiguration &uuml;ber das Formular frmConfigurations<\/span><\/b><\/p>\n<h2>Tabelle zum Speichern der Empf&auml;nger eines Mailings<\/h2>\n<p>Nun kommen wir zu den Empf&auml;ngern der Serienmail. Wir haben eine Tabelle namens <b>tblKunden<\/b> vorliegen, aus der wir die Empf&auml;nger selektieren wollen. Au&szlig;erdem verwenden wir die Tabelle <b>tblMailings <\/b>zum Speichern der Daten der einzelnen Mailings. Allerdings haben wir noch keine M&ouml;glichkeit, um zu speichern, wer welches Mailing erh&auml;lt. Dazu legen wir nun eine neue Tabelle namens <b>tblVerteiler <\/b>an. Diese enth&auml;lt neben dem Prim&auml;rschl&uuml;sselfeld <b>VerteilerID <\/b>lediglich zwei Fremdschl&uuml;sselfelder namens <b>Mailing-ID <\/b>und <b>KundeID<\/b>. Sie sehen schon: Wir legen hier eine Verkn&uuml;pfungstabelle f&uuml;r das Herstellen einer Beziehung zwischen den beiden Tabellen <b>tblMailings <\/b>und <b>tblKunden <\/b>an. Diese Tabelle sieht in der Entwurfsansicht wie in Bild 8 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_03\/pic_1372_008.png\" alt=\"Verkn&uuml;pfungstabelle zum Speichern der Versendungen einer E-Mail\" width=\"524,559\" height=\"354,7256\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: Verkn&uuml;pfungstabelle zum Speichern der Versendungen einer E-Mail<\/span><\/b><\/p>\n<p>Damit jedes Mailing nur einmal an jeden Kunden verschickt werden kann, definieren wir noch einen zusammengesetzten Index f&uuml;r die beiden Felder <b>KundeID<\/b> und <b>MailingID<\/b>. Das erledigen wir im Dialog <b>Indizes: tblVersendungen<\/b>. Hier f&uuml;gen wir einen neuen Eintrag namens <b>UniqueKey <\/b>hinzu und legen in der gleichen Zeile das Feld <b>KundeID <\/b>fest. In der Zeile darunter tragen wir keinen Wert f&uuml;r <b>Indexname <\/b>ein, sondern w&auml;hlen nur den Feldnamen <b>MailingID <\/b>aus. Dann kehren wir zur Zeile mit dem Indexnamen <b>UniqueKey <\/b>zur&uuml;ck und stellen f&uuml;r diese den Wert der Eigenschaft <b>Eindeutig <\/b>auf <b>Ja <\/b>ein (siehe Bild 9).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_03\/pic_1372_009.png\" alt=\"Zusammengesetzter, eindeutiger Index\" width=\"424,5589\" height=\"234,304\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 9: Zusammengesetzter, eindeutiger Index<\/span><\/b><\/p>\n<h2>Formular zum Selektieren der Empf&auml;ngeradressen<\/h2>\n<p>Die Empf&auml;ngeradressen wollen wir nun leicht einem Mailing zuweisen k&ouml;nnen. Dazu erstellen wir ein Formular, das jeweils ein Mailing anzeigt. Zus&auml;tzlich soll es zwei Listenfelder enthalten, von denen eines die dem aktuell angezeigten Mailing zugewiesenen Empf&auml;nger enth&auml;lt und das andere die &uuml;brigen Kunden.<\/p>\n<p>Der Benutzer soll die Kunden dann per Doppelklick oder nach vorheriger Selektierung per Schaltfl&auml;che zwischen den beiden Listenfeldern hin- und herschieben k&ouml;nnen. Au&szlig;erdem enth&auml;lt das Formular im unteren Bereich eine Schaltfl&auml;che zum Schlie&szlig;en des Dialogs (siehe Bild 10).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_03\/pic_1372_010.png\" alt=\"Formular zum Verwalten der Empf&auml;nger einer Versendung\" width=\"474,5589\" height=\"447,2952\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 10: Formular zum Verwalten der Empf&auml;nger einer Versendung<\/span><\/b><\/p>\n<p>Die wichtigsten Steuerelemente haben die folgenden Bezeichnungen:<\/p>\n<ul>\n<li><b>lstEmpfaenger<\/b>: Listenfeld der zugeordneten Kunden<\/li>\n<li><b>lstNichtEmpfaenger<\/b>: Listenfeld der nicht zugeordneten Kunden<\/li>\n<li><b>cmdAlleHinzufuegen<\/b>: Schaltfl&auml;che zum Hinzuf&uuml;gen aller Kunden zur Versendung<\/li>\n<li><b>cmdEinenHinzufuegen<\/b>: Schaltfl&auml;che zum Hinzuf&uuml;gen des aktuell im rechten Listenfeld markierten Kunden<\/li>\n<li><b>cmdEinenEntfernen<\/b>: Schaltfl&auml;che zum Entfernen des aktuell im linken Listenfeld markierten Kunden<\/li>\n<li><b>cmdAlleEntfernen<\/b>: Schaltfl&auml;che zum Entfernen aller Kunden von der Versendung<\/li>\n<\/ul>\n<p>Damit das Formular die Daten der Tabelle <b>tblMailings <\/b>anzeigt, stellen wir seine Eigenschaft <b>Datensatzquelle <\/b>auf diese Tabelle ein. Die beiden Felder <b>MailingID <\/b>und <b>Mailingname <\/b>ziehen wir im Formularentwurf aus der Feldliste an die gew&uuml;nschte Stelle im Formular.<\/p>\n<h2>Daten des Listenfeldes mit den ausgew&auml;hlten Empf&auml;ngern<\/h2>\n<p>F&uuml;r das linke Listenfeld <b>lstEmpfaenger <\/b>erstellen wir eine neue Abfrage als Datensatzherkunft. Diese soll alle Kunden anzeigen, die &uuml;ber einen Eintrag in der Tabelle <b>tblVersendungen <\/b>der aktuell im Formular angezeigten Versendung zugeordnet wurden. In dieser Abfrage ben&ouml;tigen wie also sowohl Daten der Tabelle <b>tblKunden <\/b>(zum Anzeigen) als auch aus der Tabelle <b>tblVersendungen <\/b>(zum Abgleichen mit der aktuell angezeigten Versendung). Die fertige Abfrage sehen Sie in Bild 11.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_03\/pic_1372_011.png\" alt=\"Abfrage zum Ermitteln der Empf&auml;nger der aktuellen Versendung\" width=\"649,559\" height=\"357,9037\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 11: Abfrage zum Ermitteln der Empf&auml;nger der aktuellen Versendung<\/span><\/b><\/p>\n<p>Nachdem Sie beide Tabellen in den Abfrageentwurf gezogen haben, f&uuml;gen Sie die drei Felder <b>KundeID<\/b>, <b>Empf&auml;nger <\/b>und <b>MailingID <\/b>hinzu. Empf&auml;nger ist ein berechnetes Feld, das die beiden Felder <b>Nachname <\/b>und <b>Vorname <\/b>getrennt durch ein Komma zusammenf&uuml;hrt, also beispielsweise mit dem Ergebnis <b>Minhorst, Andr&eacute;<\/b>. F&uuml;r das Feld <b>MailingID <\/b>legen wir ein Kriterium fest. Dieses soll daf&uuml;r sorgen, dass die Abfrage nur die Daten ermittelt, die dem aktuell im Formular angezeigten Mailing zugeordnet sind. Daher verwenden wir als Vergleichswert den Ausdruck <b>[Forms]![frm-Ver-sen-dun-gen]![MailingID]<\/b>. Dieser ermittelt den Wert des Feldes <b>VersendungID <\/b>des aktuell im Formular angezeigten Datensatzes der Tabelle <b>tblVersendungen<\/b>.<\/p>\n<p>Nun speichern Sie die Abfrage unter dem Namen <b>qry-Frm-Ver-sen-dun-gen-Lst-Emp-faen-ger<\/b> und weisen diesen Namen der Eigenschaft <b>Datensatzherkunft <\/b>des Listenfeldes <b>lst-Emp-faenger <\/b>zu. Wenn Sie nun in die Formularansicht des Formulars wechseln, zeigt das linke Listenfeld noch keine Daten an, da wir der Tabelle <b>tblVersendungen <\/b>noch keine Eintr&auml;ge hinzugef&uuml;gt haben.<\/p>\n<p>Damit es sp&auml;ter die gew&uuml;nschten Daten anzeigt, legen wir f&uuml;r die Eigenschaft <b>Spaltenanzahl <\/b>den Wert <b>2 <\/b>und f&uuml;r <b>Spaltenbreiten <\/b>den Wert <b>0cm <\/b>fest. Dadurch zeigt es nur den Inhalt des zweiten Feldes der Abfrage mit dem Kundennamen an und nicht den des Prim&auml;rschl&uuml;sselfeldes.<\/p>\n<h2>Daten des Listenfeldes mit den nicht gew&auml;hlten Empf&auml;ngern<\/h2>\n<p>Warum haben wir diese Datensatzherkunft als eigene Abfrage gespeichert und nicht direkt den SQL-Ausdruck f&uuml;r die Eigenschaft hinterlegt Weil wir diese noch f&uuml;r das Kriterium der Abfrage f&uuml;r das zweite Listenfeld namens <b>lstKeinEmpfaenger <\/b>ben&ouml;tigen.<\/p>\n<p>Die Abfrage f&uuml;r dieses Listenfeld legen wir ebenfalls als neue Abfrage an, die wir sp&auml;ter unter dem Namen <b>lstFrmVersendungenLstKeinEmpfaenger <\/b>speichern &#8211; die fertige Abfrage sehen Sie in Bild 12. Diese Abfrage soll alle Eintr&auml;ge der Tabelle <b>tblKunden <\/b>ermitteln, die nicht &uuml;ber einen Eintrag in der Tabelle <b>tblVersendungen <\/b>mit einem Datensatz der Tabelle <b>tblMailings <\/b>verkn&uuml;pft sind.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_03\/pic_1372_012.png\" alt=\"Abfrage zum Ermitteln der Kunden, welche die aktuelle Versendung nicht erhalten\" width=\"649,559\" height=\"301,8858\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 12: Abfrage zum Ermitteln der Kunden, welche die aktuelle Versendung nicht erhalten<\/span><\/b><\/p>\n<p>Diese finden wir heraus, indem wir alle Eintr&auml;ge der Tabelle <b>tblKunden <\/b>ermitteln, die nicht in der zuvor erstellten Abfrage <b>qryFrmVersendungenLstEmpfaenger <\/b>enthalten sind.<\/p>\n<p>Wir f&uuml;gen dem Abfrageraster die beiden Felder <b>KundeID <\/b>und <b>Kunde <\/b>hinzu, wobei <b>Kunde <\/b>wieder ein berechnetes Feld ist und den Ausdruck <b>[Nachname] &#038; &#8222;, &#8220; &#038; [Vorname] <\/b>enth&auml;lt. F&uuml;r das Feld <b>KundeID <\/b>legen wir &uuml;berdies ein Kriterium fest, das den folgenden Ausdruck als Vergleichswert enth&auml;lt:<\/p>\n<pre>Nicht In (SELECT KundeID \r\n   FROM qryFrmVersendungenLstEmpfaenger)<\/pre>\n<p>Hier ermitteln wir in Klammern den Wert des Feldes <b>KundeID <\/b>aller Eintr&auml;ge der Abfrage <b>qryFrmVersendungen-Lst-Empfaenger<\/b>, also eine Liste von Werten des Feldes <b>KundeID<\/b>. Durch das Einfassen der Abfrage in Klammern und das Voranstellen der Schl&uuml;sselw&ouml;rter <b>Nicht In <\/b>(englisch und unter SQL <b>NOT IN<\/b>) erreichen wir, dass der Vergleichswert aus allen Werten von <b>KundeID <\/b>besteht, die bereits zu den Empf&auml;ngern der aktuell angezeigten Versendung geh&ouml;ren. Nun weisen wir die Abfrage <b>qryFrmVersendungenLstKeinEmpfaenger <\/b>dem Listenfeld <b>lstKeinEmpfaenger <\/b>als <b>Datensatzherkunft <\/b>zu.<\/p>\n<p>Auch f&uuml;r dieses Listenfeld stellen wir die Eigenschaft <b>Spaltenanzahl <\/b>auf <b>2 <\/b>und <b>Spaltenbreiten <\/b>auf <b>0cm <\/b>ein, damit nur der Name der gew&auml;hlten Kunden erscheint und nicht die ID. Wechseln wir nun in die Formularansicht des Formulars <b>frmVersendungen<\/b>, finden wir immerhin schon einmal die nicht zugewiesenen Kunden im rechten Listenfeld vor (siehe Bild 13).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_03\/pic_1372_013.png\" alt=\"Anzeige der nicht zugeordneten Kunden\" width=\"424,5589\" height=\"371,9654\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 13: Anzeige der nicht zugeordneten Kunden<\/span><\/b><\/p>\n<h2>Eintr&auml;ge per Doppelklick zu den Empf&auml;ngern hinzuf&uuml;gen<\/h2>\n<p>Nun wollen wir daf&uuml;r sorgen, dass ein Doppelklick auf einen der Eintr&auml;ge des rechten Listenfeldes diesen in das linke Listenfeld verschiebt. Dazu f&uuml;gen wir dem Ereignis <b>Beim Doppelklicken <\/b>des Listenfeldes <b>lstKeinEmpfaenger <\/b>eine Ereignisprozedur hinzu, die wie folgt aussieht:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>lstKeinEmpfaenger_DblClick(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     EmpfaengerHinzufuegen\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Hier finden wir lediglich den Aufruf einer weiteren Prozedur vor. Der Grund ist, dass wir die Anweisungen dieser Prozedur noch von einer anderen Ereignisprozedur aus aufrufen wollen &#8211; n&auml;mlich von der, die durch das Ereignis <b>Beim Klicken <\/b>der Schaltfl&auml;che <b>cmdEinenHinzufuegen <\/b>ausgel&ouml;st wird.<\/p>\n<p>Die Prozedur <b>EmpfaengerHinzufuegen <\/b>pr&uuml;ft zuerst mit der <b>IsNull<\/b>-Funktion, ob &uuml;berhaupt ein Eintrag im Listenfeld <b>lstKeinEmpfaenger <\/b>zum Verschieben in das andere Listenfeld markiert ist. W&auml;re das nicht der Fall, enthielte das Listenfeld den Wert <b>Null <\/b>und die Prozedur w&uuml;rde an dieser Stelle nicht den Inhalt der <b>If&#8230;Then<\/b>-Bedingung ausf&uuml;hren.<\/p>\n<p>Innerhalb dieser Bedingung referenziert die Prozedur das aktuelle <b>Database<\/b>-Objekt und f&uuml;hrt dann eine SQL-Aktionsabfrage aus. Diese soll einen neuen Datensatz zur Tabelle <b>tblVersendungen <\/b>hinzuf&uuml;gen, der im Feld <b>MailingID <\/b>den Wert aus dem gleichnamigen Feld des Formulars enth&auml;lt und im Feld <b>KundeID <\/b>den Wert des aktuell im Listenfeld ausgew&auml;hlten Kunden. Wenn Sie das Mailing mit der ID <b>1 <\/b>und den Kunden mit der ID <b>2 <\/b>ausgew&auml;hlt haben, resultiert daraus beispielsweise die folgende Abfrage:<\/p>\n<pre>INSERT INTO tblVersendungen(MailingID, KundeID) VALUES(1, 2)<\/pre>\n<p>Die Prozedur aktualisiert anschlie&szlig;end noch die Inhalte beiden Listenfelder, damit das verschobene Element in der richtigen Liste erscheint. Au&szlig;erdem stellt es den Wert des Listenfeldes <b>lstKeinEmpfaenger <\/b>auf <b>Null <\/b>ein. Der Hintergrund dieser Aktion ist, dass das Listenfeld nach dem aktualisieren immer noch den gleichen Wert wie zuvor enth&auml;lt &#8211; also den des verschobenen Kunden. Das scheint ein Bug zu sein, da uns dieses Verhalten zuvor noch nie aufgefallen ist. Die vollst&auml;ndige Prozedur finden Sie in Listing 3.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>EmpfaengerHinzufuegen()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(Me!lstKeinEmpfaenger)<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n         db.Execute \"INSERT INTO tblVersendungen(MailingID, KundeID) VALUES(\" & Me!MailingID & \", \" _\r\n             & Me!lstKeinEmpfaenger & \")\", dbFailOnError\r\n         Me!lstEmpfaenger.Requery\r\n         Me!lstKeinEmpfaenger.Requery\r\n         Me!lstKeinEmpfaenger = Null\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Prozedur zum Hinzuf&uuml;gen eines Empf&auml;ngers zur Tabelle tblVersendungen<\/span><\/b><\/p>\n<p>Wechseln Sie nun in die Formularansicht, k&ouml;nnen Sie einen Kunden per Doppelklick auf seinen Eintrag im rechten Listenfeld zum linken Listenfeld hinzuf&uuml;gen (siehe Bild 14).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_03\/pic_1372_0014.png\" alt=\"Hinzuf&uuml;gen von Kunden per Doppelklick\" width=\"499,5589\" height=\"427,0422\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 14: Hinzuf&uuml;gen von Kunden per Doppelklick<\/span><\/b><\/p>\n<h2>Einen selektierten Kunden als Empf&auml;nger hinzuf&uuml;gen<\/h2>\n<p>Die Prozedur <b>EmpfaengerHinzufuegen <\/b>nutzen wir auch noch, wenn der Benutzer die Schaltfl&auml;che <b>cmdEinenHinzufuegen <\/b>bet&auml;tigt. In diesem Fall hinterlegen wir eine Ereignisprozedur f&uuml;r das <b>Beim Klicken<\/b>-Ereignis der Schaltfl&auml;che und f&uuml;llen diese wie folgt:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdEinenHinzufuegen_Click()\r\n     EmpfaengerHinzufuegen\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Dies ruft wieder die Prozedur <b>EmpfaengerHinzufuegen <\/b>auf, welche &#8211; wenn ein Kunde im rechten Listenfeld markiert ist &#8211; diesen in das linke Listenfeld verschiebt.<\/p>\n<h2>Eintr&auml;ge per Doppelklick von den Empf&auml;ngern entfernen<\/h2>\n<p>Als N&auml;chstes schauen wir uns an, wie Sie einen Kunden aus dem linken Listenfeld mit den Empf&auml;ngern wieder entfernen k&ouml;nnen. Dazu hinterlegen wir die folgende Ereignisprozedur f&uuml;r das Ereignis <b>Beim Doppelklicken <\/b>des Listenfeldes <b>lstEmpfaenger<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>lstEmpfaenger_DblClick(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     EmpfaengerEntfernen\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die dadurch aufgerufene Prozedur funktioniert &auml;hnlich wie die zum Hinzuf&uuml;gen eines Eintrags zur Empf&auml;ngerliste (siehe Listing 4). Allerdings nutzen wir hier eine <b>DELETE<\/b>-Aktionsabfrage, welche Datens&auml;tze aus der Tabelle <b>tblVersendungen <\/b>entfernt. Dabei verwendet sie wieder zwei Kriterien: Das Feld <b>MailingID <\/b>muss dem Wert des gleichnamigen Steuerelements aus dem Formular entsprechen und das Feld <b>KundeID <\/b>dem Wert des aktuell markierten Eintrags im Listenfeld <b>lstEmpfaenger<\/b>. Auch hier aktualisieren wir nach dem L&ouml;schen die beiden Listenfelder und stellen das Listenfeld, aus dem wir den Eintrag entfernt haben, auf <b>Null <\/b>ein.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>EmpfaengerEntfernen()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(Me!lstEmpfaenger)<span style=\"color:blue;\"> Then<\/span>\r\n         db.Execute \"DELETE FROM tblVersendungen WHERE MailingID = \" & Me!MailingID & \" AND KundeID = \" _\r\n             & Me!lstEmpfaenger, dbFailOnError\r\n         Me!lstEmpfaenger.Requery\r\n         Me!lstKeinEmpfaenger.Requery\r\n         Me!lstEmpfaenger = Null\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Prozedur zum Entfernen eines Empf&auml;ngers aus der Tabelle tblVersendungen<\/span><\/b><\/p>\n<h2>Einen selektierten Empf&auml;nger entfernen<\/h2>\n<p>Das Entfernen eines Empf&auml;ngers soll auch durch Markieren und anschlie&szlig;endes Bet&auml;tigen der Schaltfl&auml;che <b>cmdEinenEntfernen <\/b>gelingen. Dazu hinterlegen wir f&uuml;r die Ereignisprozedur <b>Beim Klicken <\/b>dieser Schaltfl&auml;che ebenfalls den Aufruf der Prozedur <b>EmpfaengerEntfernen<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdEinenEntfernen_Click()\r\n     EmpfaengerEntfernen\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Alle Kunden als Empf&auml;nger hinzuf&uuml;gen<\/h2>\n<p>Vielleicht m&ouml;chte der Benutzer einer Versendung alle Kunden hinzuf&uuml;gen. Dann bet&auml;tigt er die Schaltfl&auml;che mit den beiden Kleiner-Zeichen (<b><<<\/b>). Diese l&ouml;st die Ereignisprozedur aus Listing 5 aus.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdAlleHinzufuegen_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     db.Execute \"DELETE FROM tblVersendungen WHERE MailingID = \" & Me!MailingID, dbFailOnError\r\n     db.Execute \"INSERT INTO tblVersendungen(MailingID, KundeID) SELECT \" & Me!MailingID _\r\n         & \" AS MailingID, KundeID FROM tblKunden\", dbFailOnError\r\n     Me!lstEmpfaenger.Requery\r\n     Me!lstKeinEmpfaenger.Requery\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: Prozedur zum Hinzuf&uuml;gen aller Kunden zur Tabelle tblVersendungen<\/span><\/b><\/p>\n<p>Diese geht in zwei Schritten vor: Sie l&ouml;scht zun&auml;chst alle bisher vorhandenen Empf&auml;nger des Mailings und f&uuml;gt dann alle Eintr&auml;ge der Tabelle <b>tblKunden <\/b>zur Tabelle <b>tblVersendungen <\/b>hinzu &#8211; jeweils mit dem Wert des Feldes <b>MailingID <\/b>aus dem aktuellen Datensatz im Formular. Auch hier aktualisieren wir anschlie&szlig;end beide Listenfelder, damit die verschobenen Datens&auml;tze in den richtigen Listen erscheinen.<\/p>\n<h2>Alle Empf&auml;nger entfernen<\/h2>\n<p>Auf dem umgekehrten Weg m&ouml;chten wir die Schaltfl&auml;che mit den beiden Gr&ouml;&szlig;er-Zeichen (<b>>><\/b>) nutzen, um alle aktuell im linken Listenfeld enthaltenen Eintr&auml;ge zu entfernen. Dazu l&ouml;st die Schaltfl&auml;che die folgende Prozedur aus:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdAlleEntfernen_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     db.Execute \"DELETE FROM tblVersendungen  WHERE MailingID = \" & Me!MailingID, dbFailOnError\r\n     Me!lstEmpfaenger.Requery\r\n     Me!lstKeinEmpfaenger.Requery\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Diese entfernt alle Datens&auml;tze aus der Tabelle <b>tblVersendungen<\/b>, deren Feld <b>MailingID <\/b>der <b>MailingID <\/b>des aktuell im Formular ausgew&auml;hlten Datensatzes entspricht. Damit ist das Formular <b>frmVersendungen <\/b>fast vollst&auml;ndig: Wir brauchen nur noch die Schaltfl&auml;che <b>cmdOK <\/b>mit dieser Ereignisprozedur zu versehen:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdOK_Click()\r\n     DoCmd.Close acForm, Me.Name\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Schaltfl&auml;che zum &Ouml;ffnen der Empf&auml;ngerliste<\/h2>\n<p>Dem Formular <b>frmMailing <\/b>f&uuml;gen wir nun noch eine Schaltfl&auml;che namens <b>cmdEmpfaenger <\/b>hinzu. Diese soll mit der folgenden Prozedur das Formular <b>frmVersendungen <\/b>aufrufen:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdEmpfaenger_Click()\r\n     DoCmd.OpenForm \"frmVersendungen\",  WindowMode:=acDialog,  WhereCondition:=\"MailingID = \" & Me!MailingID\r\n     EmpfaengerzahlAktualisieren\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die Prozedur &ouml;ffnet das Formular f&uuml;r das aktuelle Mailing. Nach dem Schlie&szlig;en ruft es die folgende Prozedur auf, um die Anzahl der aktuellen Empf&auml;nger direkt auf der Schaltfl&auml;che anzuzeigen:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>EmpfaengerzahlAktualisieren()\r\n     <span style=\"color:blue;\">Dim <\/span>lngEmpfaenger<span style=\"color:blue;\"> As Long<\/span>\r\n     lngEmpfaenger = DCount(\"VersendungID\", \"tblVersendungen\",  \"MailingID = \" & Nz(Me!MailingID, 0))\r\n     Me!cmdEmpfaenger.Caption =  \"Empf&auml;nger (\" & lngEmpfaenger & \")\"\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Diese liest per <b>DCount <\/b>die Anzahl der Datens&auml;tze der Tabelle <b>tblVersendungen <\/b>f&uuml;r das aktuelle Mailing in die Variable <b>lngEmpfaenger <\/b>ein und f&uuml;gt diese anschlie&szlig;end in Klammern eingefasst der Beschriftung der Schaltfl&auml;che hinzu.<\/p>\n<p>Diese sieht anschlie&szlig;end wie in Bild 15 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_03\/pic_1372_015.png\" alt=\"Anzeige der Empf&auml;nger auf der Schaltfl&auml;che\" width=\"499,5589\" height=\"305,3437\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 15: Anzeige der Empf&auml;nger auf der Schaltfl&auml;che<\/span><\/b><\/p>\n<p>Damit die Schaltfl&auml;che immer die Anzahl der Empf&auml;nger des aktuellen Mailings anzeigt, f&uuml;gen wir den Aufruf der Prozedur <b>EmpfaengerzahlAktualisieren<\/b> auch noch der Prozedur hinzu, die durch das Ereignis <b>Beim Anzeigen <\/b>des Formulars ausgel&ouml;st wird &#8211; und damit beim Anzeigen eines jeden Datensatzes:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Current()\r\n     ...\r\n     EmpfaengerzahlAktualisieren\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Schaltfl&auml;che zum Versenden der E-Mails<\/h2>\n<p>Schlie&szlig;lich fehlt noch eine Schaltfl&auml;che zum Versenden der E-Mails, die wir wie folgt best&uuml;cken:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdSenden_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>strSQL<span style=\"color:blue;\"> As String<\/span>\r\n     strSQL = \"SELECT tblKunden.* FROM tblKunden  INNER JOIN tblVersendungen  ON tblKunden.KundeID = tblVersendungen.KundeID  WHERE tblVersendungen.MailingID = \" & Me!MailingID\r\n     Serienmail Me!cboConfigurationID, Me!MailingID,  strSQL, Me!Mailfield\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die Prozedur definiert dabei die SQL-Abfrage, welche die Datens&auml;tze der Tabelle <b>tblKunden <\/b>liefert, die &uuml;ber die Tabelle <b>tblVersendungen <\/b>dem aktuellen Mailing zugewiesen sind.<\/p>\n<p>Anschlie&szlig;end &uuml;bergibt sie diese &#8211; neben einigen weiteren Parametern &#8211; an die Prozedur <b>Serienmail<\/b>, die wir bereits im Beitrag <b>Serienmails versenden mit CDO <\/b>(<b>www.access-im-unternehmen.de\/1364<\/b>) vorgestellt haben.<\/p>\n<p>Diese &uuml;bernimmt das Senden der E-Mail an die ausgew&auml;hlten Kunden.<\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Dieser Beitrag zeigt, wie Sie das Datenmodell der L&ouml;sung aus dem Beitrag <b>Serienmails versenden mit CDO <\/b>(<b>www.access-im-unternehmen.de\/1364<\/b>) um eine Tabelle erweitern, mit der Sie Kunden zu einem Mailing hinzuf&uuml;gen oder von diesem entfernen k&ouml;nnen.<\/p>\n<p>Au&szlig;erdem stellen wir einige Formulare vor, mit denen Sie die beim Versenden einer E-Mail anfallenden Daten erfassen und den Versand der E-Mail selbst steuern k&ouml;nnen.<\/p>\n<p>Es gibt aber auch noch Erweiterungsm&ouml;glichkeiten: Sie k&ouml;nnten beispielsweise noch ein Feld zur Tabelle <b>tblVersendungen <\/b>hinzuf&uuml;gen, das erfasst, wann das Mailing an den Kunden versendet wurde. Dies k&ouml;nnen Sie dann nutzen, um eine &Uuml;bersicht der an einen Kunden versendeten Mailings anzuzeigen &#8211; inklusive Versanddatum.<\/p>\n<p>Eine weitere M&ouml;glichkeit w&auml;re, den Inhalt der E-Mail nicht nur als Plaintext, sondern auch noch in einem anderen Format anzugeben &#8211; hier bietet sich HTML an.<\/p>\n<h2>Downloads zu diesem Beitrag<\/h2>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>SerienmailsMitCDO.accdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/86074882-6062-4B83-A49F-753CCA18E80F\/aiu_1372.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Im Beitrag &#8222;Serienmails mit CDO&#8220; haben wir einige Prozeduren und Funktionen vorgestellt, mit denen Sie Serien-E-Mails &uuml;ber die CDO-Bibliothek von Windows versenden k&ouml;nnen. Das macht nat&uuml;rlich nur halbsoviel Spa&szlig;, wenn nur die nackten Routinen vorliegen. Also zeigen wir im vorliegenden Beitrag auch noch, wie Sie eine praktische Benutzeroberfl&auml;che zum Verwalten der f&uuml;r den Versand einer Serienmail ben&ouml;tigten Daten programmieren.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[662022,66032022,44000026],"tags":[],"class_list":["post-55001372","post","type-post","status-publish","format-standard","hentry","category-662022","category-66032022","category-Interaktiv"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.9 (Yoast SEO v27.4) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Benutzeroberfl&auml;che f&uuml;r CDO-Serienmails - Access im Unternehmen<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/access-im-unternehmen.de\/Benutzeroberflaeche_fuer_CDOSerienmails\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Benutzeroberfl&auml;che f&uuml;r CDO-Serienmails\" \/>\n<meta property=\"og:description\" content=\"Im Beitrag &quot;Serienmails mit CDO&quot; haben wir einige Prozeduren und Funktionen vorgestellt, mit denen Sie Serien-E-Mails &uuml;ber die CDO-Bibliothek von Windows versenden k&ouml;nnen. Das macht nat&uuml;rlich nur halbsoviel Spa&szlig;, wenn nur die nackten Routinen vorliegen. Also zeigen wir im vorliegenden Beitrag auch noch, wie Sie eine praktische Benutzeroberfl&auml;che zum Verwalten der f&uuml;r den Versand einer Serienmail ben&ouml;tigten Daten programmieren.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Benutzeroberflaeche_fuer_CDOSerienmails\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2022-05-31T20:20:18+00:00\" \/>\n<meta name=\"author\" content=\"Andr\u00e9 Minhorst\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andr\u00e9 Minhorst\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"29\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Benutzeroberflaeche_fuer_CDOSerienmails\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Benutzeroberflaeche_fuer_CDOSerienmails\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Benutzeroberfl&auml;che f&uuml;r CDO-Serienmails\",\"datePublished\":\"2022-05-31T20:20:18+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Benutzeroberflaeche_fuer_CDOSerienmails\\\/\"},\"wordCount\":5083,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"articleSection\":[\"2022\",\"3\\\/2022\",\"Interaktiv\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Benutzeroberflaeche_fuer_CDOSerienmails\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Benutzeroberflaeche_fuer_CDOSerienmails\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Benutzeroberflaeche_fuer_CDOSerienmails\\\/\",\"name\":\"Benutzeroberfl&auml;che f&uuml;r CDO-Serienmails - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"datePublished\":\"2022-05-31T20:20:18+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Benutzeroberflaeche_fuer_CDOSerienmails\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Benutzeroberflaeche_fuer_CDOSerienmails\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Benutzeroberflaeche_fuer_CDOSerienmails\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Benutzeroberfl&auml;che f&uuml;r CDO-Serienmails\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"name\":\"Access im Unternehmen\",\"description\":\"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access\",\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/access-im-unternehmen.de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\",\"name\":\"Andr\u00e9 Minhorst Verlag\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"contentUrl\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"width\":370,\"height\":111,\"caption\":\"Andr\u00e9 Minhorst Verlag\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\",\"name\":\"Andr\u00e9 Minhorst\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"caption\":\"Andr\u00e9 Minhorst\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Benutzeroberfl&auml;che f&uuml;r CDO-Serienmails - Access im Unternehmen","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/access-im-unternehmen.de\/Benutzeroberflaeche_fuer_CDOSerienmails\/","og_locale":"de_DE","og_type":"article","og_title":"Benutzeroberfl&auml;che f&uuml;r CDO-Serienmails","og_description":"Im Beitrag \"Serienmails mit CDO\" haben wir einige Prozeduren und Funktionen vorgestellt, mit denen Sie Serien-E-Mails &uuml;ber die CDO-Bibliothek von Windows versenden k&ouml;nnen. Das macht nat&uuml;rlich nur halbsoviel Spa&szlig;, wenn nur die nackten Routinen vorliegen. Also zeigen wir im vorliegenden Beitrag auch noch, wie Sie eine praktische Benutzeroberfl&auml;che zum Verwalten der f&uuml;r den Versand einer Serienmail ben&ouml;tigten Daten programmieren.","og_url":"https:\/\/access-im-unternehmen.de\/Benutzeroberflaeche_fuer_CDOSerienmails\/","og_site_name":"Access im Unternehmen","article_published_time":"2022-05-31T20:20:18+00:00","author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"29\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Benutzeroberflaeche_fuer_CDOSerienmails\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Benutzeroberflaeche_fuer_CDOSerienmails\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Benutzeroberfl&auml;che f&uuml;r CDO-Serienmails","datePublished":"2022-05-31T20:20:18+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Benutzeroberflaeche_fuer_CDOSerienmails\/"},"wordCount":5083,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"articleSection":["2022","3\/2022","Interaktiv"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Benutzeroberflaeche_fuer_CDOSerienmails\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Benutzeroberflaeche_fuer_CDOSerienmails\/","url":"https:\/\/access-im-unternehmen.de\/Benutzeroberflaeche_fuer_CDOSerienmails\/","name":"Benutzeroberfl&auml;che f&uuml;r CDO-Serienmails - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"datePublished":"2022-05-31T20:20:18+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Benutzeroberflaeche_fuer_CDOSerienmails\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Benutzeroberflaeche_fuer_CDOSerienmails\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Benutzeroberflaeche_fuer_CDOSerienmails\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Benutzeroberfl&auml;che f&uuml;r CDO-Serienmails"}]},{"@type":"WebSite","@id":"https:\/\/access-im-unternehmen.de\/#website","url":"https:\/\/access-im-unternehmen.de\/","name":"Access im Unternehmen","description":"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access","publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/access-im-unternehmen.de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/access-im-unternehmen.de\/#organization","name":"Andr\u00e9 Minhorst Verlag","url":"https:\/\/access-im-unternehmen.de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/","url":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","contentUrl":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","width":370,"height":111,"caption":"Andr\u00e9 Minhorst Verlag"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f","name":"Andr\u00e9 Minhorst","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","caption":"Andr\u00e9 Minhorst"}}]}},"_links":{"self":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001372","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/comments?post=55001372"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001372\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001372"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001372"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001372"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}