{"id":55001084,"date":"2017-04-01T00:00:00","date_gmt":"2020-05-14T13:42:21","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1084"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Stuecklisten_Teil_II","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Stuecklisten_Teil_II\/","title":{"rendered":"St&uuml;cklisten, Teil II"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg09.met.vgwort.de\/na\/5ce066e88ff44a04b0fbe795e10a84e8\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Was lange w&auml;hrt, wird endlich gut: Hier kommt der zweite Teil zu der vor einigen Ausgaben begonnenen Reihe zum Thema St&uuml;cklisten. Nachdem das TreeView-Steuerelement zur Anzeige und zum Bearbeiten von Baugruppen und Teilen steht, erweitern wir die L&ouml;sung nun um Kontextmen&uuml;-Eintr&auml;ge, mit denen Sie Baugruppen und Elemente hinzuf&uuml;gen, bearbeiten und entfernen k&ouml;nnen.<\/b><\/p>\n<h2>Kontextmen&uuml;s f&uuml;r die St&uuml;ckliste<\/h2>\n<p>Im ersten Teil der Beitragsreihe haben wir bereits ein erstes Kontextmen&uuml; mit Untermen&uuml;s zur Anwendung hinzugef&uuml;gt. Dieses wird angezeigt, wenn der Benutzer mit der rechten Maustaste auf den Root-Eintrag des TreeViews mit der Beschriftung <b>Produkte <\/b>klickt. Es erscheint dann ein Kontextmen&uuml; mit den beiden Eintr&auml;gen <b>Baugruppen <\/b>und <b>Einzelteile<\/b>, die wiederum Untermen&uuml;s mit den vorhandenen Elementen anzeigen (s. Bild 1).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_02\/pic_1084_001.png\" alt=\"Kontextmen&uuml; zum Anlegen von Produkten\" width=\"424,7115\" height=\"454,9709\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Kontextmen&uuml; zum Anlegen von Produkten<\/span><\/b><\/p>\n<p>Nach dem Anklicken einer der Baugruppen beziehungsweise eines der Einzelteile wird dieses direkt unterhalb des Elements <b>Produkte <\/b>angelegt.<\/p>\n<p>Es fehlen allerdings noch einige weitere Kontextmen&uuml;s &#8211; beispielsweise, um Baugruppen weitere Baugruppen oder Elemente hinzuzuf&uuml;gen, um Baugruppen und Teile zu entfernen oder auch um Baugruppen oder Teile zu markieren, zu kopieren und an anderen Stellen wieder einzuf&uuml;gen.<\/p>\n<h2>Produkte, Baugruppen und Einzelteile<\/h2>\n<p>Zur Erinnerung an den ersten Teil der Beitragsreihe: Wir unterscheiden prinzipiell zwischen Baugruppen (Tabelle <b>tblBaugruppen<\/b>) und Einzelteilen (<b>tblEinzelteile<\/b>) sowie zwischen Produkten. Letztere sind Baugruppen oder Einzelteile, die im Datensatz ihrer Tabelle mit dem Wert <b>True <\/b>im Feld <b>IstProdukt <\/b>gekennzeichnet sind.<\/p>\n<p>Produkte werden immer direkt unterhalb des Root-Elements <b>Produkte <\/b>angezeigt, alle anderen dort, wo sie zugeordnet wurden. Es kann nat&uuml;rlich auch Baugruppen oder Einzelteile geben, die nur in der Tabelle verf&uuml;gbar sind, aber noch keinem Produkt oder keiner Baugruppe untergeordnet sind und dementsprechend nicht im TreeView erscheinen.<\/p>\n<h2>Baugruppen oder Teile per Kontextmen&uuml; entfernen<\/h2>\n<p>Die wichtigste Prozedur beim Anzeigen von Kontextmen&uuml;s ist die, welche beim Herunterdr&uuml;cken der rechten Maustaste ausgel&ouml;st wird. In unserem Fall sieht diese wie in Listing 1 aus.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>objTreeView_MouseDown(ByVal Button<span style=\"color:blue;\"> As Integer<\/span>, ByVal Shift<span style=\"color:blue;\"> As Integer<\/span>, _\r\n         ByVal x<span style=\"color:blue;\"> As <\/span>stdole.OLE_XPOS_PIXELS, ByVal y<span style=\"color:blue;\"> As <\/span>stdole.OLE_YPOS_PIXELS)\r\n     <span style=\"color:blue;\">Dim <\/span>objNode<span style=\"color:blue;\"> As <\/span>MSComctlLib.Node\r\n     <span style=\"color:blue;\">Dim <\/span>strTyp<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngID<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strKey<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strKeyItem<span style=\"color:blue;\"> As String<\/span>\r\n     Select Case Button\r\n         <span style=\"color:blue;\">Case <\/span>acRightButton\r\n             <span style=\"color:blue;\">Set<\/span> objNode = objTreeView.HitTest(x, y)\r\n             <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> objNode Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n                 strKey = objNode.Key\r\n                 strKeyItem = <span style=\"color:blue;\">Mid<\/span>(objNode.Key, <span style=\"color:blue;\">InStrRev<\/span>(objNode.Key, \"|\") + 1)\r\n                 strTyp = <span style=\"color:blue;\">Left<\/span>(strKeyItem, 1)\r\n                 lngID = <span style=\"color:blue;\">Mid<\/span>(strKeyItem, 2)\r\n                 Select Case strTyp\r\n                     <span style=\"color:blue;\">Case <\/span>\"r\"\r\n                         KontextmenueRoot strKey\r\n                     <span style=\"color:blue;\">Case <\/span>\"b\"\r\n                         KontextmenueBaugruppe lngID, strKey\r\n                     <span style=\"color:blue;\">Case <\/span>\"e\"\r\n                         KontextmenueEinzelteil lngID, strKey\r\n                 <span style=\"color:blue;\">End Select<\/span>\r\n             <span style=\"color:blue;\">End If<\/span>\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 1: Aufrufen eines Kontextmen&uuml;-Eintrags<\/span><\/b><\/p>\n<p>Achtung: M&ouml;glicherweise kommt Ihnen diese Prozedur bekannt vor, da wir diese im ersten Teil schon einmal vorgestellt haben. Bei der Weiterentwicklung haben wir jedoch ein paar kleine Optimierungen vorgenommen, welche das Anlegen und Entfernen von Elementen wesentlich erleichtern. Dazu geh&ouml;rt unter anderem, dass wir bei den Aufrufen der Prozeduren <b>KontextmenueEinzelteil <\/b>und <b>KontextmenueBaugruppe <\/b>nun immer auch den <b>Key<\/b>-Wert des aktuellen Elements mitgeben &#8211; mehr dazu weiter unten.<\/p>\n<p>Die Prozedur pr&uuml;ft anhand des Parameters <b>Button<\/b>, welche Maustaste der Benutzer heruntergedr&uuml;ckt hat. Handelt es sich um die rechte Maustaste (<b>acRightButton<\/b>), soll das Kontextmen&uuml; eingeblendet werden. Unsere Prozedur dient dabei als eine Art Verteiler, die pr&uuml;fen soll, auf welcher Elementart die rechte Maustaste bet&auml;tigt wurde, und dann eine entsprechende Prozedur zum Anzeigen des Kontextmen&uuml;s aufruft. Bevor dies geschieht, ermittelt sie mit der <b>HitTest<\/b>-Methode des <b>TreeView<\/b>-Elements auf welchem Element die rechte Maustaste bet&auml;tigt wurde. Das Ergebnis landet in der <b>MSComCtlLib.Node<\/b>-Variablen <b>objNode<\/b>. Sollte diese anschlie&szlig;end nicht den Wert <b>Nothing <\/b>enthalten, hat der Benutzer tats&auml;chlich ein Element angeklickt.<\/p>\n<p>In diesem Fall ermitteln wir aus der <b>Key<\/b>-Variablen des Elements, der beispielsweise den Wert <b>r0<\/b>, <b>r0|b18 <\/b>oder <b>r0|b18|e50 <\/b>enthalten kann, den Buchstaben des hintersten Elements, hier also <b>r<\/b>, <b>b <\/b>oder <b>e<\/b>, und speichern diesen in der Variablen <b>strTyp<\/b>. Die Zahl hinter diesem Element, die dem Prim&auml;rschl&uuml;sselwert des entsprechenden Eintrags einer der Tabellen <b>tblBaugruppen <\/b>oder <b>tblEinzelteile <\/b>entspricht, landet hingegen in der Variablen <b>lngID<\/b>. Der Inhalt von <b>strTyp<\/b> wird in der folgenden <b>Select Case<\/b>-Bedingung gepr&uuml;ft.  Abh&auml;ngig vom Wert rufen wir dann eine der Prozeduren <b>KontextmenueRoot <\/b>(<b>r<\/b>, Root-Element), <b>KontextmenueEinzelteil <\/b>(<b>p<\/b>, Produkt-Element), <b>KontextmenueBaugruppe <\/b>(<b>b<\/b>, Bauteil-Element) oder <b>KontextmenueEinzelteil <\/b>(<b>e<\/b>, Einzelteil-Element) auf. Dieser &uuml;bergeben wir den Prim&auml;rschl&uuml;ssel des aktuellen Elements sowie den <b>Key<\/b>-Wert (zum Beispiel <b>r0<\/b>, <b>r0|b18 <\/b>oder <b>r0|b18|e50<\/b>).<\/p>\n<p>Schauen wir uns erst das Root-Kontextmen&uuml; an. Die entsprechende Prozedur hei&szlig;t <b>KontextmenueRoot <\/b>und sieht wie in Listing 2 aus. Die Prozedur l&ouml;scht zun&auml;chst ein eventuell bereits vorhandenes Kontextmen&uuml; namens <b>TreeView_Root<\/b> &#8211; vorsichtshalber bei deaktivierter Fehlerbehandlung, da der Versuch, ein nicht vorhandenes Kontextmen&uuml; zu l&ouml;schen, einen Fehler ausl&ouml;sen w&uuml;rde. Danach legen wir dieses direkt neu an, und zwar mit der daf&uuml;r vorgesehenen Methode <b>Add<\/b>. Diese erwartet als Parameter den Namen des Men&uuml;s, die Art (da die <b>Add<\/b>-Methode fr&uuml;her auch f&uuml;r das Anlegen von Men&uuml;leisten und Symbolleisten verantwortlich war &#8211; hier <b>msoBarPopup <\/b>f&uuml;r Kontextmen&uuml;) sowie die Angabe, ob es sich um ein tempor&auml;res Men&uuml; handelt, das mit dem Schlie&szlig;en der Anwendung wieder entsorgt werden kann.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>KontextmenueRoot(<span style=\"color:blue;\">Optional<\/span> strKey<span style=\"color:blue;\"> As String<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>cbr<span style=\"color:blue;\"> As <\/span>CommandBar\r\n     On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n     CommandBars(\"TreeView_Root\").Delete\r\n     <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n     <span style=\"color:blue;\">Set<\/span> cbr = CommandBars.Add(\"TreeView_Root\", msoBarPopup, False, <span style=\"color:blue;\">True<\/span>)\r\n     <span style=\"color:blue;\">With<\/span> cbr\r\n         KontextmenueBaugruppen cbr, 0, \"r0\"\r\n         KontextmenueEinzelteile cbr, 0, \"r0\"\r\n         .ShowPopup\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 2: Kontextmen&uuml; f&uuml;r das Root-Element einblenden<\/span><\/b><\/p>\n<p>Danach folgt der Aufruf zweier weiterer Routinen, n&auml;mlich  <b>KontextmenueBaugruppen <\/b>und <b>KontextmenueEinzelteile<\/b>:<\/p>\n<pre>KontextmenueBaugruppen cbr, 0, \"r0\"\r\nKontextmenueEinzelteile cbr, 0, \"r0\"<\/pre>\n<p>Nach dem Anlegen der Kontextmen&uuml;-Elemente f&uuml;r die Baugruppen und die Einzelteile wird das Men&uuml; mit der <b>Popup<\/b>-Methode des <b>CommandBar<\/b>-Objekts eingeblendet.<\/p>\n<h2>Men&uuml;eintr&auml;ge f&uuml;r die Baugruppen<\/h2>\n<p>Die Prozedur <b>KontextmenueBaugruppen <\/b>(s. Listing 3) erwartet drei Parameter &#8211; einen Verweis auf das Kontextmen&uuml; (<b>cbr<\/b>), die ID des Elements, von dem aus das Kontextmen&uuml; aufgerufen wurde, sowie den Wert der <b>Key<\/b>-Eigenschaft dieses Elements.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KontextmenueBaugruppen(cbr<span style=\"color:blue;\"> As <\/span>CommandBar, <span style=\"color:blue;\">Optional<\/span> lngBaugruppeParentID<span style=\"color:blue;\"> As Long<\/span>, <span style=\"color:blue;\">Optional<\/span> ByVal strKey<span style=\"color:blue;\"> As String<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>cbp<span style=\"color:blue;\"> As <\/span>CommandBarPopup\r\n     <span style=\"color:blue;\">Dim <\/span>cbc<span style=\"color:blue;\"> As <\/span>CommandBarButton\r\n     <span style=\"color:blue;\">Dim <\/span>strIN<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> cbp = cbr.Controls.Add(msoControlPopup)\r\n     cbp.Caption = \"Baugruppen\"\r\n     <span style=\"color:blue;\">Set<\/span> cbc = cbp.Controls.Add(msoControlButton)\r\n     <span style=\"color:blue;\">With<\/span> cbc\r\n         .Caption = \"Neue Baugruppe\"\r\n         .OnAction = \"=NeueBaugruppe(0)\"\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">If <\/span>lngBaugruppeParentID = 0<span style=\"color:blue;\"> Then<\/span>\r\n         strWHERE = \" WHERE IstProdukt = FALSE\"\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         strWHERE = AuszuschliessendeBaugruppen(lngBaugruppeParentID)\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strWHERE) &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n             strWHERE = \" WHERE BaugruppeID NOT IN (\" & strIN & \")\"\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT BaugruppeID, Baugruppe FROM tblBaugruppen \" & strWHERE _\r\n         & \" ORDER BY Baugruppe\", dbOpenDynaset)\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> <span style=\"color:blue;\">Len<\/span>(strKey) = 0<span style=\"color:blue;\"> Then<\/span>\r\n         strKey = \", ''\" & strKey & \"''\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rst.EOF\r\n         <span style=\"color:blue;\">Set<\/span> cbc = cbp.Controls.Add(msoControlButton)\r\n         <span style=\"color:blue;\">With<\/span> cbc\r\n             .Caption = rst!Baugruppe\r\n             .OnAction = \"=BaugruppeHinzufuegen(\" & rst!BaugruppeID & \", \" & lngBaugruppeParentID & strKey & \")\"\r\n         End <span style=\"color:blue;\">With<\/span>\r\n         rst.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Untermen&uuml; f&uuml;r die Baugruppen hinzuf&uuml;gen<\/span><\/b><\/p>\n<p>Die Prozedur legt mit der Variablen <b>cbp<\/b> ein Steuerelement des Typs <b>msoControlPopup <\/b>an, also ein Untermen&uuml;, und weist diesem den Text <b>Baugruppen <\/b>hinzu. Das Untermen&uuml; soll einen Eintrag mit der Bezeichnung <b>Neue Baugruppe <\/b>sowie eine Auflistung der bisher verf&uuml;gbaren Baugruppen erhalten. Also legen wir zun&auml;chst eine einzelne Schaltfl&auml;che an (Typ <b>msoControlButton<\/b>), speichern diese in der Variablen <b>cbc <\/b>und weisen den Text <b>Neue Baugruppe <\/b>sowie f&uuml;r die <b>OnAction<\/b>-Eigenschaft den Wert <b>=NeueBaugruppe(0) <\/b>hinzu. Der Wert <b>0 <\/b>f&uuml;r den einzigen Parameter kennzeichnet, dass die Baugruppe in der obersten Ebene, also als Produkt angelegt werden soll &#8211; dazu sp&auml;ter mehr.<\/p>\n<p>Danach ermittelt die Prozedur die anzuzeigenden Baugruppen. Wenn die Funktion vom Root-Element aus aufgerufen wurde, sollen alle Elemente erscheinen, die noch nicht &uuml;ber das Feld <b>IstProdukte <\/b>als Produkt gekennzeichnet sind und ohnehin schon als Produkt-Elemente angezeigt werden.<\/p>\n<p>Wurde das Kontextmen&uuml; von einer Baugruppe aus aufgerufen, erzeugt die Prozedur mit Hilfe der Funktion <b>AuszuschliessendeBaugruppen <\/b>eine Liste der Baugruppen, die nicht angelegt werden k&ouml;nnen, da dies einen Zirkelbezug erzeugen w&uuml;rde. Mehr dazu weiter unten. Dies ergibt eine Liste wie <b>1, 2, 3<\/b>, die dann innerhalb der folgenden <b>If&#8230;Then<\/b>-Bedingung zu einer <b>WHERE<\/b>-Bedingung hinzugef&uuml;gt wird. Diese sieht anschlie&szlig;end etwa wie folgt aus:<\/p>\n<pre>WHERE BaugruppeID NOT IN (1, 2, 3)<\/pre>\n<p>Danach erstellen wir ein Recordset auf Basis der Tabelle <b>tblBaugruppen<\/b>, in der wir alle Baugruppen erfassen, die dem aktuellen Element untergeordnet werden k&ouml;nnen, ohne einen Zirkelbezug auszul&ouml;sen. Falls der Prozedur mit dem Parameter <b>strKey <\/b>ein Wert wie etwa <b>r0|b1|b2 <\/b>&uuml;bermittelt wurde, soll dieser auch in der Funktion als Parameter enthalten sein, die durch einen der nun anzulegenden Kontextmen&uuml;befehle aufgerufen wird. Dazu wandeln wir die Zeichenkette wie <b>r0|b1|b2 <\/b>in die Zeichenkette <b>, &#8220;r0|b1|b2&#8220; <\/b>um, damit wir diese in der folgenden Schleife leicht als Parameter zu der Funktion hinzuf&uuml;gen k&ouml;nnen.<\/p>\n<p>In einer <b>Do While<\/b>-Schleife durchlaufen wir nun alle Datens&auml;tze dieses Recordsets und legen f&uuml;r jeden Datensatz einen neuen Eintrag im Kontextmen&uuml; an. Die Eintr&auml;ge erhalten jeweils den Namen der Baugruppe als Beschriftung und einen Ausdruck f&uuml;r die Eigenschaft <b>OnAction<\/b>, der die Funktion <b>BaugruppeHinzufuegen <\/b>mit dem Prim&auml;rschl&uuml;sselwert des aktuellen Datensatzes als Parameter enth&auml;lt &#8211;  also beispielsweise folgenden Wert, wenn <b>strKey <\/b>leer war:<\/p>\n<pre>=BaugruppeHinzufuegen(123, 0)<\/pre>\n<p>Enthielt <b>strKey <\/b>hingegen einen Wert, lautet der Funktionsaufruf wie folgt:<\/p>\n<pre>=BaugruppeHinzufuegen(123, 0, ''r0|b1|b2'')<\/pre>\n<p>Das Ergebnis sieht dann etwa wie in Bild 2 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_02\/pic_1084_005.png\" alt=\"Anzeigen der Baugruppen im Kontextmen&uuml; f&uuml;r das Root-Element\" width=\"424,7115\" height=\"287,8844\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Anzeigen der Baugruppen im Kontextmen&uuml; f&uuml;r das Root-Element<\/span><\/b><\/p>\n<h2>Men&uuml;eintr&auml;ge f&uuml;r die Einzelteile<\/h2>\n<p>Die zweite von der Prozedur <b>KontextmenueRoot <\/b>aufgerufene Prozedur hei&szlig;t <b>KontextmenueEinzelteile <\/b>und sieht wie in Listing 4 aus. Diese Prozedur erwartet die gleichen drei Parameter wie die Prozedur <b>KontextmenueBaugruppen<\/b>.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>KontextmenueEinzelteile(cbr<span style=\"color:blue;\"> As <\/span>CommandBar, <span style=\"color:blue;\">Optional<\/span> lngBaugruppeParentID<span style=\"color:blue;\"> As Long<\/span>, <span style=\"color:blue;\">Optional<\/span> ByVal _\r\n         strKey<span style=\"color:blue;\"> As String<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>cbp<span style=\"color:blue;\"> As <\/span>CommandBarPopup\r\n     <span style=\"color:blue;\">Dim <\/span>cbc<span style=\"color:blue;\"> As <\/span>CommandBarButton\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> cbp = cbr.Controls.Add(msoControlPopup)\r\n     cbp.Caption = \"Einzelteile\"\r\n     <span style=\"color:blue;\">Set<\/span> cbc = cbp.Controls.Add(msoControlButton)\r\n     <span style=\"color:blue;\">With<\/span> cbc\r\n         .Caption = \"Neues Einzelteil\"\r\n         .OnAction = \"=NeuesEinzelteil(0)\"\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT EinzelteilID, Einzelteil FROM tblEinzelteile ORDER BY Einzelteil\", _\r\n         dbOpenDynaset)\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> <span style=\"color:blue;\">Len<\/span>(strKey) = 0<span style=\"color:blue;\"> Then<\/span>\r\n         strKey = \", ''\" & strKey & \"''\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rst.EOF\r\n         <span style=\"color:blue;\">Set<\/span> cbc = cbp.Controls.Add(msoControlButton)\r\n         <span style=\"color:blue;\">With<\/span> cbc\r\n             .Caption = rst!Einzelteil\r\n             .OnAction = \"=EinzelteilHinzufuegen(\" & rst!EinzelteilID & \", \" & lngBaugruppeParentID & strKey & \")\"\r\n         End <span style=\"color:blue;\">With<\/span>\r\n         rst.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><!--30percent--><\/p>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Untermen&uuml; f&uuml;r die Einzelteile hinzuf&uuml;gen<\/span><\/b><\/p>\n<p>Sie erstellt ein Untermen&uuml; mit der Beschriftung <b>Einzelteile <\/b>und speichert dieses in der Variablen <b>cbp<\/b>. Diesem f&uuml;gt sie nun eine einzelne Schaltfl&auml;che hinzu, welche den Text <b>Neues Einzelteil <\/b>und die Funktion <b>=NeuesEinzelteil(0)<\/b> besitzt. Danach erstellt die Prozedur ein Recordset auf Basis der Tabelle <b>tblEinzelteile <\/b>und durchl&auml;uft diese Datens&auml;tze genau wie bei den Bauteilen.<\/p>\n<p>Dabei erstellt sie f&uuml;r jeden Eintrag eine neue Schaltfl&auml;che im Kontextuntermen&uuml; und weist diesem als Bezeichnung den Wert des Feldes <b>Einzelteil <\/b>zu. Auch der Wert der Eigenschaft <b>OnAction <\/b>mit der Funktion, die beim Bet&auml;tigen dieses Kontextmen&uuml;-Eintrags ausgel&ouml;st werden soll, ist prinzipiell mit den von den Baugruppen bekannten Funktionen identisch &#8211; allein der Name der Funktion hei&szlig;t <b>EinzelteilHinzufuegen <\/b>und nicht <b>BaugruppeHinzufuegen<\/b>. Ein Beispiel f&uuml;r einen solchen Funktionsaufruf sieht so aus:<\/p>\n<pre>=EinzelteilHinzufuegen(65, 47, ''r0|b46|b47'')<\/pre>\n<p>Das resultierende Kontextmen&uuml; sieht wie in Bild 3 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_02\/pic_1084_006.png\" alt=\"Kontextmen&uuml; zum Hinzuf&uuml;gen eines Einzelteils zum Root-Element\" width=\"424,7115\" height=\"329,1515\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Kontextmen&uuml; zum Hinzuf&uuml;gen eines Einzelteils zum Root-Element<\/span><\/b><\/p>\n<h2>Kontextmen&uuml; f&uuml;r eine Baugruppe<\/h2>\n<p>Eine Baugruppe kann weitere Baugruppen und Einzelteile enthalten. Das Kontextmen&uuml; soll also auch die beiden Eintr&auml;ge zum Hinzuf&uuml;gen von Baugruppen und Einzelteilen enthalten. Au&szlig;erdem ben&ouml;tigen wir einen Eintrag, mit dem wir die aktuelle Baugruppe aus dem Produkt beziehungsweise der &uuml;bergeordneten Baugruppe entfernen k&ouml;nnen.<\/p>\n<p>Es wird schon hier offensichtlich: Wir ben&ouml;tigen Teile des zuvor definierten Kontextmen&uuml;s nochmal. Da wir komplett gleiche Code-Segmente nicht mehrfach in verschiedene Prozeduren schreiben wollen, um die Wartbarkeit zu erh&ouml;hen, haben wir die mehrfach verwendeten Teile einfach in eigene Routinen ausgegliedert &#8211; also beispielsweise in die Prozeduren <b>KontextmenueBaugruppen <\/b>und <b>KontextmenueEinzelteile<\/b>, die wir dann von den Prozeduren, in denen diese eigentlich zum Einsatz kommen, aufrufen.<\/p>\n<h2>Kontextmen&uuml; f&uuml;r Baugruppen<\/h2>\n<p>Soeben haben wir uns angesehen, wie das Kontextmen&uuml; f&uuml;r das Root-Element gef&uuml;llt wird. Das Kontextmen&uuml; f&uuml;r eine Baugruppe sieht nicht wesentlich anders aus. Es soll allerdings einen Eintrag zum Entfernen einer Baugruppe enthalten.<\/p>\n<p>Daher wollen wir nun auf &auml;hnliche Weise die Prozedur <b>KontextmenueBaugruppe <\/b>f&uuml;llen. Diese soll auch die beiden Untermen&uuml;s <b>Baugruppen <\/b>und <b>Einzelteile <\/b>enthalten sowie auch noch einen Unterpunkt, mit dem die aktuelle Baugruppe aus dem Projekt beziehungsweise aus der &uuml;bergeordneten Baugruppe entfernen werden kann (s. Bild 4).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_02\/pic_1084_007.png\" alt=\"Kontextmen&uuml; zum Entfernen einer Baugruppe\" width=\"499,6607\" height=\"277,824\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Kontextmen&uuml; zum Entfernen einer Baugruppe<\/span><\/b><\/p>\n<p>Die Prozedur <b>KontextmenueBaugruppe<\/b>, die von der Prozedur <b>objTreeView_MouseDown <\/b>aufgerufen wird, wenn der <b>Key<\/b> des angeklickten Elements im letzten Element den Buchstaben <b>b <\/b>enth&auml;lt (also zum Beispiel f&uuml;r <b>r0|e123<\/b>), sieht wie folgt aus:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>KontextmenueBaugruppe(lngID<span style=\"color:blue;\"> As Long<\/span>, _\r\n         <span style=\"color:blue;\">Optional<\/span> strKey<span style=\"color:blue;\"> As String<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>cbr<span style=\"color:blue;\"> As <\/span>CommandBar\r\n     <span style=\"color:blue;\">Dim <\/span>cbc<span style=\"color:blue;\"> As <\/span>CommandBarButton\r\n     On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n     CommandBars(\"TreeView_Baugruppe\").Delete\r\n     <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n     <span style=\"color:blue;\">Set<\/span> cbr = CommandBars.Add(\"TreeView_Baugruppe\", _\r\n         msoBarPopup, False, <span style=\"color:blue;\">True<\/span>)\r\n     <span style=\"color:blue;\">With<\/span> cbr\r\n         <span style=\"color:blue;\">Set<\/span> cbc = cbr.Controls.Add(msoControlButton)\r\n         <span style=\"color:blue;\">With<\/span> cbc\r\n             .Caption = \"Baugruppe entfernen\"\r\n             .OnAction = \"=BaugruppeEntfernen(\" _\r\n                 & lngID & \", ''\" & strKey & \"'')\"\r\n         End <span style=\"color:blue;\">With<\/span>\r\n         KontextmenueBaugruppen cbr, lngID, strKey\r\n         KontextmenueEinzelteile cbr, lngID, strKey\r\n         .ShowPopup\r\n     End <span style=\"color:blue;\">With<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Zus&auml;tzlich zum Aufruf der beiden Prozeduren <b>KontextmenueBaugruppen <\/b>und <b>KontextmenueEinzelteile <\/b>enth&auml;lt es zuvor noch ein paar Anweisungen, welche dem Kontextmen&uuml; im Untermen&uuml; noch einen Eintrag mit der Beschriftung <b>Baugruppe entfernen <\/b>zuweist.<\/p>\n<h2>Anlegen von Baugruppen<\/h2>\n<p>Bisher haben wir ja nur die Kontextmen&uuml;s besprochen, was in Anbetracht verschiedener Elemente, die dynamisch aus mehreren Tabellen zusammengestellt werden m&uuml;ssen, keine allzu triviale Aufgabe war. Nun k&uuml;mmern wir uns um die Funktionen, die durch einen Mausklick auf die zuvor erstellten Men&uuml;eintr&auml;ge aufgerufen werden. In der Tat ben&ouml;tigen wir hier auf jeden Fall Funktionen, mit Prozeduren gelingt dies nicht.<\/p>\n<p>Die erste Funktion hei&szlig;t <b>BaugruppeHinzuf&uuml;gen <\/b>(s. Listing 5) und wird immer dann ausgel&ouml;st, wenn der Benutzer einen der Eintr&auml;ge <b>Bau-grup-pen|[Bau-grup-pen-name]<\/b> des Kontextmen&uuml;s anklickt. Die Prozedur kann von den &uuml;ber die Prozeduren <b>KontextmenueRoot <\/b>und <b>KontextmenueBaugruppe <\/b>aus erstellten Kontextmen&uuml;s aus aufgerufen werden.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>BaugruppeHinzufuegen(lngBaugruppeID<span style=\"color:blue;\"> As Long<\/span>, <span style=\"color:blue;\">Optional<\/span> lngBaugruppeParentID<span style=\"color:blue;\"> As Long<\/span>, _\r\n         <span style=\"color:blue;\">Optional<\/span> strKey<span style=\"color:blue;\"> As String<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>strBaugruppe<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>intMenge<span style=\"color:blue;\"> As Integer<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngError<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     strBaugruppe = DLookup(\"Baugruppe\", \"tblBaugruppen\", \"BaugruppeID = \" & lngBaugruppeID)\r\n     intMenge = Nz(DLookup(\"Menge\", \"tblBaugruppenzuordnungen\", \"BaugruppeVaterID = \" & lngBaugruppeParentID _\r\n         & \" AND BaugruppeKindID = \" & lngBaugruppeID), 0)\r\n     intMenge = intMenge + 1\r\n     <span style=\"color:blue;\">If <\/span>lngBaugruppeParentID = 0<span style=\"color:blue;\"> Then<\/span>\r\n         db.Execute \"UPDATE tblBaugruppen SET IstProdukt = <span style=\"color:blue;\">True<\/span> WHERE BaugruppeID = \" & lngBaugruppeID, dbFailOnError\r\n         objTreeView.Nodes.Add \"r0\", tvwChild, \"r0|b\" & lngBaugruppeID, strBaugruppe, \"gearwheels\"\r\n         TreeViewFuellen_Rek db, lngBaugruppeID, \"r0|b\" & lngBaugruppeID\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n         db.Execute \"INSERT INTO tblBaugruppenzuordnungen(BaugruppeVaterID, BaugruppeKindID, Menge) VALUES(\" _\r\n             & lngBaugruppeParentID & \", \" & lngBaugruppeID & \", \" & intMenge & \")\", dbFailOnError\r\n         <span style=\"color:blue;\">If <\/span>Err.Number = 3022<span style=\"color:blue;\"> Then<\/span>\r\n             db.Execute \"UPDATE tblBaugruppenzuordnungen SET Menge = \" & intMenge & \" WHERE BaugruppeVaterID = \" _\r\n                 & lngBaugruppeParentID & \" AND BaugruppeKindID = \" & lngBaugruppeID, dbFailOnError\r\n             objTreeView.Nodes.Remove strKey & \"|b\" & lngBaugruppeID\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         TreeViewFuellen_Rek db, lngBaugruppeParentID, strKey\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: Eine Baugruppe hinzuf&uuml;gen<\/span><\/b><\/p>\n<p>Im ersten Fall kommt mit dem Parameter <b>lngBaugruppeParentID <\/b>der Wert <b>0 <\/b>und mit <b>strKey <\/b>eine leere Zeichenkette, weil das Kontextmen&uuml; in diesem Fall f&uuml;r das Root-Element erstellt wurde.  Anderenfalls enth&auml;lt <b>lngBaugruppeParentID <\/b>den Prim&auml;rschl&uuml;sselwert der &uuml;bergeordneten Baugruppe und <b>strKey <\/b>den Schl&uuml;ssel dieses Elements. Dies wird im weiteren Verlauf eine Rolle spielen.<\/p>\n<p>Die Funktion ermittelt zun&auml;chst einige Informationen &#8211; zum Beispiel den Namen der Baugruppe, die hinzugef&uuml;gt werden soll. Diesen liefert der Aufruf einer <b>DLookup<\/b>-Funktion mit dem Wert aus <b>lngBaugruppe <\/b>als Kriterium. Dieser kommt mit dem ersten Parameter der Funktion.<\/p>\n<p>Die n&auml;chste <b>DLookup<\/b>-Funktion liefert die aktuelle Menge des Bauteils unterhalb des &uuml;bergeordneten Bauteils. Diese findet sich in der Verkn&uuml;pfungstabelle <b>tblBaugruppenzuordnungen<\/b>, das Ergebnis landet in der Variablen <b>intMenge<\/b>. Warum dies Nun: Es kann ja sein, dass eine Baugruppe oder auch ein Einzelteil mehr als einmal innerhalb eines Produkts beziehungsweise einer Baugruppe vorkommt (genau genommen ist das sogar sehr wahrscheinlich). Wenn die Funktion vom Root-Element aus aufgerufen wurde, spielt die Menge keine Rolle, denn dann wird die Baugruppe als Produkt eingestellt (also als Baugruppe, deren Feld <b>IstProdukt <\/b>den Wert <b>True <\/b>hat). Wurde die Funktion hingegen von einer Baugruppe aus aufgerufen, kann die Anzahl gr&ouml;&szlig;er als eins sein, weshalb wir zuvor die Menge abfragen.<\/p>\n<p>In der aktuellen Fassung kann die Menge nur erh&ouml;ht werden, indem man eine Baugruppe oder ein Einzelteil ein weiteres Mal zur jeweils &uuml;bergeordneten Gruppe hinzuf&uuml;gt.<\/p>\n<p>Die Menge wird in jedem Fall um eins erh&ouml;ht. Sollte das Bauteil noch nicht unter dem &uuml;bergeordneten Bauteil vorhanden sein, wurde zuvor der Wert <b>0 <\/b>f&uuml;r <b>intMenge <\/b>ermittelt, die Menge lautet dann <b>1<\/b>.<\/p>\n<p>Nun folgt eine <b>If&#8230;Then<\/b>-Bedingung, die zwei F&auml;lle unterscheidet: <b>lngBaugruppeParentID <\/b>hat den Wert <b>0 <\/b>(Aufruf vom Root-Element) oder einen anderen Wert (Aufruf von einer Baugruppe). Im ersten Fall aktualisiert die Prozedur lediglich den Wert des Feldes <b>IstProdukt <\/b>f&uuml;r die hinzuzuf&uuml;gende Baugruppe auf <b>True<\/b>. Au&szlig;erdem f&uuml;gt die Prozedur die Baugruppe als neues Produkt mit der <b>Add<\/b>-Methode der <b>Nodes<\/b>-Auflistung zum &uuml;bergeordneten Element hinzu und legt dann mit der Funktion <b>TreeViewFuellen_Rek<\/b>, die im ersten Teil der Beitragsreihe beschrieben wird, auch noch die diesem Element untergeordneten Elemente an.<\/p>\n<p>Hat <b>lngBaugruppeParentID <\/b>einen anderen Wert als <b>0<\/b>, wurde die Funktion von einer Baugruppe aus aufgerufen. Dann f&uuml;gen wir einen Datensatz zur Tabelle <b>tblBaugruppenzuordnungen <\/b>hinzu, der die &uuml;bergeordnete Baugruppe und die hinzuzuf&uuml;gende Baugruppe vereint. Als Menge wird der Wert aus <b>intMenge <\/b>eingetragen.<\/p>\n<p>Sollte diese Kombination bereits in der Tabelle gespeichert sein, l&ouml;st dies den Fehler <b>3022<\/b> aus, der allerdings nicht behandelt wird, da wir die Fehlerbehandlung zuvor abgeschaltet haben. Stattdessen f&uuml;gen wir keinen neuen Datensatz zu dieser Tabelle hinzu, sondern aktualisieren den vorhandenen Datensatz mit dieser Kombination so, dass dieser die neue Anzahl aus der Variablen <b>intMenge <\/b>erh&auml;lt. In diesem Fall entfernen wir das vorhandene Element mit der <b>Remove<\/b>-Methode aus dem <b>TreeView<\/b>-Steuerelement. Anschlie&szlig;end rufen wir die bereits aus dem ersten Teil der Beitragsreihe bekannte Funktion <b>TreeViewFuellen_Rek <\/b>auf, welche nicht nur das <b>Baugruppe<\/b>-Element hinzuf&uuml;gt, sondern auch eventuell bereits darunter angelegte weitere Baugruppen und Einzelteile. Zur Erinnerung: Wir k&ouml;nnen ja jede Baugruppe und jedes Einzelteil beliebig oft in verschiedenen Produkten und Baugruppen unterbringen.<\/p>\n<h2>Einzelteil hinzuf&uuml;gen<\/h2>\n<p>Nun wollen wir auch noch daf&uuml;r sorgen, dass der Benutzer Einzelteile zur Baugruppe hinzuf&uuml;gen kann. Daf&uuml;r zeichnet die Funktion <b>EinzelteilHinzufuegen <\/b>verantwortlich (s. Listing 6). Diese ist prinzipiell wie die Funktion <b>BaugruppeHinzufuegen<\/b>. Sie unterscheidet sich im Wesentlichen dadurch, dass sie nur einzelne Elemente hinzuf&uuml;gt und keine Baugruppen, die wiederum aus weiteren Baugruppen und Einzelteilen bestehen k&ouml;nnen. Dementsprechend fallen hier die rekursiven Aufrufe weg.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>EinzelteilHinzufuegen(lngEinzelteilID<span style=\"color:blue;\"> As Long<\/span>, <span style=\"color:blue;\">Optional<\/span> lngBaugruppeParentID<span style=\"color:blue;\"> As Long<\/span>, _\r\n         <span style=\"color:blue;\">Optional<\/span> strKey<span style=\"color:blue;\"> As String<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>strEinzelteil<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>intMenge<span style=\"color:blue;\"> As Integer<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     strEinzelteil = DLookup(\"Einzelteil\", \"tblEinzelteile\", \"EinzelteilID = \" & lngEinzelteilID)\r\n     intMenge = Nz(DLookup(\"Menge\", \"tblBaugruppenEinzelteile\", \"BaugruppeID = \" & lngBaugruppeParentID _\r\n         & \" AND EinzelteilID = \" & lngEinzelteilID), 0)\r\n     intMenge = intMenge + 1\r\n     <span style=\"color:blue;\">If <\/span>lngBaugruppeParentID = 0<span style=\"color:blue;\"> Then<\/span>\r\n         db.Execute \"UPDATE tblEinzelteile SET IstProdukt = <span style=\"color:blue;\">True<\/span> WHERE EinzelteilID = \" & lngEinzelteilID, dbFailOnError\r\n         objTreeView.Nodes.Add \"r0\", tvwChild, \"r0|e\" & lngEinzelteilID, strEinzelteil & \"(\" & intMenge & \")\", \"gearwheel\"\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n         db.Execute \"INSERT INTO tblBaugruppenEinzelteile(BaugruppeID, EinzelteilID, Menge) VALUES(\" _\r\n             & lngBaugruppeParentID & \", \" & lngEinzelteilID & \", \" & intMenge & \")\", dbFailOnError\r\n         <span style=\"color:blue;\">If <\/span>Err.Number = 3022<span style=\"color:blue;\"> Then<\/span>\r\n             db.Execute \"UPDATE tblBaugruppenEinzelteile SET Menge = \" & intMenge & \" WHERE BaugruppeID = \" _\r\n                 & lngBau8gruppeParentID & \" AND EinzelteilID = \" & lngEinzelteilID, dbFailOnError\r\n             objTreeView.Nodes.Remove strKey & \"|e\" & lngEinzelteilID\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         objTreeView.Nodes.Add strKey, tvwChild, strKey & \"|e\" & lngEinzelteilID, strEinzelteil & \" (\" & intMenge \r\n             & \")\", \"gearwheel\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: Ein Einzelteil hinzuf&uuml;gen<\/span><\/b><\/p>\n<h2>Ausschlie&szlig;en von Zirkelbez&uuml;gen<\/h2>\n<p>In den meisten TreeView-Anwendungen kommt jeder Eintrag nur einmal vor. In der St&uuml;ckliste sind die Anforderungen etwas spannender: Hier kann es ja durchaus vorkommen, dass ein Einzelteil Bestandteil mehrerer Baugruppen ist. Das ist jedoch unkritisch. Kritisch wird es deshalb, weil ja auch Baugruppen Teil anderer Baugruppen sein k&ouml;nnen. Dummerweise kann es dann theoretisch auch geschehen, dass Baugruppe 1 eine Baugruppe 2 enth&auml;lt, der Benutzer aber versucht, der Baugruppe 2 nochmals die Baugruppe 1 unterzuordnen. Dies f&uuml;hrt dann nat&uuml;rlich zu einer unendlichen Rekursion, die sp&auml;testens dann endet, wenn die Prozeduren zum F&uuml;llen des TreeView aus Ressourcenmangel einen Fehler melden. Noch interessanter wird es, wenn der Benutzer Baugruppe 1 die Baugruppe 2 hinzuf&uuml;gt, der Baugruppe 2 die Baugruppe 3 und der Baugruppe 3 wieder die Baugruppe 1. Diese Art von Zirkelbezug erfordert eine spezielle Behandlung, die wir nachfolgend beschreiben.<\/p>\n<p>Wir m&uuml;ssen also sicherstellen, dass der Benutzer nur solche Baugruppen zu einer anderen Baugruppe zuordnen kann, welche die Zielbaugruppe nicht enthalten. Dies erfordert wiederum eine rekursive Funktion. Die Startfunktion und die rekursive Funktion finden Sie in Listing 7.<\/p>\n<pre><span style=\"color:blue;\">Private Function <\/span>AuszuschliessendeBaugruppen(lngID<span style=\"color:blue;\"> As Long<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strIN<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\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> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT * FROM tblBaugruppen\", dbOpenDynaset)\r\n     strIN = lngID\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rst.EOF\r\n         <span style=\"color:blue;\">If <\/span>AuszuschliessendeBaugruppen_Rek(db, lngID, rst!BaugruppeID) = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n             strIN = strIN & \", \" & rst!BaugruppeID\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         rst.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     AuszuschliessendeBaugruppen = strIN\r\n<span style=\"color:blue;\">End Function<\/span>\r\n<span style=\"color:blue;\">Private Function <\/span>AuszuschliessendeBaugruppen_Rek(db<span style=\"color:blue;\"> As <\/span>DAO.Database, lngCheckID<span style=\"color:blue;\"> As Long<\/span>, lngBaugruppeID<span style=\"color:blue;\"> As Long<\/span>)<span style=\"color:blue;\"> As Boolean<\/span>\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> rst = db.OpenRecordset(\"SELECT * FROM tblBaugruppenzuordnungen WHERE BaugruppeVaterID = \" & lngBaugruppeID, _\r\n         dbOpenDynaset)\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rst.EOF\r\n         <span style=\"color:blue;\">If <\/span>rst!BaugruppeKindID = lngCheckID<span style=\"color:blue;\"> Then<\/span>\r\n             AuszuschliessendeBaugruppen_Rek = <span style=\"color:blue;\">True<\/span>\r\n             <span style=\"color:blue;\">Exit Function<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         AuszuschliessendeBaugruppen_Rek = AuszuschliessendeBaugruppen_Rek(db, lngCheckID, rst!BaugruppeKindID)\r\n         rst.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     rst.Close\r\n     <span style=\"color:blue;\">Set<\/span> rst = Nothing\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 7: Ermittlung der m&ouml;glichen Baugruppen per Rekursion<\/span><\/b><\/p>\n<p>Die Funktion <b>AuszuschliessendeBaugruppen <\/b>erh&auml;lt die ID der zu pr&uuml;fenden Baugruppe als Parameter. Wenn Sie also etwa die <b>lngID<\/b> mit dem Wert <b>3 <\/b>&uuml;bergeben, dann soll die Funktion eine Liste aller Baugruppen zur&uuml;ckliefern, die selbst irgendwo unterhalb die Baugruppe mit der ID <b>3 <\/b>enthalten.<\/p>\n<p>Die Funktion erzeugt ein Recordset auf Basis der Tabelle <b>tblBaugruppen <\/b>und stellt die Variable <b>strIN<\/b>, welche die nicht zul&auml;ssigen IDs sammeln soll, direkt auf die Untergruppe selbst ein, denn eine Untergruppe darf nat&uuml;rlich auch nicht in sich selbst abgebildet werden.<\/p>\n<p> <b>strIn <\/b>enth&auml;lt also nun beispielsweise den Wert <b>123<\/b>.<\/p>\n<p>Danach durchl&auml;uft die Prozedur <b>AuszuschliessendeBaugruppen <\/b>alle Datens&auml;tze der Tabelle <b>tblBaugruppen <\/b>und pr&uuml;ft f&uuml;r jede, ob diese irgendwo in der Hierarchie ein untergeordnetes Element aufweist, das dem zu untersuchenden Element entspricht. Dazu ruft es die rekursive Funktion <b>AuszuschliessendeBaugruppen_Rek <\/b>auf, die weiter unten im Listing zu finden ist.<\/p>\n<p>Sollte diese den Wert <b>True <\/b>zur&uuml;ckliefern, ist die gesuchte Baugruppe schon der aktuell durchlaufenen Baugruppe untergeordnet und soll ebenfalls der Liste der auszuschlie&szlig;enden Baugruppen hinzugef&uuml;gt werden. In diesem Fall wird die ID durch ein Komma getrennt hinten an den bereits in <b>strIn <\/b>enthaltenen String angeh&auml;ngt, also zum Beispiel so: <b>123, 14<\/b>. Nach dem Durchlaufen aller Baugruppen der Tabelle <b>tblBaugruppen <\/b>wird der Ausdruck aus <b>strIn <\/b>als Funktionswert zur&uuml;ckgeliefert.<\/p>\n<p>Nun schauen wir uns an, was in der Funktion <b>AuszuschliessendeBaugruppen_Rek <\/b>geschieht. Die Funktion erwartet einen Verweis auf die aktuelle Datenbank (<b>db<\/b>), die ID der Baugruppe, f&uuml;r die das Kontextmen&uuml; zusammengestellt werden soll (<b>lngCheckID<\/b>) sowie die ID der zu untersuchenden Baugruppe (<b>lngBaugruppeID<\/b>).<\/p>\n<p>Sie erstellt wiederum ein Recordset, diesmal auf Basis der Tabelle <b>tblBaugruppenzuordnungen<\/b>. Hier werden alle Datens&auml;tze ermittelt, deren Feld <b>BaugruppeVaterID <\/b>mit der ID aus dem Parameter <b>lngBaugruppeID <\/b>&uuml;bereinstimmt. Wenn in der Schleife &uuml;ber alle gefundenen Datens&auml;tze der Wert des Feldes <b>BaugruppeKindID <\/b>mit dem Wert aus <b>CheckID <\/b>&uuml;bereinstimmt, weist das aktuell untersuchte Element das Element, f&uuml;r das der Kontextmen&uuml;-Eintrag erstellt werden soll, bereits als Kind-Element auf. Damit k&ouml;nnen wir den R&uuml;ckgabewert der Funktion <b>AuszuschliessendeBaugruppen-_Rek <\/b>auf <b>True <\/b>einstellen und die weitere Untersuchung dieses Zweiges beenden.<\/p>\n<p>Anderenfalls wird nun durch einen erneuten Aufruf der Funktion <b>AuszuschliessendeBaugruppen_Rek <\/b>das n&auml;chste Element in der Hierarchie dahingehend untersucht, ob eines der untergeordneten Elemente mit dem Element &uuml;bereinstimmt, f&uuml;r welches das Kontextmen&uuml; erstellt werden soll.<\/p>\n<p>Auf diese Weise durchl&auml;uft die rekursive Funktion alle Elemente der Tabelle <b>tblBaugruppen <\/b>und tr&auml;gt diejenigen in die Variable <b>strIn <\/b>ein, welche die Baugruppe, f&uuml;r die das Kontextmen&uuml; zusammengestellt werden soll, noch nicht in irgendeiner Hierarchieebene als Unterelement aufweisen.<\/p>\n<p>Mit dem Ergebnis wird dann, wie weiter oben beschrieben, eine <b>WHERE<\/b>-Bedingung zusammengesetzt, die nur die m&ouml;glichen Baugruppen f&uuml;r das Kontextmen&uuml; zur&uuml;ckliefert.<\/p>\n<h2>Beschriftung &auml;ndern<\/h2>\n<p>Wir haben gegen&uuml;ber der ersten Version aus dem ersten Teil der Beitragsreihe noch die Anzahl der Baugruppen und Einzelteile der Produkte hinzugef&uuml;gt und zeigen diese in Klammern hinter den Bezeichnungen im TreeView an. Aktuell lassen sich diese nur durch nochmaliges Hinzuf&uuml;gen erh&ouml;hen, aber im n&auml;chsten Teil der Beitragsreihe werden wir hierzu eine alternative M&ouml;glichkeit anbieten.<\/p>\n<p>Worauf wir hinauswollen: Nun stehen die Bezeichnungen mit der Menge des jeweiligen Elements im <b>TreeView<\/b>-Eintrag. Dadurch wird es nat&uuml;rlich schwierig, die Bezeichnung &uuml;ber die eingebauten Mechanismen zu &auml;ndern, bei denen man &uuml;blicherweise das Ereignis <b>AfterLabelEdit <\/b>des TreeView-Steuerelements nutzt, um vom Benutzer durchgef&uuml;hrte &auml;nderung an der Bezeichnung in der Tabelle zu speichern (s. Listing 8).<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>objTreeView_BeforeLabelEdit(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>strLabel<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strKey<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngID<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     strKey = objTreeView.SelectedItem.Key\r\n     strKey = <span style=\"color:blue;\">Mid<\/span>(strKey, <span style=\"color:blue;\">InStrRev<\/span>(strKey, \"|\") + 1)\r\n     lngID = <span style=\"color:blue;\">Mid<\/span>(strKey, 2)\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     Select Case <span style=\"color:blue;\">Left<\/span>(strKey, 1)\r\n         <span style=\"color:blue;\">Case <\/span>\"b\"\r\n             strLabel = Nz(DLookup(\"Baugruppe\", \"tblBaugruppen\", \"BaugruppeID = \" & lngID), \"\")\r\n             strLabel = InputBox(\"Geben Sie die neue Bezeichnung f&uuml;r die Baugruppe ein:\", \"Bezeichnung &auml;ndern\", strLabel)\r\n             <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strLabel) &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n                 db.Execute \"UPDATE tblBaugruppen SET Baugruppe = ''\" & strLabel & \"'' WHERE BaugruppeID = \" & lngID, _\r\n                     dbFailOnError\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">Case <\/span>\"e\"\r\n             strLabel = DLookup(\"Einzelteil\", \"tblEinzelteile\", \"EinzelteilID = \" & lngID)\r\n             strLabel = InputBox(\"Geben Sie die neue Bezeichnung f&uuml;r das Einzelteil ein:\", \"Bezeichnung &auml;ndern\", strLabel)\r\n             <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strLabel) &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n                 db.Execute \"UPDATE tblEinzelteile SET Einzelteil = ''\" & strLabel & \"'' WHERE EinzelteilID = \" _\r\n                     & lngID, dbFailOnError\r\n             <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">End Select<\/span>\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strLabel) &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n         objTreeView.SelectedItem.Text = strLabel\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     Cancel = <span style=\"color:blue;\">True<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 8: Diese Prozedur erm&ouml;glicht das &auml;ndern einer Bezeichnung<\/span><\/b><\/p>\n<p>Dies ist im vorliegenden Fall problematisch, weil der Benutzer ja per Mausklick auf den markierten Eintrag oder <b>F2 <\/b>nicht den tats&auml;chlichen Namen des Elements &auml;ndert, sondern den Namen plus der in Klammern angegebenen Menge (s. Bild 5).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_02\/pic_1084_008.png\" alt=\"Herk&ouml;mmliches &auml;ndern eines Eintrags\" width=\"424,7115\" height=\"196,7414\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Herk&ouml;mmliches &auml;ndern eines Eintrags<\/span><\/b><\/p>\n<p>Wenn der Benutzer den Eintrag &auml;ndern will, sollte er nicht die Bezeichnung plus die Menge editieren m&uuml;ssen, sondern nur die reine Bezeichnung. Also zeigen wir mithilfe der Ereignisprozedur, die vor dem Bearbeiten der Bezeichnung ausgel&ouml;st wird (<b>BeforeLabelEdit<\/b>) eine <b>InputBox <\/b>an, welche die tats&auml;chliche Bezeichnung enth&auml;lt.<\/p>\n<p>Diese Prozedur finden Sie in Bild 6. Sie liefert genau einen Parameter namens <b>Cancel<\/b>, mit dem Sie einstellen k&ouml;nnen, ob die &auml;nderung tats&auml;chlich durchgef&uuml;hrt werden soll. Die Prozedur ermittelt den Schl&uuml;ssel des zu &auml;ndernden Elements und liest daraus den Buchstaben f&uuml;r das Element und den Wert des Prim&auml;rschl&uuml;sselwertes des entsprechenden Datenbankeintrags aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_02\/pic_1084_009.png\" alt=\"Eintrag &auml;ndern per InputBox\" width=\"499,6607\" height=\"276,2502\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Eintrag &auml;ndern per InputBox<\/span><\/b><\/p>\n<p>Die ID landet in der Variablen <b>lngID<\/b>, den Buchstaben verwendet die Prozedur gleich in einer <b>Select Case<\/b>-Bedingung weiter. Hier unterscheidet die Prozedur zwischen den beiden Buchstaben <b>b <\/b>(f&uuml;r Baugruppe) und <b>e <\/b>(f&uuml;r Einzelteil).<\/p>\n<p>Im Falle einer Baugruppe liest die Prozedur die tats&auml;chliche Bezeichnung des Elements ohne die Menge per <b>DLookup <\/b>aus der Tabelle <b>tblBaugruppen <\/b>aus und speichert diese in der Variablen <b>strLabel<\/b>. Damit ruft sie die <b>InputBox<\/b>-Funktion auf, die den Inhalt von <b>strLabel <\/b>als Standardwert erh&auml;lt. Ist <b>strLabel <\/b>nach der Eingabe des Benutzers leer, was darauf hindeutet, dass er entweder die <b>InputBox <\/b>geleert und auf <b>OK <\/b>geklickt oder dass er direkt auf die <b>Abbrechen<\/b>-Schaltfl&auml;che geklickt hat.<\/p>\n<p>Ist <b>strLabel <\/b>nicht leer, speichert die Prozedur den neuen Wert im entsprechenden Datensatz der zugrunde liegenden Datenbank.<\/p>\n<p>Prinzipiell der gleiche Ablauf erfolgt, wenn es sich beim zu &auml;ndernden Element um ein Einzelteil handelt. Der Unterschied ist dass der zu &auml;ndernde Datensatz aus der Tabelle <b>tblEinzelteile <\/b>stammt.<\/p>\n<p>Nach dem &auml;ndern des Eintrags in der Datenbank wollen wir die &auml;nderungen auch auf die Bezeichnung des Eintrags &uuml;bertragen. Dazu stellen wir einfach die <b>Label<\/b>-Eigenschaft des markierten Elements auf den Inhalt der Variablen <b>strLabel <\/b>ein. Au&szlig;erdem, und das ist wichtig, stellen wir den Wert des Parameters <b>Cancel <\/b>auf <b>True <\/b>ein, damit das Bezeichnungsfeld nach dem &auml;ndern nicht den Bearbeiten-Status annimmt.<\/p>\n<h2>Neue Baugruppen und neue Einzelteile<\/h2>\n<p>Die Kontextmen&uuml;s enthalten jeweils einen Eintrag namens <b>Baugruppen|Neue Baugruppe<\/b>. Diese l&ouml;st eine Funktion aus, die prinzipiell mit den bereits im ersten Teil der Beitragsreihe vorgestellten Ereignisprozeduren der Schaltfl&auml;chen zum Anlegen einer neuen Baugruppe &uuml;bereinstimmt. Gleiches gilt f&uuml;r den Eintrag <b>Einzelteile|Neues Einzelteil<\/b>.<\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Im zweiten Teil haben wir einige neue Funktionen zum TreeView hinzugef&uuml;gt. Im n&auml;chsten Teil kommen wir noch zu den Kontextmen&uuml;eintr&auml;gen zum L&ouml;schen von Elementen. Au&szlig;erdem f&uuml;gen wir die M&ouml;glichkeit hinzu, Eintr&auml;ge per Drag and Drop im TreeView zu bewegen.<\/p>\n<h3>Downloads zu diesem Beitrag<\/h3>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>StuecklistenII.mdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/6C41B325-A59A-4A45-8F23-A438A5E65C56\/aiu_1084.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Was lange w&auml;hrt, wird endlich gut: Hier kommt der zweite Teil zu der vor einigen Ausgaben begonnenen Reihe zum Thema St&uuml;cklisten. Nachdem das TreeView-Steuerelement zur Anzeige und zum Bearbeiten von Baugruppen und Teilen steht, erweitern wir die L&ouml;sung nun um Kontextmen&uuml;-Eintr&auml;ge, mit denen Sie Baugruppen und Elemente hinzuf&uuml;gen, bearbeiten und entfernen k&ouml;nnen.<\/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":[66022017,662017,44000027],"tags":[],"class_list":["post-55001084","post","type-post","status-publish","format-standard","hentry","category-66022017","category-662017","category-Loesungen"],"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>St&uuml;cklisten, Teil II - 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\/Stuecklisten_Teil_II\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"St&uuml;cklisten, Teil II\" \/>\n<meta property=\"og:description\" content=\"Was lange w&auml;hrt, wird endlich gut: Hier kommt der zweite Teil zu der vor einigen Ausgaben begonnenen Reihe zum Thema St&uuml;cklisten. Nachdem das TreeView-Steuerelement zur Anzeige und zum Bearbeiten von Baugruppen und Teilen steht, erweitern wir die L&ouml;sung nun um Kontextmen&uuml;-Eintr&auml;ge, mit denen Sie Baugruppen und Elemente hinzuf&uuml;gen, bearbeiten und entfernen k&ouml;nnen.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Stuecklisten_Teil_II\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2020-05-14T13:42:21+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg09.met.vgwort.de\/na\/5ce066e88ff44a04b0fbe795e10a84e8\" \/>\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\\\/Stuecklisten_Teil_II\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Stuecklisten_Teil_II\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"St&uuml;cklisten, Teil II\",\"datePublished\":\"2020-05-14T13:42:21+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Stuecklisten_Teil_II\\\/\"},\"wordCount\":4004,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Stuecklisten_Teil_II\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/5ce066e88ff44a04b0fbe795e10a84e8\",\"articleSection\":[\"2\\\/2017\",\"2017\",\"L\u00f6sungen\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Stuecklisten_Teil_II\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Stuecklisten_Teil_II\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Stuecklisten_Teil_II\\\/\",\"name\":\"St&uuml;cklisten, Teil II - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Stuecklisten_Teil_II\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Stuecklisten_Teil_II\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/5ce066e88ff44a04b0fbe795e10a84e8\",\"datePublished\":\"2020-05-14T13:42:21+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Stuecklisten_Teil_II\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Stuecklisten_Teil_II\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Stuecklisten_Teil_II\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/5ce066e88ff44a04b0fbe795e10a84e8\",\"contentUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/5ce066e88ff44a04b0fbe795e10a84e8\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Stuecklisten_Teil_II\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"St&uuml;cklisten, Teil II\"}]},{\"@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":"St&uuml;cklisten, Teil II - 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\/Stuecklisten_Teil_II\/","og_locale":"de_DE","og_type":"article","og_title":"St&uuml;cklisten, Teil II","og_description":"Was lange w&auml;hrt, wird endlich gut: Hier kommt der zweite Teil zu der vor einigen Ausgaben begonnenen Reihe zum Thema St&uuml;cklisten. Nachdem das TreeView-Steuerelement zur Anzeige und zum Bearbeiten von Baugruppen und Teilen steht, erweitern wir die L&ouml;sung nun um Kontextmen&uuml;-Eintr&auml;ge, mit denen Sie Baugruppen und Elemente hinzuf&uuml;gen, bearbeiten und entfernen k&ouml;nnen.","og_url":"https:\/\/access-im-unternehmen.de\/Stuecklisten_Teil_II\/","og_site_name":"Access im Unternehmen","article_published_time":"2020-05-14T13:42:21+00:00","og_image":[{"url":"http:\/\/vg09.met.vgwort.de\/na\/5ce066e88ff44a04b0fbe795e10a84e8","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\/Stuecklisten_Teil_II\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Stuecklisten_Teil_II\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"St&uuml;cklisten, Teil II","datePublished":"2020-05-14T13:42:21+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Stuecklisten_Teil_II\/"},"wordCount":4004,"commentCount":1,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Stuecklisten_Teil_II\/#primaryimage"},"thumbnailUrl":"http:\/\/vg09.met.vgwort.de\/na\/5ce066e88ff44a04b0fbe795e10a84e8","articleSection":["2\/2017","2017","L\u00f6sungen"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Stuecklisten_Teil_II\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Stuecklisten_Teil_II\/","url":"https:\/\/access-im-unternehmen.de\/Stuecklisten_Teil_II\/","name":"St&uuml;cklisten, Teil II - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Stuecklisten_Teil_II\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Stuecklisten_Teil_II\/#primaryimage"},"thumbnailUrl":"http:\/\/vg09.met.vgwort.de\/na\/5ce066e88ff44a04b0fbe795e10a84e8","datePublished":"2020-05-14T13:42:21+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Stuecklisten_Teil_II\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Stuecklisten_Teil_II\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/Stuecklisten_Teil_II\/#primaryimage","url":"http:\/\/vg09.met.vgwort.de\/na\/5ce066e88ff44a04b0fbe795e10a84e8","contentUrl":"http:\/\/vg09.met.vgwort.de\/na\/5ce066e88ff44a04b0fbe795e10a84e8"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Stuecklisten_Teil_II\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"St&uuml;cklisten, Teil II"}]},{"@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\/55001084","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=55001084"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001084\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001084"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001084"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001084"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}