Stücklisten, Teil I

Wenn Sie ein Abonnement des Magazins 'Access im Unternehmen' besitzen, können Sie sich anmelden und den kompletten Artikel lesen.
Anderenfalls können Sie das Abonnement hier im Shop erwerben.

Stücklisten, Teil I

Stücklisten dienen dazu, die Einzelteile eines Objekts strukturiert darzustellen. Strukturiert bedeutet dabei, dass die Einzelteile jeweils zu Baugruppen zusammengefasst werden. Ein Objekt besteht also aus Baugruppen und/oder Einzelteilen, wobei jede Baugruppe wieder aus Baugruppen und/oder Einzelteilen besteht – bis am Ende jede Baugruppe in ihre Einzelteile aufgelöst ist. Einzelteile sind die kleinsten Einheiten in der Stückliste, sie können nicht weiter aufgeteilt werden.Dieser Beitrag soll zeigen, wie Sie Stücklisten mit Access verwalten. Dabei spielt das TreeView-Steuerelement eine große Rolle.

Die Stückliste kann dazu für verschiedene Zwecke genutzt werden: Sie können daraus zum Beispiel entnehmen, welche Mengen der verschiedenen Einzelteile benötigt werden. Dies ist sowohl für die Beschaffung der benötigten Einzelteile wichtig als auch für die Kalkulation der Materialkosten.

Datenmodell

Das Datenmodell der Lösung sieht im Überblick wie in Bild 1 aus. Die Tabelle tblBaugruppen enthält alle Baugruppen, für die in unserem einfach gehaltenen Ansatz nur der Name angegeben werden soll. Die Tabelle enthält lediglich die beiden Felder BaugruppeID als Primärschlüsselfeld sowie das Feld Baugruppe zur Eingabe einer Bezeichnung für die Baugruppe. Diese Tabelle kommt noch ein zweites Mal in der Übersicht des Datenmodells vor. Dies hat den Hintergrund, dass Sie jeder Baugruppe weitere Baugruppen unterordnen können. Eine Baugruppe besteht ja nicht direkt lediglich aus Einzelteilen, sondern kann auch andere aus Einzelteilen oder auch Baugruppen zusammengesetzte Baugruppen enthalten.

Übersicht des Datenmodells für die Stückliste

Bild 1: Übersicht des Datenmodells für die Stückliste

Die Abbildung zeigt die Tabelle tblBaugruppen ein zweites Mal, damit wir die Beziehung zwischen zwei Baugruppen abbilden können.

Verknüpfung zwischen Baugruppen

Dies geschieht nämlich durch die Verknüpfungstabelle tblBaugruppenzuordnungen (s. Bild 2). Sie enthält die Felder BaugruppenzuordnungID (als Primärschlüsselfeld), BaugruppeVaterID zur Angabe der übergeordneten Baugruppe aus der Tabelle tblBaugruppen, das Feld BaugruppeKindID zur Angabe der untergeordneten Baugruppe sowie das Feld Menge.

Entwurf der Tabelle tblBaugruppenzuordnungen

Bild 2: Entwurf der Tabelle tblBaugruppenzuordnungen

Es kann ja auch sein, dass eine Baugruppe gleich mehrmals einer anderen Baugruppe zugeordnet werden soll (zum Beispiel vier Reifen zu einem Auto). Die gewünschte Anzahl tragen Sie dann in das Feld Menge ein.

Für dieses Feld legen wir außerdem den Wert 1 als Standardwert fest – dieser muss dann beim Anlegen gegebenenfalls angepasst werden.

Außerdem soll die Tabellen einen aus den beiden Feldern BaugruppeVaterID und BaugruppeKindID zusammengesetzten eindeutigen Index besitzen. Auf diese Weise verhindern wir, dass eine Kombination aus zwei Baugruppen mehrfach angelegt wird – zu diesem Zweck haben wir ja das Feld Menge hinzugefügt.

Weiter im Datenmodell: Neben den Baugruppen gibt es eine Tabelle mit den Daten der Einzelteile. Sie heißt tblEinzelteile und ist in Bild 3 abgebildet.

Entwurf der Tabelle tblEinzelteile

Bild 3: Entwurf der Tabelle tblEinzelteile

Wo sind die Projekte?

Irgendwann führen alle Baugruppen und/oder Einzelteile zu einem vollständigen Projekt oder Produkt. Warum also gibt es in der Datenbank keine Tabelle für solche Elemente? Wenn Sie möchten, können Sie natürlich eine solche hinzufügen. Allerdings ist schon jede einzelne Baugruppe ein Produkt für sich, und eigentlich auch jedes Einzelteil. Machen wir es uns also einfach und fügen einfach den beiden Tabellen tblBaugruppen und tblEinzelteile ein Ja/Nein-Feld namens IstProdukt hinzu, mit dem Sie festlegen können, ob ein solches Element nicht nur Bestandteil einer Baugruppe sein kann, sondern gleichzeitig ein Produkt. Wozu aber benötigen wir solche eine Unterscheidung? Weil wir auf irgendeine Weise kennzeichnen wollen, welche Elemente als Hauptelemente im TreeView zur Darstellung des Aufbaus von Bauteilen dienen sollen.

Beziehung zwischen Baugruppen und Einzelteilen

Um die Einzelteile den Baugruppen zuzuordnen, benötigen wir wiederum eine Verknüpfungstabelle zur Herstellung einer m:n-Beziehung. Diese heißt tblBaugruppenEinzelteile und enthält das Primärschlüsselfeld BaugruppeEinzelteilID, das Fremdschlüsselfeld BaugruppeID zum Festlegen der Baugruppe aus der Tabelle tblBaugruppen, das Fremdschlüsselfeld EinzelteilID zum Festlegen des Einzelteils aus der Tabelle tblEinzelteile und das Feld Menge zur Angabe der Anzahl der Einzelteile.

Auch für das Feld Menge dieser Tabelle stellen wir 1 als Standardwert ein, außerdem legen wir für die beiden Felder BaugruppeID und EinzelteilID einen zusammengesetzten, eindeutigen Index fest (s. Bild 4).

Entwurf der Tabelle tblBaugruppenEinzelteile

Bild 4: Entwurf der Tabelle tblBaugruppenEinzelteile

Die Tabelle tblEinzelteilKategorien schließlich enthält verschiedene Kategorien für die Einzelteile. Diese Tabelle hat den Hinterrgrund, dass vielleicht später einmal die Einzelteile verschiedener Kategorien unterschiedlich rabattiert werden – gegebenenfalls auch noch in Abhängigkeit vom jeweiligen Kunden. Die Tabelle enthält die beiden Felder EinzelteilKategorieID (als Primärschlüsselfeld) und Einzelteilkategorie.

Die Tabelle tblEinzelteile ist über das Feld EinzelteilkategorieID mit dieser Tabelle verknüpft.

Darstellung der Baugruppen und Einzelteile

Die Lösung soll verschiedene Darstellungen ermöglichen. Zunächst einmal wollen wir eine einfache Möglichkeit zur Eingabe der Einzelteile schaffen. Dazu legen Sie ein neues Formular namens frmEinzelteile an, das ein Unterformular sfmEinzelteile enthält. Das Unterformular soll alle Einzelteile enthalten, das Hauptformular das aktuell im Unterformular ausgewählte Einzelteil. Das Unterformular statten wir außerdem mit einer Möglichkeit zum Filtern nach verschiedenen Kriterien aus.

In der Entwurfsansicht sieht das Unterformular wie in Bild 5 aus. Das Feld EinzelteilkategorieID haben wir über den Kontextmenü-Eintrag Ändern zu|Kombinationsfeld in ein Kombinationsfeld umgewandelt, damit die Einträge der Tabelle tblEinzelteilkategorien direkt nachgeschlagen werden können. Dazu haben wir die Eigenschaft Datensatzherkunft auf den folgenden Ausdruck eingestellt:

Aufbau des Unterformulars sfmEinzelteile

Bild 5: Aufbau des Unterformulars sfmEinzelteile

SELECT EinzelteilkategorieID, Einzelteilkategorie FROM tblEinzelteilkategorien
ORDER BY Einzelteilkategorie;

Die Eigenschaften Spaltenanzahl und Spaltenbreiten erhalten die Werte 2 und 0cm. Das Formular erhält für die Eigenschaft Standardansicht den Wert Datenblatt.

Haupt- und Unterformular synchronisieren

Damit Haupt- und Unterformular die gleichen Datensätze markieren, muss beim Laden des Hauptformulars die folgende Prozedur ausgeführt werden. Diese stellt das Recordset des Hauptformulars auf das Recordset des Unterformulars ein:

Private Sub Form_Load()
    Set Me.Recordset =  Me!sfmEinzelteile.Form.Recordset
End Sub

Einzelteilkategorie auswählen

Damit neben der Auswahl der Datensätze der Tabelle tblEinzelteilkategorien im Hauptformular nicht nur die bestehenden Einträge gewählt, sondern auch direkt neue Einträge eingefügt werden können, fügen Sie dem Kombinationsfeld eine Prozedur hinzu, die durch das Ereignis Bei nicht in Liste ausgelöst wird (s. Listing 1).

Private Sub cboEinzelteilkategorieID_NotInList(NewData As String, Response As Integer)
    Dim db As DAO.Database
    If MsgBox("Kategorie '" & NewData & "' anlegen?", vbYesNo) = vbYes Then
        Set db = CurrentDb
        db.Execute "INSERT INTO tblEinzelteilkategorien(Einzelteilkategorie) VALUES('"
            & NewData & "')", dbFailOnError
        Response = acDataErrAdded
    End If
End Sub

Listing 1: Hinzufügen neuer Einträge in die Tabelle tblEinzelteilkategorien über das Kombinationsfeld

Diese Prozedur fragt den Benutzer bei der Eingabe eines noch nicht vorhandenen Wertes, ob ein neuer Eintrag in der Tabelle tblEinzelteilkategorien angelegt werden soll. Falls ja, legt die Prozedur den Datensatz gleich mit einer entsprechenden SQL-INSERT INTO-Anweisung an. Auf diese Weise sparen wir uns an dieser Stelle ein extra Formular für die Verwaltung der Kategorien (dieses würde notwendig, wenn Sie einmal Kategorien ändern oder löschen wollen).

Baugruppen verwalten

Für die Verwaltung der Baugruppen wollen wir ein ähnliches Formular wie für die Einzelteile anlegen. Wir benötigen also wieder ein Hauptformular, diesmal namens frmBaugruppen, sowie ein Unterformular namens sfmBaugruppen.

Allerdings weist die Tabelle tblBaugruppen im aktuellen Zustand mit Baugruppe und IstProdukt ohnehin nur zwei Felder mit Geschäftsdaten auf. Wir müssen also nicht unbedingt ein Hauptformular mit den Detaildaten und ein Unterformular mit einer Listenansicht erstellen. Eine Liste im Unterformular reicht völlig aus, das Hauptformular soll nur weitere Steuerelemente wie etwa die OK-Schaltfläche nachliefern. Im Entwurf sieht das Formular wie in Bild 6 aus.

Haupt- und Unterformular zur Darstellung von Baugruppen

Bild 6: Haupt- und Unterformular zur Darstellung von Baugruppen

Wenn Sie nun in die Formularansicht des Formulars frmBaugruppen wechseln, erwartet Sie der Anblick auf Bild 7.

Bearbeiten von Baugruppen in der Formularansicht mit einem Unterformular in der Datenblattansicht

Bild 7: Bearbeiten von Baugruppen in der Formularansicht mit einem Unterformular in der Datenblattansicht

Baugruppen und Einzelteile im TreeView

Nun wenden wir uns interessanteren Themen zu: zum Beispiel dem Formular frm­StuecklisteTreeView, welches ein TreeView-Steuerelement zur hierarchischen Anzeige der Baugruppen und Einzelteile enthält.

Statten Sie dazu ein neues Formular namens frmStuecklisteTreeView zunächst mit einem TreeView-Steuerelement namens ctlTreeView sowie mit einer Schaltfläche namens cmdOK aus.

Stellen Sie die Eigenschaften Horizontaler Anker und Vertikaler Anker für das TreeView-Steuerelement jeweils auf Beide ein (s. Bild 8). Für die Eigenschaft Vertikaler Anker der Schaltfläche cmdOK legen Sie den Wert Unten fest.

Verankern des TreeView-Steuerelements, sodass es sowohl vertikal als auch horizontal angepasst wird

Bild 8: Verankern des TreeView-Steuerelements, sodass es sowohl vertikal als auch horizontal angepasst wird

Auf diese Weise passen sich TreeView-Steuerelement und Schaltfläche an die Ausmaße des Formulars an – allerdings stehen diese Eigenschaften erst ab Access 2007 zur Verfügung. Sollten Sie noch eine ältere Access-Version nutzen – dies wäre ein Grund zum Wechseln.

Legen Sie außerdem ein ImageList-Steuerelement an, denn wir wollen die Einträge des TreeView-Steuerelements mit entsprechenden Icons etwas aufhübschen. Dieses Steuerelement soll den Namen ctlImageList erhalten.

ImageList-Steuerelement füllen

Das ImageList-Steuerelement müssen wir zunächst mit den benötigten Bildern füllen. Wir benötigen eines für das Root-Element des TreeView-Steuerelements, eines für ein Projekt – in diesem Fall ein Hierarchie-Icon, eines für eine Baugruppe (hier durch mehrere Zahnräder abgebildet) sowie ein Icon für ein Einzelteil, hier ein einzelnes Zahnrad. Damit diese wie in Bild 9 in das ImageList-Steuerelement gelangen, fügen Sie entweder selbst geeignete Bilder hinzu oder erledigen dies per VBA-Code.

Hinzufügen der benötigten Icons für das TreeView-Steuerelement

Bild 9: Hinzufügen der benötigten Icons für das TreeView-Steuerelement

In unserem Fall war das notwendig, da das ImageList-Steuerelement nicht mit dem Bildformat zurechtkam. Also lesen wir die Bilder erst in ein StdPicture-Objekt ein und speichern diese dann im ImageList-Steuerelement.

Eine einfache Prozedur, um dies zu erledigen, finden Sie im Modul mdl­ImageListFuellen beziehungsweise in Listing 2.

Public Sub ImageListFuellen(strFormular As String, strImageList As String, _
        ParamArray strImages() As Variant)
    Dim objPicture As stdole.StdPicture
    Dim strImage As String
    Dim strImageName As String
    Dim objNode As MSComctlLib.Node
    Dim objImageList As MSComctlLib.ImageList
    Dim i As Integer
    Dim frm As Form
    DoCmd.OpenForm strFormular, acDesign
    Set frm = Forms(strFormular)
    Set objImageList = frm(strImageList).Object
    For i = LBound(strImages) To UBound(strImages)
        strImage = strImages(i)
        strImageName = Mid(strImage, InStrRev(strImage, "") + 1)
        strImageName = Left(strImageName, InStrRev(strImageName, ".") - 1)
        Set objPicture = mdlOGL0713.LoadPictureGDIP(strImage)
        objImageList.ListImages.Add , strImageName, objPicture
    Next i
    DoCmd.Close acForm, frm.Name
End Sub

Listing 2: Prozedur zum Füllen des ImageList-Steuerelements mit Bilddateien

Der Aufruf dieser Prozedur sieht etwa wie folgt aus:

ImageListFuellen  "frmStuecklisteTreeView",  "ctlImageList",  "c:gearwheel.png", _
"c:gearwheels.png",  "c:home.png",  "c:elements_hierarchy.png"

Die Prozedur erwartet den Namen des Formulars, in dem sich das ImageList-Steuerelement befindet, sowie den Namen des ImageList-Steuerelements als Parameter. Außerdem können Sie eine durch Kommata getrennte Liste der hinzuzufügenden Elemente angeben.

Die Prozedur ImageListFuellen öffnet das Formular im Entwurf, referenziert das ImageList-Steuerelement und durchläuft alle Elemente der Bilderliste. Dabei liest sie erst den Pfad in die Variable strImage ein und ermittelt daraus den reinen Dateinamen ohne Verzeichnis und Dateiendung. Dann erstellt sie ein StdPicture-Objekt auf Basis des in der Datei gespeicherten Bildes und fügt dieses mit der Add-Methode der ListImages-Auflistung in das ImageList-Steuerelement ein. Schließlich schließt sie das Formular und speichert dieses gleichzeitig.

Achtung: Die per VBA hinzugefügten Bilder bleiben nur dauerhaft im ImageList-Steuerelement erhalten, wenn die Prozedur das Formular in der Entwurfsansicht öffnet – in der Formularansicht gelingt dies nicht. Deswegen müssen zur Laufzeit hinzugefügte Bilder auch bei jedem Öffnen des Formulars neu hinzugefügt werden – außer, Sie möchten das Formular zur Laufzeit in der Entwurfsansicht öffnen, was zum Beispiel mit der Runtime oder mit .mde/.accde-Datenbanken nicht möglich ist.

Laden des Formulars

Nun erstellen Sie eine Ereignisprozedur, die durch das Ereignis Beim Laden des Formulars ausgelöst wird. Dieses soll die Eigenschaften des TreeView-Steuerelements einstellen, das ImageList-Steuerelement zuweisen und eine Prozedur aufrufen, die das TreeView-Steuerelement mit den Daten füllt.

In Listing 3 finden Sie zunächst die Deklaration zweier Variablen zum Referenzieren der beiden Steuerelemente ctlTreeView und ctlImageList.

Dim WithEvents objTreeView As MSComctlLib.TreeView
Dim objImageList As MSComctlLib.ImageList
Private Sub Form_Load()
     Set objTreeView = Me!ctlTreeView.Object
     Set objImageList = Me!ctlImageList.Object
     With objTreeView
         .Appearance = ccFlat
         .BorderStyle = ccNone
         .ImageList = objImageList
         .LineStyle = tvwTreeLines
         .OLEDragMode = ccOLEDragAutomatic
         .OLEDropMode = ccOLEDropManual
         .Style = tvwTreelinesPlusMinusPictureText
     End With

Dies war die Leseprobe dieses Artikels.
Melden Sie sich an, um auf den vollständigen Artikel zuzugreifen.

Bitte geben Sie die Zeichenfolge in das nachfolgende Textfeld ein

Die mit einem * markierten Felder sind Pflichtfelder.

Ich habe die Datenschutzbestimmungen zur Kenntnis genommen.