{"id":55001109,"date":"2017-12-01T00:00:00","date_gmt":"2020-05-13T21:27:44","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1109"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Undo_in_mehreren_Unterformularen","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Undo_in_mehreren_Unterformularen\/","title":{"rendered":"Undo in mehreren Unterformularen"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg09.met.vgwort.de\/na\/26a2197d643948308f065d0b16940c6b\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>In den Beitr&auml;gen &#8222;Undo in Haupt- und Unterformular&#8220; und &#8222;Undo in Haupt- und Unterformular mit Klasse&#8220; haben wir gezeigt, wie Sie die Undo-Funktion etwa durch einen Abbrechen-Schaltfl&auml;che nicht nur auf das Hauptformular, sondern auch auf die &auml;nderungen im Unterformular erstrecken. Nun hat ein Leser gefragt, ob man dies auch f&uuml;r mehrere Unterformulare erledigen kann. Klar kann man &#8211; die angepasste L&ouml;sung stellt der vorliegende Beitrag vor.<\/b><\/p>\n<p>Dabei setzen wir auf der L&ouml;sung aus dem Beitrag <b>Undo in Haupt- und Unterformular mit Klasse <\/b>(<b>www.access-im-unternehmen.de\/912<\/b>) auf. Diese enth&auml;lt im Vergleich zu dem vorherigen Beitrag eine Klasse mit der vollst&auml;ndigen Funktionalit&auml;t f&uuml;r das Implementieren des Undo in Haupt- und Unterformular, die man nur noch im Ereignis <b>Beim Laden <\/b>des Hauptformulars initialisieren und mit einigen Eigenschaften versehen muss. Der dazu anzulegende Code war sehr &uuml;berschaubar gemessen an der Funktion, die er hinzuf&uuml;gte.<\/p>\n<p>Die Einschr&auml;nkung dieser Klasse ist, dass man in dieser lediglich das Hauptformular und das Unterformular angeben konnte, auf die sich die Undo-Funktion auswirken sollte. Wenn man ein zweites Unterformular hinzuf&uuml;gen wollte, war das nicht m&ouml;glich. Also schauen wir uns im vorliegenden Beitrag an, wie wir dieses Feature nachr&uuml;sten.<\/p>\n<p>Aber wie viele Formular d&uuml;rfen es denn sein Reichen zwei aus &#8211; oder ben&ouml;tigt man drei oder vier Unterformulare Und macht man sich denn mit so vielen Unterformularen nicht die &uuml;bersicht im Formular kaputt Der Leser, der mit seiner Anfrage an uns herangetreten ist, lieferte gleich einen Screenshot seines Formulars mit, auf dem ersichtlich wurde, dass er ein Register-Steuerelement verwendet, um die verschiedenen Unterformulare im Formular unterzubringen. So gelingt das nat&uuml;rlich bei Erhaltung guter &uuml;bersicht.<\/p>\n<p>Damit war klar: Wir sollten uns nicht unbedingt einer Einschr&auml;nkung hingeben und eine L&ouml;sung programmieren, die beliebig viele Unterformulare ber&uuml;cksichtigt.<\/p>\n<h2>Erster Ansatz: Zwei Unterformulare<\/h2>\n<p>Dennoch haben wir, um uns ein wenig in den VBA-Code der anzupassenden L&ouml;sung einzuarbeiten, zun&auml;chst die bestehende L&ouml;sung auf den Einsatz mit zwei Unterformularen erweitert. Dazu haben wir lediglich im Code &uuml;berall dort, wo bisher das Unterformular erw&auml;hnt wurde, den Code f&uuml;r ein zweites Unterformular untergebracht. Das klappte recht leicht &#8211; die Hauptarbeit war Copy and Paste und ein wenig Achtsamkeit an den richtigen Stellen.<\/p>\n<h2>Zweiter Ansatz: So viele Unterformulare wie n&ouml;tig<\/h2>\n<p>Klar: Die Anzahl der Unterformular je Formulare ist begrenzt. Dennoch wollen Sie den Code der Klasse ja nicht jedes Mal erweitern, wenn ein neues Unterformular zum Formular hinzukommt. Wir wollen dann maximal ein paar Zeilen Code im <b>Beim Laden<\/b>-Ereignis hinzuf&uuml;gen, die das neu hinzugekommene Formular und dessen Datenherkunft sowie das Fremdschl&uuml;sselfeld der zugrunde liegenden Tabelle an die Klasse &uuml;bergeben.<\/p>\n<p>Daf&uuml;r ist dann zum Umwandeln des Codes ein wenig mehr Aufwand erforderlich. Aber es lohnt sich!<\/p>\n<h2>Aus eins mach zwei<\/h2>\n<p>Wenn Sie bereits andere L&ouml;sungen von uns gesehen haben, die Funktionen in Klassen auslagern und bei denen sich die Funktion auf ein oder mehrere Steuer-elemente bezieht, dann wissen Sie bereits: Es wird eine Collection geben, welche die Informationen f&uuml;r die in beliebiger Anzahl auftretenden Unterformulare aufnehmen soll. Dazu legen wir f&uuml;r jedes Unterformular eine neue Klasse an, die dann in der Collection gespeichert wird. Das hei&szlig;t weiter: Wir ben&ouml;tigen nicht mehr nur eine Hauptklasse, sondern noch eine weitere Klasse f&uuml;r die untergeordneten Steuer-elemente.<\/p>\n<p>Bild 1 zeigt schon einmal, wie das Endergebnis aussehen soll. Zu diesem Zweck haben wir drei Tabellen namens <b>tblHaupt<\/b>, <b>tblUnter1 <\/b>und <b>tblUnter2 <\/b>erstellt, von denen die letzten beiden per Fremdschl&uuml;ssel die erste Tabelle referenzieren.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1109_001.png\" alt=\"Formular mit zwei Unterformularen\" width=\"424,7115\" height=\"552,345\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Formular mit zwei Unterformularen<\/span><\/b><\/p>\n<p>Das Hauptformular ist an die Tabelle <b>tblHaupt <\/b>gebunden, die beiden Unterformulare an die Tabellen <b>tblUnter1 <\/b>und <b>tblUnter2<\/b>.<\/p>\n<h2>Undo-Funktion implementieren<\/h2>\n<p>Damit die Daten in diesen drei Formularen auch nach dem &auml;ndern von Daten im Hauptformular und in den beiden Unterformularen noch wieder hergestellt werden k&ouml;nnen, ist nur wenig Code notwendig, wenn die beiden Klassen <b>clsUndoMultiMain <\/b>und <b>clsUndoMultiSub <\/b>einmal zur Datenbank hinzugef&uuml;gt wurden.<\/p>\n<p>Diese sieht dann n&auml;mlich wie in Listing 1 aus. Hier deklarieren wir im allgemeinen Teil der Code behind-Klasse des Formulars die Variable <b>objUndoMultiMain <\/b>vom Typ <b>clsUndoMultiMain<\/b>. Diese erstellen wir dann in der <b>Beim Laden<\/b>-Ereignisprozedur, wo wir zuerst die Datenherkunft des Hauptformulars f&uuml;r <b>RecordsourceForm <\/b>angeben, dann das Prim&auml;rschl&uuml;sselfeld der Datenherkunft des Hauptformulars und schlie&szlig;lich einen Verweis auf das Hauptformular selbst. <\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>objUndomultiMain<span style=\"color:blue;\"> As <\/span>clsUndoMultiMain\r\n<span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     <span style=\"color:blue;\">Set<\/span> objUndomultiMain = <span style=\"color:blue;\">New<\/span> clsUndoMultiMain\r\n     <span style=\"color:blue;\">With<\/span> objUndomultiMain\r\n         .RecordsourceForm = \"tblHaupt\"\r\n         .PKForm = \"HauptID\"\r\n         <span style=\"color:blue;\">Set<\/span> .Form = Me\r\n         .AddSubform Me!sfmUnter1.Form, \"tblUnter1\", \"HauptID\"\r\n         .AddSubform Me!sfmUnter2.Form, \"tblUnter2\", \"HauptID\"\r\n         <span style=\"color:blue;\">Set<\/span> .OKButton = Me!cmdOK\r\n         <span style=\"color:blue;\">Set<\/span> .CancelButton = Me!cmdAbbrechen\r\n     End <span style=\"color:blue;\">With<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Code zum Einbinden der Funktion zum Undo in Haupt- und Unterformular<\/span><\/b><\/p>\n<p>Dann folgen die neuen Elemente: Die Methode <b>AddSubform <\/b>kann n&auml;mlich so oft aufgerufen werden, wie es n&ouml;tig ist, und damit ein oder mehrere Unterformulare in die <b>Undo<\/b>-Funktion einschlie&szlig;en.<\/p>\n<p>Dazu &uuml;bergeben Sie dieser Methode die folgenden drei Parameter:<\/p>\n<ul>\n<li><b>frm<\/b>: Verweis auf das Unterformular (nicht auf das Unterformular-Steuerelement, sondern auf das darin enthaltene Element &#8211; zu referenzieren mit der <b>Form<\/b>-Eigenschaft)<\/li>\n<li><b>strRecordsource<\/b>: Bezeichnung der Datenherkunft (Tabelle oder Abfrage)<\/li>\n<li><b>strFKSubform<\/b>: Fremdschl&uuml;sselfeld der Datenherkunft des Unterformulars, &uuml;ber welches die Beziehung zum Datensatz im Hauptformular hergestellt wird<\/li>\n<\/ul>\n<h2>Grundlegende Technik<\/h2>\n<p>Die grundlegende Idee ist es, die gesamte Bearbeitung des aktuellen Datensatzes des Hauptformulars und der mit diesem Datensatz verkn&uuml;pften Datens&auml;tze im Unterformular in eine Transaktion einzuarbeiten. Dazu gibt es einen wichtigen Unterschied zur herk&ouml;mmlichen Arbeit mit an Formulare gebundene Datenherk&uuml;nften: Diese d&uuml;rfen n&auml;mlich nicht wie sonst einfach etwa &uuml;ber die Eigenschaft <b>Recordsource <\/b>beziehungsweise <b>Datenherkunft <\/b>an die Formulare und Unterformulare gebunden werden. Stattdessen erstellen wir diese als zun&auml;chst unabh&auml;ngiges Recordset und weisen dieses dann der <b>Recordset<\/b>-Eigenschaft des Hauptformulars und der Unterformulare zu.<\/p>\n<p>Die Recordsets erstellen wir im Kontext eines <b>Database<\/b>-Objekts, das direkt dem <b>Workspace<\/b>-Objekt der aktuellen Sitzung untergeordnet ist. Auf diese Weise k&ouml;nnen wir in den Recordsets &auml;nderungen vornehmen und diese dann &uuml;ber die <b>Transaction<\/b>-Methoden <b>BeginTrans<\/b>, <b>CommitTrans <\/b>und <b>Rollback <\/b>des <b>Workspace<\/b>-Objekts nach den W&uuml;nschen des Benutzers entweder zusammen speichern oder verwerfen.<\/p>\n<h2>Zusammenhang zwischen den beiden Klassen<\/h2>\n<p>Die Hauptklasse <b>clsUndoMultiMain <\/b>nimmt die wichtigsten Elemente wie das <b>Database<\/b>&#8211; und das <b>Workspace<\/b>-Objekt auf und enth&auml;lt die Ereignisprozeduren, die f&uuml;r das Hauptformular angelegt werden. Die Klasse <b>clsUndoMultiSub<\/b>, die f&uuml;r jedes Unterformular einmal als Objekt angelegt wird, erh&auml;lt den Verweis auf das jeweilige Unterformular und implementiert die Ereignisprozeduren f&uuml;r das Unterformular.<\/p>\n<p>Wichtig: Hierbei ist zu beachten, dass das Unterformular ein VBA-Klassenmodul enth&auml;lt! Sie k&ouml;nnen das am einfachsten erreichen, indem Sie die Eigenschaft <b>Enth&auml;lt Modul <\/b>auf <b>Ja <\/b>einstellen. Anderenfalls werden die Ereignisprozeduren, die in der Klasse <b>clsUndoMultiSub <\/b>angelegt sind, nicht f&uuml;r das betroffene Unterformular ausgel&ouml;st. Beim Hauptformular sollte dies standardm&auml;&szlig;ig der Fall sein, da wir dort ja auch die Ereignisprozedur <b>Form_Load <\/b>unterbringen, in der wie die <b>Undo<\/b>-Klassen initialisieren und einstellen.<\/p>\n<p>Die ersten beiden Einstellungen, die der Benutzer vornehmen muss, sind der Name des Prim&auml;rschl&uuml;ssels der Datenherkunft des Formulars sowie der Name der Datenherkunft. Diese landen in den beiden folgenden Variablen:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>m_PKForm<span style=\"color:blue;\"> As String<\/span>\r\n<span style=\"color:blue;\">Private <\/span>m_RecordsourceForm<span style=\"color:blue;\"> As String<\/span><\/pre>\n<p>Damit diese Variablen &uuml;ber die Eigenschaft <b>PKForm <\/b>gef&uuml;llt und ausgelesen werden kann, legen wir die beiden folgenden <b>Property Let<\/b>\/<b>Get<\/b>-Prozeduren an:<\/p>\n<pre><span style=\"color:blue;\">Public Property Let <\/span>PKForm(str<span style=\"color:blue;\"> As String<\/span>)\r\n     m_PKForm = str\r\n<span style=\"color:blue;\">End Property<\/span>\r\n<span style=\"color:blue;\">Public Property Get <\/span>PKForm()<span style=\"color:blue;\"> As String<\/span>\r\n     PKForm = m_PKForm\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p>F&uuml;r <b>m_RecordsourceForm <\/b>ben&ouml;tigen wir nur eine &ouml;ffentliche Eigenschaft zum Zuweisen, ausgelesen wird diese Eigenschaft von au&szlig;en nicht:<\/p>\n<pre><span style=\"color:blue;\">Public Property Let <\/span>RecordsourceForm(str<span style=\"color:blue;\"> As String<\/span>)\r\n     m_RecordsourceForm = str\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p>Die Klasse <b>clsUndoMultiMain<\/b> enth&auml;lt au&szlig;erdem die folgenden zwei Elemente, f&uuml;r welche Ereignisprozeduren implementiert werden:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>WithEvents m_OKButton<span style=\"color:blue;\"> As <\/span>CommandButton\r\n<span style=\"color:blue;\">Private <\/span>WithEvents m_CancelButton<span style=\"color:blue;\"> As <\/span>CommandButton<\/pre>\n<p>&uuml;ber die folgenden beiden <b>Property Set<\/b>-Methoden k&ouml;nnen die Schaltfl&auml;chen des Formulars den Variablen zugewiesen werden.<\/p>\n<p>Dabei stellen wir auch gleich ein, dass das Ereignis <b>Beim Klicken <\/b>in dieser Klasse implementiert werden soll:<\/p>\n<pre><span style=\"color:blue;\">Public Property <span style=\"color:blue;\">Set<\/span> <\/span>OKButton(cmd<span style=\"color:blue;\"> As <\/span>CommandButton)\r\n     <span style=\"color:blue;\">Set<\/span> m_OKButton = cmd\r\n     <span style=\"color:blue;\">With<\/span> m_OKButton\r\n         .OnClick = \"[Event Procedure]\"\r\n     End <span style=\"color:blue;\">With<\/span>\r\n<span style=\"color:blue;\">End Property<\/span>\r\n<span style=\"color:blue;\">Public Property <span style=\"color:blue;\">Set<\/span> <\/span>CancelButton(cmd<span style=\"color:blue;\"> As <\/span>CommandButton)\r\n     <span style=\"color:blue;\">Set<\/span> m_CancelButton = cmd\r\n     <span style=\"color:blue;\">With<\/span> m_CancelButton\r\n         .OnClick = \"[Event Procedure]\"\r\n     End <span style=\"color:blue;\">With<\/span>\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<h2>Angabe des Hauptformulars<\/h2>\n<p>Der Verweis auf das Hauptformular soll in der Variablen <b>m_Form <\/b>gespeichert werden, f&uuml;r das wir ebenfalls Ereignisprozeduren implementieren wollen k&ouml;nnen:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>WithEvents m_Form<span style=\"color:blue;\"> As <\/span>Form<\/pre>\n<p>Die beiden folgenden Variablen nehmen die Verweise auf das <b>Database<\/b>&#8211; und das <b>Workspace<\/b>-Objekt auf:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>m_db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n<span style=\"color:blue;\">Private <\/span>m_wrk<span style=\"color:blue;\"> As <\/span>DAO.Workspace<\/pre>\n<p>Die Variable <b>m_Form <\/b>wird &uuml;ber die folgende <b>Property Set<\/b>-Methode gef&uuml;llt, die auch gleich einstellt, f&uuml;r welche Ereignisse im aktuellen Modul Ereignisprozeduren hinterlegt werden sollen. Au&szlig;erdem werden hier die beiden Variablen <b>m_db <\/b>und <b>m_wrk <\/b>gef&uuml;llt.<\/p>\n<p>Schlie&szlig;lich f&uuml;llt sie noch das Recordset des Hauptformulars mit der in <b>m_RecordsourceForm <\/b>angegebenen Datenherkunft (zuerst als Recordset <b>rst <\/b>als Element des aktuelle <b>Database<\/b>-Objekts und dann durch Zuweisen dieser Variablen an die <b>Recordset<\/b>-Eigenschaft des Formulars):<\/p>\n<pre><span style=\"color:blue;\">Public Property <span style=\"color:blue;\">Set<\/span> <\/span>Form(frm<span style=\"color:blue;\"> As <\/span>Form)\r\n    <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> m_Form = frm\r\n     <span style=\"color:blue;\">With<\/span> m_Form\r\n         .AfterUpdate = \"[Event Procedure]\"\r\n         .BeforeDelConfirm = \"[Event Procedure]\"\r\n         .OnCurrent = \"[Event Procedure]\"\r\n         .OnDelete = \"[Event Procedure]\"\r\n         .OnDirty = \"[Event Procedure]\"\r\n         .OnError = \"[Event Procedure]\"\r\n         .OnOpen = \"[Event Procedure]\"\r\n         .OnUnload = \"[Event Procedure]\"\r\n         .OnUndo = \"[Event Procedure]\"\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> m_db = DBEngine(0)(0)\r\n     <span style=\"color:blue;\">Set<\/span> m_wrk = DBEngine.Workspaces(0)\r\n     <span style=\"color:blue;\">Set<\/span> rst = m_db.OpenRecordset(m_RecordsourceForm, _\r\n          dbOpenDynaset)\r\n     <span style=\"color:blue;\">Set<\/span> m_Form.Recordset = rst\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p>Wir m&uuml;ssen auch von der Klasse <b>clsUndoMultiSub <\/b>auf das in <b>clsUndoMultiMain <\/b>enthaltene Formular zugreifen k&ouml;nnen, also legen wir auch eine <b>Property Get<\/b>-Prozedur an:<\/p>\n<pre><span style=\"color:blue;\">Public Property Get <\/span>Form()<span style=\"color:blue;\"> As <\/span>Form\r\n     <span style=\"color:blue;\">Set<\/span> Form = m_Form\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p>Auch die Variable <b>m_wrk <\/b>mit dem Workspace-Objekt wollen wir von den Unterklassen aus nutzen. Dazu legen wir die folgende <b>Property Get<\/b>-Methode an:<\/p>\n<pre><span style=\"color:blue;\">Public Property Get <\/span>wrk()<span style=\"color:blue;\"> As <\/span>DAO.Workspace\r\n     <span style=\"color:blue;\">Set<\/span> wrk = m_wrk\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p>Es gibt noch drei Eigenschaften, mit denen wir den aktuellen Zustand der Daten im Formular speichern m&uuml;ssen. Diese sollen in der Hauptklasse <b>clsUndoMultiMain <\/b>gespeichert werden, aber auch von den Unterklassen zugreifbar sein. Daher legen wir auch diese als private Variablen an:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>m_DirtyForm<span style=\"color:blue;\"> As Boolean<\/span>\r\n<span style=\"color:blue;\">Private <\/span>m_SavedForm<span style=\"color:blue;\"> As Boolean<\/span>\r\n<span style=\"color:blue;\">Private <\/span>m_DeletedForm<span style=\"color:blue;\"> As Boolean<\/span><\/pre>\n<p><b>m_DirtyForm <\/b>m&uuml;ssen wir lesen und schreiben k&ouml;nnen:<\/p>\n<pre><span style=\"color:blue;\">Public Property Get <\/span>DirtyForm()<span style=\"color:blue;\"> As Boolean<\/span>\r\n     DirtyForm = m_DirtyForm\r\n<span style=\"color:blue;\">End Property<\/span>\r\n<span style=\"color:blue;\">Public Property Let <\/span>DirtyForm(bol<span style=\"color:blue;\"> As Boolean<\/span>)\r\n     m_DirtyForm = bol\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p>Gleiches gilt f&uuml;r <b>m_SavedForm<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Public Property Get <\/span>SavedForm()<span style=\"color:blue;\"> As Boolean<\/span>\r\n     SavedForm = m_SavedForm\r\n<span style=\"color:blue;\">End Property<\/span>\r\n<span style=\"color:blue;\">Public Property Let <\/span>SavedForm(bol<span style=\"color:blue;\"> As Boolean<\/span>)\r\n     m_SavedForm = bol\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p><!--30percent--><\/p>\n<p><b>m_DeletedForm <\/b>verwenden wir nur innerhalb der Klasse <b>clsUndoMultiMain<\/b>, also brauchen wir keine <b>Property<\/b>-Eigenschaften. Schlie&szlig;lich fehlt noch eine <b>Collection<\/b>-Variable, in der wir die durch die weiter unten beschriebene <b>AddSubform<\/b>-Methode hinzugef&uuml;gten Instanzen der Klasse <b>clsUndoMultiSub <\/b>speichern:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>col<span style=\"color:blue;\"> As <\/span>Collection<\/pre>\n<p>Diese instanzieren wir in dem folgenden Ereignis, das gleich beim Erstellen der Klasse <b>clsUndoMultiMain <\/b>ausgel&ouml;st wird:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Class_Initialize()\r\n     <span style=\"color:blue;\">Set<\/span> col = <span style=\"color:blue;\">New<\/span> Collection\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Unterklassen f&uuml;r Unterformular hinzuf&uuml;gen<\/h2>\n<p>Damit kommen wir auch gleich zur Methode <b>AddSubform<\/b>. Diese sieht wie folgt aus und erwartet die weiter oben beschriebenen Parameter:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>AddSubform(frm<span style=\"color:blue;\"> As <\/span>Form, _\r\n         strRecordsourceSubform<span style=\"color:blue;\"> As String<\/span>, _\r\n         strFKSubform<span style=\"color:blue;\"> As String<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>objUndoMultiSub<span style=\"color:blue;\"> As <\/span>clsUndoMultiSub\r\n     <span style=\"color:blue;\">Set<\/span> objUndoMultiSub = <span style=\"color:blue;\">New<\/span> clsUndoMultiSub\r\n     <span style=\"color:blue;\">With<\/span> objUndoMultiSub\r\n         <span style=\"color:blue;\">Set<\/span> .Main = Me\r\n         <span style=\"color:blue;\">Set<\/span> .Subform = frm\r\n         .RecordsourceSubform = strRecordsourceSubform\r\n         .FKSubform = strFKSubform\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     col.Add objUndoMultiSub\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Sie erstellt mit jedem Aufruf ein neues Objekt des Typs <b>clsUndoMultiSub <\/b>und speichert dieses in <b>objUndoMultiSub<\/b>. Dann stellt sie die mit den Parametern &uuml;bergebenen Werte ein (die Innereien dieser Klasse schauen wir uns ebenfalls weiter unten an). Die in dieser Methode deklarierte Objektvariable <b>objUndoMultiSub <\/b>w&uuml;rde mit Beenden der Methode ihre G&uuml;ltigkeit verlieren, also f&uuml;gen wir diese mit der <b>Add<\/b>-Methode zur die Auflistung <b>col <\/b>hinzu.<\/p>\n<h2>Daten im Unterformular anzeigen<\/h2>\n<p>Damit die Unterformulare anzeigen, muss das Ereignis Beim Anzeigen des Hauptformulars ausgel&ouml;st werden. Warum Weil immer, wenn der Benutzer den Datensatz im Hauptformular wechselt, auch die Datens&auml;tze in den Unterformularen aktualisiert werden m&uuml;ssen. Sie erinnern sich Wir k&ouml;nnen nicht mit einer herk&ouml;mmlichen Bindung zwischen Haupt- und Unterformular arbeiten, weil wir die Daten der Formulare jeweils erst in Recordsets auf Basis des aktuellen Workspaces schreiben und dann an die Eigenschaft <b>Recordset <\/b>der Formulare &uuml;bergeben m&uuml;ssen. Anders k&ouml;nnen wir die &auml;nderungen an allen Recordsets nicht innerhalb einer Transaktion erfassen und gegebenenfalls r&uuml;ckg&auml;ngig machen.<\/p>\n<p>Deshalb richten wir f&uuml;r das Ereignis <b>Beim Anzeigen <\/b>des Recordsets im Hauptformular die Ereignisprozedur aus Listing 2 ein. Entscheidend f&uuml;r den Moment ist der untere Teil, wo eine <b>If&#8230;Then<\/b>-Bedingung pr&uuml;ft, ob das Hauptformular gerade einen neuen oder einen bereits vorhandenen Datensatz anzeigt. Im Falle eines vorhandenen Datensatzes stellt die Prozedur eine Abfrage zusammen, welche alle Datens&auml;tze der Datenherkunft des Unterformulars ermittelt, deren Fremdschl&uuml;sselfeld dem aktuellen Prim&auml;rschl&uuml;ssel im Hauptformular entspricht. Das auf dieser Abfrage basierende Recordset wird dann der Eigenschaft Recordset des jeweiligen Unterformulars zugewiesen.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_Form_Current()\r\n     <span style=\"color:blue;\">Dim <\/span>objUndoMultiSub<span style=\"color:blue;\"> As <\/span>clsUndoMultiSub\r\n     <span style=\"color:blue;\">If <\/span>m_DeletedForm = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         m_wrk.CommitTrans\r\n         m_SavedForm = <span style=\"color:blue;\">False<\/span>\r\n         m_DirtyForm = <span style=\"color:blue;\">False<\/span>\r\n         m_DeletedForm = <span style=\"color:blue;\">False<\/span>\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         <span style=\"color:blue;\">If <\/span>m_DirtyForm = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n             DoCmd.RunCommand acCmdSaveRecord\r\n             m_wrk.CommitTrans\r\n             m_SavedForm = <span style=\"color:blue;\">False<\/span>\r\n             m_DirtyForm = <span style=\"color:blue;\">False<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> m_Form.NewRecord<span style=\"color:blue;\"> Then<\/span>\r\n         For Each objUndoMultiSub In col\r\n             <span style=\"color:blue;\">Set<\/span> objUndoMultiSub.rst = m_db.OpenRecordset(\"SELECT * FROM \" & objUndoMultiSub.RecordsourceSubform _\r\n                 & \" WHERE \" & objUndoMultiSub.FKSubform & \" = \" & Nz(m_Form.Recordset.Fields(m_PKForm), 0), dbOpenDynaset)\r\n         <span style=\"color:blue;\">Next<\/span> objUndoMultiSub\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         For Each objUndoMultiSub In col\r\n             <span style=\"color:blue;\">Set<\/span> objUndoMultiSub.rst = m_db.OpenRecordset(\"SELECT * FROM \" & objUndoMultiSub.RecordsourceSubform _\r\n                 & \" WHERE 1=2\", dbOpenDynaset)\r\n         <span style=\"color:blue;\">Next<\/span> objUndoMultiSub\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: Code, der beim Wechseln des Datensatzes im Hauptformular ausgef&uuml;hrt wird.<\/span><\/b><\/p>\n<p>Neu gegen&uuml;ber der vorherigen Version, die nur ein einziges Unterformular erlaubte, eine <b>For Each<\/b>-Schleife, welche alle Unterformulare der in der Collection <b>col <\/b>gespeicherten Klassen durchl&auml;uft und diese mit den passenden Daten f&uuml;llt.<\/p>\n<p>Handelt es sich hingegen um einen neuen Datensatz im Hauptformular, durchl&auml;uft die Prozedur in einer alternativen <b>For Each<\/b>-Schleife alle Unterformular-Klassen und f&uuml;gt allen einen Recordset auf Basis der angegebenen Datenherkunft zu, allerdings mit einem Kriterium (<b>1=2<\/b>), das keine Ergebnisse liefert.<\/p>\n<p>Das ist korrekt, denn ein neuer Datensatz im Hauptformular darf nat&uuml;rlich noch keine Datens&auml;tze in den Unterformularen enthalten.<\/p>\n<p>Im oberen Teil pr&uuml;ft die Prozedur, ob <b>m_DeletedForm <\/b>den Wert <b>True <\/b>enth&auml;lt, was zeigt, dass der Datensatz im Hauptformular gel&ouml;st wurde. In diesem Fall wird die Transaktion beendet und die drei Variablen <b>m_DeletedForm<\/b>, <b>m_SavedForm <\/b>und <b>m_DirtyForm <\/b>auf <b>False <\/b>eingestellt &#8211; also ein unbearbeiteter Zustand angegeben.<\/p>\n<p>Wurde der aktuelle Datensatz nicht gel&ouml;scht, pr&uuml;ft die Prozedur, ob <b>m_DirtyForm <\/b>den Wert <b>True <\/b>hat, was bedeuten w&uuml;rde, dass der zuvor angezeigte Datensatz bearbeitet wurde. In diesem Fall wird der Datensatz gespeichert und auch die Transaktion abgeschlossen, damit auch die &auml;nderungen in den Unterformularen gespeichert werden.<\/p>\n<p>Auch dann werden <b>m_DirtyForm <\/b>und <b>m_SavedForm <\/b>auf <b>False <\/b>eingestellt.<\/p>\n<h2>Datensatz l&ouml;schen<\/h2>\n<p>Wenn der Benutzer einen Datensatz im Hauptformular l&ouml;scht, werden die beiden folgenden Prozeduren ausgel&ouml;st. Die erste stellt einfach nur ein, dass keine R&uuml;ckfrage angezeigt wird:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_Form_BeforeDelConfirm(Cancel<span style=\"color:blue;\"> As Integer<\/span>, _\r\n         Response<span style=\"color:blue;\"> As Integer<\/span>)\r\n     Response = acDataErrContinue\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die zweite pr&uuml;ft, ob bereits Daten im Haupt- oder Unterformular bearbeitet wurden, wodurch die Variable <b>m_DirtyForm <\/b>den Wert <b>True <\/b>h&auml;tte. Dann w&uuml;rde die Transaktion zuvor noch zur&uuml;ckabgewickelt werden:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_Form_Delete(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     <span style=\"color:blue;\">If <\/span>m_DirtyForm<span style=\"color:blue;\"> Then<\/span>\r\n         m_wrk.Rollback\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     m_DeletedForm = <span style=\"color:blue;\">True<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Undo ausl&ouml;sen<\/h2>\n<p>Wenn der Benutzer die am aktuellen Datensatz des Hauptformulars und an den Datens&auml;tzen im Unterformular vorgenommen &auml;nderungen zur&uuml;cknehmen m&ouml;chte, klickt er die Schaltfl&auml;che an, die f&uuml;r die Eigenschaft <b>m_CancelButton <\/b>hinterlegt wurde. Diese l&ouml;st das <b>Beim Klicken<\/b>-Ereignis aus, f&uuml;r die wir folgende Ereignisprozedur angelegt haben:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_CancelButton_Click()\r\n     <span style=\"color:blue;\">If <\/span>m_DirtyForm = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> m_SavedForm<span style=\"color:blue;\"> Then<\/span>\r\n             <span style=\"color:blue;\">If <\/span>m_Form.Dirty<span style=\"color:blue;\"> Then<\/span>\r\n                 m_Form.Undo\r\n             <span style=\"color:blue;\">Else<\/span>\r\n                 RunCommand acCmdSaveRecord\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         m_wrk.Rollback\r\n         m_DirtyForm = <span style=\"color:blue;\">False<\/span>\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 &uuml;berhaupt &auml;nderungen stattgefunden haben (bei jeder &auml;nderung wird die Variable <b>m_DirtyForm <\/b>auf <b>True <\/b>eingestellt). In diesem Fall pr&uuml;ft die Prozedur noch, ob die &auml;nderungen gegebenenfalls schon gespeichert wurden (<b>m_SavedForm = True<\/b>). Falls nicht, werden die &auml;nderungen im Formular r&uuml;ckg&auml;ngig gemacht und auch die Transaktion r&uuml;ckabgewickelt, was die &auml;nderungen in den Unterformularen r&uuml;ckg&auml;ngig machen w&uuml;rde.<\/p>\n<h2>&auml;nderungen speichern<\/h2>\n<p>Wenn der Benutzer die &auml;nderungen speichern m&ouml;chte, kann er entweder die <b>OK<\/b>-Schaltfl&auml;che anklicken oder einfach das Formular schlie&szlig;en. Beim Anklicken der <b>OK<\/b>-Schaltfl&auml;che wird die folgende Prozedur ausgel&ouml;st:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_OKButton_Click()\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> m_Form.NewRecord<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Call<\/span> AenderungenUebernehmen\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     DoCmd.Close acForm, m_Form.Name\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Dies ruft, wenn nicht gerade ein neuer Datensatz angezeigt wird, die Prozedur <b>AenderungenUebernehmen <\/b>auf, die wir weiter unten beschreiben. Diese Prozedur wird auch von der Ereignisprozedur aufgerufen, welche beim Entladen des Formulars feuert:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_Form_Unload(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     <span style=\"color:blue;\">Call<\/span> AenderungenUebernehmen\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die Prozedur <b>AenderungenUebernehmen <\/b>speichert zun&auml;chst den Datensatz im Hauptformular und pr&uuml;ft dann, ob weitere &auml;nderungen vorliegen (<b>m_DirtyForm = True<\/b>). In diesem Fall schlie&szlig;t sie die Transaktion ab und stellt <b>m_DirtyForm <\/b>auf <b>False <\/b>ein:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>AenderungenUebernehmen()\r\n     DoCmd.RunCommand acCmdSaveRecord\r\n     <span style=\"color:blue;\">If <\/span>m_DirtyForm = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         m_wrk.CommitTrans\r\n         m_DirtyForm = <span style=\"color:blue;\">False<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Das Ereignis <b>Nach Aktualisierung <\/b>des Hauptformulars wird beispielsweise ausgel&ouml;st, wenn der Benutzer den Datensatz im Hauptformular &auml;ndert und diesen dann speichert, indem er etwa den Fokus auf eines der Unterformulare verschiebt. Dies stellt einfach die Variable <b>m_SavedForm <\/b>auf <b>True <\/b>ein:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_Form_AfterUpdate()\r\n     m_SavedForm = <span style=\"color:blue;\">True<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Sobald der Benutzer eine &auml;nderung in irgendeinem der Felder des Hauptformulars vornimmt, wird das Ereignis <b>Bei &auml;nderung <\/b>ausgel&ouml;st, f&uuml;r welches wie die folgende Prozedur hinterlegt haben. Diese pr&uuml;ft, ob <b>m_DirtyForm <\/b>zuvor noch <b>False <\/b>war und stellt es dann auf <b>True <\/b>ein &#8211; au&szlig;erdem wird die Transaktion gestartet:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_Form_Dirty(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     <span style=\"color:blue;\">If <\/span>m_DirtyForm = <span style=\"color:blue;\">False<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         m_DirtyForm = <span style=\"color:blue;\">True<\/span>\r\n         m_wrk.BeginTrans\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Fehlt noch die Prozedur, die durch Fehler im Formular-Kontext ausgel&ouml;st werden. Hier zielen wir speziell auf den Fehler <b>3246 <\/b>ab, der auftritt, wenn der Benutzer versucht, mit <b>F5 <\/b>das Formular zu aktualisieren, w&auml;hrend schon &auml;nderungen vorgenommen wurden. Diesen Fehler fangen wir mit der Prozedur <b>Form_Error <\/b>ab und untersuchen, ob sie durch den genannten Fehler ausgel&ouml;st wurde (s. Listing 3). Die Prozedur fragt dann beim Benutzer nach, ob die &auml;nderungen im aktuellen Datensatz gespeichert oder verworfen werden sollen. Abh&auml;ngig vom Ergebnis wird dann entweder die Transaktion durchgef&uuml;hrt oder abgebrochen. Schlie&szlig;lich wird <b>m_DirtyForm <\/b>auf <b>False <\/b>und <b>m_SavedForm <\/b>auf <b>True <\/b>eingestellt. Die letzte Anweisung sorgt daf&uuml;r, dass die Standardfehlermeldung unterdr&uuml;ckt wird.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_Form_Error(DataErr<span style=\"color:blue;\"> As Integer<\/span>, Response<span style=\"color:blue;\"> As Integer<\/span>)\r\n     Select Case DataErr\r\n         <span style=\"color:blue;\">Case <\/span>3246 ''''Versuch, zu aktualisieren\r\n             <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">MsgBox<\/span>(\"&auml;nderungen speichern (OK) oder Verwerfen (Abbrechen)\", vbOKCancel) = vbOK<span style=\"color:blue;\"> Then<\/span>\r\n                 m_wrk.CommitTrans\r\n             <span style=\"color:blue;\">Else<\/span>\r\n                 m_wrk.Rollback\r\n             <span style=\"color:blue;\">End If<\/span>\r\n             m_DirtyForm = <span style=\"color:blue;\">False<\/span>\r\n             m_SavedForm = <span style=\"color:blue;\">True<\/span>\r\n             Response = acDataErrContinue\r\n     <span style=\"color:blue;\">End Select<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Code, der beim Versuch der Aktualisierung mit F5 ausgel&ouml;st wird.<\/span><\/b><\/p>\n<p>Fehlt nur noch die Prozedur, die beim Ausl&ouml;sen des <b>Undo<\/b> etwa durch Bet&auml;tigen der <b>Escape<\/b>-Taste ausgel&ouml;st wird:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_Form_Undo(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     <span style=\"color:blue;\">If <\/span>m_SavedForm = <span style=\"color:blue;\">False<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         m_DirtyForm = <span style=\"color:blue;\">False<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Dies pr&uuml;ft, ob die &auml;nderungen noch nicht gespeichert wurden, und stellt in diesem Fall <b>m_DirtyForm <\/b>auf <b>False <\/b>ein.<\/p>\n<h2>Die Unterklasse clsUndoMultiSub<\/h2>\n<p>Nun kommt die Klasse, in die bei der Umwandlung von einer L&ouml;sung f&uuml;r ein Unterformular in eine L&ouml;sung f&uuml;r mehrere Unterformulare viel Hirnschmalz geflossen ist. Die Frage war: Welche Elemente m&uuml;ssen in der Unterklasse landen, welche stellt man in der Hauptklasse bereits und wie gelangt man von den Unterklassen dann an die Eigenschaften der Elemente in der Hauptklasse Es ist ja nicht so einfach wie bei Formularen und Unterformularen, wo man &uuml;ber die <b>Parent<\/b>-Eigenschaft auf die &uuml;bergeordneten Instanzen zugreifen kann. Genau genommen wissen die beiden Klassen <b>clsUndoMultiMain <\/b>und <b>clsUndoMultiSub <\/b>ja noch nicht einmal von ihrer von uns aufgepr&auml;gten Beziehung als Vertreter von Haupt- und Unterformularen.<\/p>\n<p>Nach einigen Experimenten hat sich jedoch die Idee durchgesetzt, dass wir den Unterobjekten auf Basis der Klasse <b>clsUndoMultiSub <\/b>bei Anlegen einen Verweis auf die instanzierende Klasse <b>clsUndoMultiSub <\/b>&uuml;bergeben. Diesen speichern wir in der Klasse <b>clsUndoMultiSub <\/b>in der folgenden Variablen:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>m_Main<span style=\"color:blue;\"> As <\/span>clsUndoMultiMain<\/pre>\n<p>Und &uuml;ber diese <b>Property Get<\/b>-Methode &uuml;bergeben wir eine Instanz des aufrufenden Objekts an die Variable <b>m_Main<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Public Property <span style=\"color:blue;\">Set<\/span> <\/span>Main(obj<span style=\"color:blue;\"> As <\/span>clsUndoMultiMain)\r\n     <span style=\"color:blue;\">Set<\/span> m_Main = obj\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p>In <b>clsUndoMultiSub <\/b>brauchen wir noch private Variablen f&uuml;r weitere Elemente und Werte, n&auml;mlich das Unterformular, auf das sich die Instanz der Klasse bezieht (<b>m_Subform<\/b>), ein Recordset mit den Daten des Unterformulars (<b>m_rst<\/b>) sowie zwei <b>String<\/b>-Variablen f&uuml;r den Namen des Fremdschl&uuml;sselfeldes und der Datenherkunft des Unterformulars. Diese deklarieren wir wie folgt:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>WithEvents m_Subform<span style=\"color:blue;\"> As <\/span>Form\r\n<span style=\"color:blue;\">Private <\/span>m_rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n<span style=\"color:blue;\">Private <\/span>m_FKSubform<span style=\"color:blue;\"> As String<\/span>\r\n<span style=\"color:blue;\">Private <\/span>m_RecordsourceSubform<span style=\"color:blue;\"> As String<\/span><\/pre>\n<p>Um diesen Variablen die Werte von der instanzierenden Klasse <b>clsUndoMultiMain <\/b>zuweisen und diese auch lesen zu k&ouml;nnen, legen wir wieder einige <b>Property Get<\/b>\/<b>Set<\/b>-Prozeduren an.<\/p>\n<p>Das Recordset wollen wir nur setzen k&ouml;nnen. Dabei speichern wir es nicht nur in der Variablen <b>m_rst<\/b>, sondern weisen es auch gleich dem Unterformular f&uuml;r die Eigenschaft <b>Recordset <\/b>zu:<\/p>\n<pre><span style=\"color:blue;\">Public Property <span style=\"color:blue;\">Set<\/span> <\/span>rst(rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset)\r\n     <span style=\"color:blue;\">Set<\/span> m_rst = rst\r\n     <span style=\"color:blue;\">Set<\/span> m_Subform.Recordset = m_rst\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p>Den Verweis auf die Datenquelle des Unterformulars wollen wir lesen und schreiben k&ouml;nnen:<\/p>\n<pre><span style=\"color:blue;\">Public Property Let <\/span>RecordsourceSubform(str<span style=\"color:blue;\"> As String<\/span>)\r\n     m_RecordsourceSubform = str\r\n<span style=\"color:blue;\">End Property<\/span>\r\n<span style=\"color:blue;\">Public Property Get <\/span>RecordsourceSubform()<span style=\"color:blue;\"> As String<\/span>\r\n     RecordsourceSubform = m_RecordsourceSubform\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p>Gleiches gilt f&uuml;r den Namen des Fremdschl&uuml;sselfeldes im Unterformular:<\/p>\n<pre><span style=\"color:blue;\">Public Property Let <\/span>FKSubform(str<span style=\"color:blue;\"> As String<\/span>)\r\n     m_FKSubform = str\r\n<span style=\"color:blue;\">End Property<\/span>\r\n<span style=\"color:blue;\">Public Property Get <\/span>FKSubform()<span style=\"color:blue;\"> As String<\/span>\r\n     FKSubform = m_FKSubform\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p>Das Unterformular, f&uuml;r das wie die Klasse instanzieren, wollen wir hingegen einfach nur der Klasse bekanntgeben. In dieser <b>Property Set<\/b>-Methode stecken allerdings ein paar mehr Anweisungen, n&auml;mlich solche, mit denen wir der Klasse mitteilen, welche Ereignisse des Unterformulars hier implementiert werden sollen:<\/p>\n<pre><span style=\"color:blue;\">Public Property <span style=\"color:blue;\">Set<\/span> <\/span>Subform(frm<span style=\"color:blue;\"> As <\/span>Form)\r\n     <span style=\"color:blue;\">Set<\/span> m_Subform = frm\r\n     <span style=\"color:blue;\">With<\/span> m_Subform\r\n         .AfterDelConfirm = \"[Event Procedure]\"\r\n         .AfterUpdate = \"[Event Procedure]\"\r\n         .BeforeDelConfirm = \"[Event Procedure]\"\r\n         .OnDelete = \"[Event Procedure]\"\r\n         .OnDirty = \"[Event Procedure]\"\r\n         .OnError = \"[Event Procedure]\"\r\n         .OnOpen = \"[Event Procedure]\"\r\n         .OnUndo = \"[Event Procedure]\"\r\n     End <span style=\"color:blue;\">With<\/span>\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p>Die meisten davon stellen nur die beiden Eigenschaften <b>SavedForm <\/b>und <b>DirtyForm <\/b>der Klasse f&uuml;r das Hauptformular ein.<\/p>\n<h2>L&ouml;schen eines Datensatzes im Unterformular<\/h2>\n<p>Wenn der Benutzer beispielsweise einen Datensatz im Unterformular l&ouml;scht, werden diese drei Ereignisprozeduren ausgel&ouml;st.<\/p>\n<p>Die erste stellt sicher, dass keine R&uuml;ckfrage an den Benutzer gemeldet wird:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_Subform_BeforeDelConfirm(Cancel<span style=\"color:blue;\"> As Integer<\/span>, _\r\n         Response<span style=\"color:blue;\"> As Integer<\/span>)\r\n     Response = acDataErrContinue\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die zweite pr&uuml;ft, ob das L&ouml;schen durchgef&uuml;hrt wurde und stellt dann die Eigenschaft <b>SavedForm <\/b>der Klasse <b>clsUndoMultiMain <\/b>auf <b>True <\/b>ein:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_Subform_AfterDelConfirm(Status<span style=\"color:blue;\"> As Integer<\/span>)\r\n     <span style=\"color:blue;\">If <\/span>Status = acDeleteOK<span style=\"color:blue;\"> Then<\/span>\r\n         m_Main.SavedForm = <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>Schlie&szlig;lich pr&uuml;ft die durch das Ereignis <b>Beim L&ouml;schen <\/b>ausgel&ouml;ste Prozedur anhand der Eigenschaft <b>DirtyForm<\/b>, ob in der Hauptklasse bereits gemeldet ist, dass sich ein Datensatz im Hauptformular oder einem der Unterformulare bereits in Bearbeitung befindet. Falls nicht, wird <b>DirtyForm <\/b>auf <b>True <\/b>eingestellt und die Transaktion mit <b>BeginTrans <\/b>gestartet. Dabei greift die Prozedur wieder auf die durchgereichte Referenz auf die Klasse f&uuml;r das Hauptformular zu:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_Subform_Delete(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     <span style=\"color:blue;\">If <\/span>m_Main.DirtyForm = <span style=\"color:blue;\">False<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         m_Main.DirtyForm = <span style=\"color:blue;\">True<\/span>\r\n         m_Main.wrk.BeginTrans\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>&auml;ndern eines Datensatzes im Unterformular<\/h2>\n<p>&auml;ndert der Benutzer einen Datensatz im Unterformular, l&ouml;st dies das Ereignis <b>Bei &auml;nderung <\/b>aus. Daf&uuml;r haben wir die folgende Ereignisprozedur hinterlegt:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_Subform_Dirty(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     <span style=\"color:blue;\">If <\/span>m_Subform.NewRecord<span style=\"color:blue;\"> Then<\/span>\r\n         m_Subform.Controls(m_FKSubform) = _\r\n             m_Main.Form.Controls(m_Main.PKForm)\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span>m_Main.DirtyForm = <span style=\"color:blue;\">False<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         m_Main.DirtyForm = <span style=\"color:blue;\">True<\/span>\r\n         m_Main.wrk.BeginTrans\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Diese pr&uuml;ft zun&auml;chst, ob es sich um einen neuen Datensatz handelt. In diesem Fall wird der Wert des Fremdschl&uuml;sselfeldes im Unterformular auf den Wert des Prim&auml;rschl&uuml;sselfeldes im Hauptformular eingestellt. Dies ersetzt die sonst eingestellten Eigenschaften <b>Verkn&uuml;pfen von <\/b>und <b>Verkn&uuml;pfen nach <\/b>im Unterformular-Steuerelement, mit denen &uuml;blicherweise sichergestellt wird, dass das Unterformular nur zu dem im Hauptformular angezeigten Datensatz passende Datens&auml;tze anzeigt.<\/p>\n<p>Diese Eigenschaften sorgen normalerweise auch daf&uuml;r, dass das Fremdschl&uuml;sselfeld im Unterformular mit dem Prim&auml;rschl&uuml;sselwert des Unterformulars gef&uuml;llt wird, was wir hier manuell erledigen m&uuml;ssen. Au&szlig;erdem pr&uuml;ft die Prozedur, ob im Hauptformular bereits ein Datensatz im Hauptformular selbst oder ein verkn&uuml;pfter Datensatz im Unterformular als bearbeitet gemeldet wurde, was wir an der Eigenschaft <b>DirtyForm <\/b>erkennen k&ouml;nnen.<\/p>\n<p>Falls nicht, wird <b>DirtyForm <\/b>auf <b>True <\/b>eingestellt und die Transaktion mit der Methode <b>BeginTrans <\/b>gestartet.<\/p>\n<h2>Nach der Aktualisierung im Unterformular<\/h2>\n<p>Wenn der Benutzer einen Datensatz in einem der Unterformulare angelegt oder bearbeitet und dann etwa durch einen Wechsel zu einem anderen Datensatz oder in das Hauptformular gespeichert hat, wird das Ereignis <b>Nach Aktualisierung <\/b>ausgel&ouml;st. Daf&uuml;r haben wir die folgende Ereignisprozedur hinterlegt:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_Subform_AfterUpdate()\r\n     m_Main.SavedForm = <span style=\"color:blue;\">True<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Diese meldet schlicht &uuml;ber die Eigenschaft <b>SavedForm<\/b>, dass der in Bearbeitung befindliche Datensatz (von dem es immer nur einen geben kann, da ja durch einen Wechsel zu einem anderen Datensatz, sei es im Haupt- oder im Unterformular) gespeichert ist.<\/p>\n<h2>F5 im Unterformular<\/h2>\n<p>Auch wenn der Benutzer gerade den Fokus auf ein Steuer-element im Unterformular gelegt hat, kann er auf die Idee kommen, die aktuell angezeigten Datens&auml;tze mit der Taste <b>F5 <\/b>zu aktualisieren. In diesem Fall wird bei nicht gespeichertem Datensatz auch im Unterformular das <b>Bei Fehler<\/b>-Ereignis ausgel&ouml;st. Daf&uuml;r hinterlegen wir die Ereignisprozedur aus Listing 4.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_Subform_Error(DataErr<span style=\"color:blue;\"> As Integer<\/span>, Response<span style=\"color:blue;\"> As Integer<\/span>)\r\n     Select Case DataErr\r\n         <span style=\"color:blue;\">Case <\/span>3246 ''''Versuch, zu aktualisieren\r\n             <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">MsgBox<\/span>(\"&auml;nderungen speichern (OK) oder Verwerfen (Abbrechen)\", vbOKCancel) = vbOK<span style=\"color:blue;\"> Then<\/span>\r\n                 m_Main.wrk.CommitTrans\r\n             <span style=\"color:blue;\">Else<\/span>\r\n                 m_Main.wrk.Rollback\r\n             <span style=\"color:blue;\">End If<\/span>\r\n             m_Main.DirtyForm = <span style=\"color:blue;\">False<\/span>\r\n             m_Main.SavedForm = <span style=\"color:blue;\">False<\/span>\r\n             Response = acDataErrContinue\r\n     <span style=\"color:blue;\">End Select<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Code, der beim Versuch der Aktualisierung mit F5 im Unterformular ausgel&ouml;st wird.<\/span><\/b><\/p>\n<p>Sie reagiert wieder nur auf die hier relevante Fehlernummer <b>3246<\/b>. In diesem Fall zeigt sie eine Meldung an, die den Benutzer fragt, ob er die offenen &auml;nderungen speichern oder verwerfen m&ouml;chte. W&uuml;nscht der Benutzer das Speichern, wird die Transaktion mit der <b>CommitTrans<\/b>-Methode des <b>Workspace<\/b>-Objekts aus der Hauptklasse abgeschlossen, anderenfalls wird sie mit <b>Rollback <\/b>zur&uuml;ckgesetzt. Anschlie&szlig;end werden <b>DirtyForm <\/b>und <b>SavedForm <\/b>auf <b>False <\/b>gesetzt, weil ja keine offenen &auml;nderungen mehr vorliegen. <b>Response = acDataErrContinue <\/b>sorgt daf&uuml;r, dass die eingebaute Fehlermeldung unterbunden wird.<\/p>\n<h2>Escape-Taste im Unterformular<\/h2>\n<p>Auch wenn der Fokus auf einem der Unterformulare liegt, kann der Benutzer versuchen, die &auml;nderungen mit der <b>Undo<\/b>-Taste r&uuml;ckg&auml;ngig zu machen. In diesem Fall m&uuml;ssen wir Eigenschaft <b>DirtyForm<\/b>, die durch eventuell zuvor durchgef&uuml;hrte &auml;nderungen im aktuellen Unterformular eingestellt wurde, wieder auf <b>False <\/b>setzen &#8211; dies allerdings nur, wenn es auch noch ungespeicherte &auml;nderungen gibt:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>m_Subform_Undo(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     <span style=\"color:blue;\">If <\/span>m_Main.SavedForm = <span style=\"color:blue;\">False<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         m_Main.DirtyForm = <span style=\"color:blue;\">False<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Zusammenfassung<\/h2>\n<p>Wenn Sie die hier vorgestellte Funktionalit&auml;t in eigenen Formularen einsetzen wollen, sind die folgenden Schritte zu erledigen: <\/p>\n<ul>\n<li>Hinzuf&uuml;gen der beiden Klassen <b>clsUndoMultiMain <\/b>und <b>clsUndoMultiSub <\/b>zum VBA-Projekt<\/li>\n<li>Einstellen der Eigenschaft <b>Enth&auml;lt Modul <\/b>f&uuml;r die beteiligten Unterformulare auf den Wert <b>Ja<\/b><\/li>\n<li>Hinzuf&uuml;gen einer Variablen f&uuml;r ein Objekt auf Basis der Klasse <b>clsUndoMultiMain<\/b> zum Klassenmodul des Hauptformulars<\/li>\n<li>Hinzuf&uuml;gen einer Prozedur f&uuml;r das Ereignis <b>Beim Laden <\/b>zum Hauptformular, welche die Klasse instanziert und die Eigenschaften einstellt sowie die Klassen f&uuml;r die Unterformulare hinzuf&uuml;gt.<\/li>\n<h3>Downloads zu diesem Beitrag<\/h3>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>UndoInFormularUndUnterformular.mdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/30F4BAA8-003F-4229-BB64-061824D97BAF\/aiu_1109.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In den Beitr&auml;gen &#8222;Undo in Haupt- und Unterformular&#8220; und &#8222;Undo in Haupt- und Unterformular mit Klasse&#8220; haben wir gezeigt, wie Sie die Undo-Funktion etwa durch einen Abbrechen-Schaltfl&auml;che nicht nur auf das Hauptformular, sondern auch auf die &Auml;nderungen im Unterformular erstrecken. Nun hat ein Leser gefragt, ob man dies auch f&uuml;r mehrere Unterformulare erledigen kann. Klar kann man &#8211; die angepasste L&ouml;sung stellt der vorliegende Beitrag vor.<\/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":[662017,66062017,44000023],"tags":[],"class_list":["post-55001109","post","type-post","status-publish","format-standard","hentry","category-662017","category-66062017","category-Mit_Formularen_arbeiten"],"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>Undo in mehreren Unterformularen - 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\/Undo_in_mehreren_Unterformularen\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Undo in mehreren Unterformularen\" \/>\n<meta property=\"og:description\" content=\"In den Beitr&auml;gen &quot;Undo in Haupt- und Unterformular&quot; und &quot;Undo in Haupt- und Unterformular mit Klasse&quot; haben wir gezeigt, wie Sie die Undo-Funktion etwa durch einen Abbrechen-Schaltfl&auml;che nicht nur auf das Hauptformular, sondern auch auf die &Auml;nderungen im Unterformular erstrecken. Nun hat ein Leser gefragt, ob man dies auch f&uuml;r mehrere Unterformulare erledigen kann. Klar kann man - die angepasste L&ouml;sung stellt der vorliegende Beitrag vor.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Undo_in_mehreren_Unterformularen\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2020-05-13T21:27:44+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg09.met.vgwort.de\/na\/26a2197d643948308f065d0b16940c6b\" \/>\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=\"25\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Undo_in_mehreren_Unterformularen\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Undo_in_mehreren_Unterformularen\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Undo in mehreren Unterformularen\",\"datePublished\":\"2020-05-13T21:27:44+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Undo_in_mehreren_Unterformularen\\\/\"},\"wordCount\":3950,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Undo_in_mehreren_Unterformularen\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/26a2197d643948308f065d0b16940c6b\",\"articleSection\":[\"2017\",\"6\\\/2017\",\"Mit Formularen arbeiten\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Undo_in_mehreren_Unterformularen\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Undo_in_mehreren_Unterformularen\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Undo_in_mehreren_Unterformularen\\\/\",\"name\":\"Undo in mehreren Unterformularen - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Undo_in_mehreren_Unterformularen\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Undo_in_mehreren_Unterformularen\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/26a2197d643948308f065d0b16940c6b\",\"datePublished\":\"2020-05-13T21:27:44+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Undo_in_mehreren_Unterformularen\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Undo_in_mehreren_Unterformularen\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Undo_in_mehreren_Unterformularen\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/26a2197d643948308f065d0b16940c6b\",\"contentUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/26a2197d643948308f065d0b16940c6b\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Undo_in_mehreren_Unterformularen\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Undo in mehreren Unterformularen\"}]},{\"@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":"Undo in mehreren Unterformularen - 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\/Undo_in_mehreren_Unterformularen\/","og_locale":"de_DE","og_type":"article","og_title":"Undo in mehreren Unterformularen","og_description":"In den Beitr&auml;gen \"Undo in Haupt- und Unterformular\" und \"Undo in Haupt- und Unterformular mit Klasse\" haben wir gezeigt, wie Sie die Undo-Funktion etwa durch einen Abbrechen-Schaltfl&auml;che nicht nur auf das Hauptformular, sondern auch auf die &Auml;nderungen im Unterformular erstrecken. Nun hat ein Leser gefragt, ob man dies auch f&uuml;r mehrere Unterformulare erledigen kann. Klar kann man - die angepasste L&ouml;sung stellt der vorliegende Beitrag vor.","og_url":"https:\/\/access-im-unternehmen.de\/Undo_in_mehreren_Unterformularen\/","og_site_name":"Access im Unternehmen","article_published_time":"2020-05-13T21:27:44+00:00","og_image":[{"url":"http:\/\/vg09.met.vgwort.de\/na\/26a2197d643948308f065d0b16940c6b","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"25\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Undo_in_mehreren_Unterformularen\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Undo_in_mehreren_Unterformularen\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Undo in mehreren Unterformularen","datePublished":"2020-05-13T21:27:44+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Undo_in_mehreren_Unterformularen\/"},"wordCount":3950,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Undo_in_mehreren_Unterformularen\/#primaryimage"},"thumbnailUrl":"http:\/\/vg09.met.vgwort.de\/na\/26a2197d643948308f065d0b16940c6b","articleSection":["2017","6\/2017","Mit Formularen arbeiten"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Undo_in_mehreren_Unterformularen\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Undo_in_mehreren_Unterformularen\/","url":"https:\/\/access-im-unternehmen.de\/Undo_in_mehreren_Unterformularen\/","name":"Undo in mehreren Unterformularen - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Undo_in_mehreren_Unterformularen\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Undo_in_mehreren_Unterformularen\/#primaryimage"},"thumbnailUrl":"http:\/\/vg09.met.vgwort.de\/na\/26a2197d643948308f065d0b16940c6b","datePublished":"2020-05-13T21:27:44+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Undo_in_mehreren_Unterformularen\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Undo_in_mehreren_Unterformularen\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/Undo_in_mehreren_Unterformularen\/#primaryimage","url":"http:\/\/vg09.met.vgwort.de\/na\/26a2197d643948308f065d0b16940c6b","contentUrl":"http:\/\/vg09.met.vgwort.de\/na\/26a2197d643948308f065d0b16940c6b"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Undo_in_mehreren_Unterformularen\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Undo in mehreren Unterformularen"}]},{"@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\/55001109","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=55001109"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001109\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001109"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001109"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001109"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}