Bilder und binäre Dateien
Transcription
Bilder und binäre Dateien
11 Bilder und binäre Dateien Probleme mit dem Speichern von Dateien und insbesondere von Bildern in Dateien gehören in den Newsgroups und Foren zum täglichen Leben. Access bietet mit dem Datentyp OLE-Objekt die Möglichkeit, auch Dateien in einer Tabelle zu speichern. Leider gab es bisher in Access keine eingebaute einfache Funktion, um Dateien ohne Weiteres in einem solchen Feld abzulegen – zumindest keine, die nicht früher oder später die Größe der Datenbankdatei explodieren ließ. Mit Access 2007 kam die Wende: Microsoft hat die Access-Gemeinde erhört und nicht nur einen neuen Felddatentyp namens Anlage, sondern auch noch ein neues Steuerelement zum Anzeigen von Bildern in Formularen und Berichten spendiert. Und dabei können Sie im Anlage-Feld sogar Dateien speichern, und sogar mehrere pro Datensatz. Access 2010 schließlich bringt weitere Verbesserungen wie etwa eine Systemtabelle zum Speichern von Bildern, die innerhalb der Anwendung verwendet werden können. Und mit unserem Standardmodul Kapitel 11 Bilder und binäre Dateien zum Verwenden von Bilddateien in allen Teilen einer Access-Anwendung machen Sie aus der langweiligen Benutzeroberfläche mit Office 97-Charm endlich eine Software, deren Bedienung auch für die Augen des Benutzers eine Freude ist. In diesem Kapitel lernen Sie aber nicht nur, wie das Anlage-Feld und das passende Steuerelement funktionieren, sondern auch alternative Techniken kennen. Außerdem finden Sie einige VBAModule, die wichtige Techniken für den Umgang mit Bildern und binären Daten liefern. Die Module stammen aus der Feder von Sascha Trowitzsch, der auch bei der Erstellung der Beispiele maßgeblich beteiligt war. Die Beispiele in diesem Kapitel drehen sich ausschließlich um Bilder, weil dies wohl der primäre Einsatzbereich für das Speichern von Dateien und binären Daten in Tabellen ist. Tatsächlich können Sie die Techniken, die sich nicht explizit auf Bilder beziehen, auch für das Speichern und Wiederherstellen von normalen Dateien verwenden. Ein Einsatzzweck abseits der bunten Welt der Bilder ist etwa das Speichern von Dateien, die Sie für die Ausführung einer Datenbankanwendung benötigen, und das Wiederherstellen solcher Dateien zur Laufzeit. So können Sie zum Beispiel für den Ablauf notwendige DLL-Dateien mitliefern, ohne dass der Benutzer sich mit zusätzlichen Dateien herumschlagen muss. BEISPIELDATENBANK Die Beispieldatei zu diesem Kapitel finden Sie unter dem Namen BilderUndBinaere Dateien.accdb auf www.acciu.de/aeb2010. 11.1 Bilder für Formulare und Berichte Access 2010 bietet ein neues Feature: Sie können im Entwurf von Formularen und Berichten Bilder in der Datenbank speichern, die dann in Formularen und Berichten verwendet werden können. Das Hinzufügen erfolgt beispielsweise über den entsprechenden Ribbon-Eintrag in der Entwurfsansicht (siehe Abbildung 11.1). Das Feature ist natürlich vor allem für die Kompatibilität mit Webdatenbanken gedacht. Die zentral gespeicherten und von Formularen, Berichten und Steuerelementen aus referenzierbaren Bilder lassen sich schließlich wesentlich leichter auf einen Webserver transferieren als eine Mischung aus eingebetteten, verknüpften und in Tabellen gespeicherten Bildern. Sollten Sie planen, eine Access-Datenbank mit Access 2010 zu entwickeln und diese auch unter Access 2007 einzusetzen, müssen Sie die Bilder auf eine der anderen in diesem Kapitel besprochenen Arten speichern. Unter Access 2007 werden über die Bildergalerie eingebundene Bilder nicht angezeigt. 642 Bilder für Formulare und Berichte Abbildung 11.1: Hinzufügen von Bildern zu einer Datenbank Wenn Sie ein Bild zur Bildergalerie hinzugefügt haben, können Sie dieses auf verschiedene Arten verwenden: »» Wählen Sie das Bild aus der Galerie aus und klicken Sie an eine freie Stelle im Formular. Access legt ein neues Bildsteuerelement an und stellt dessen Eigenschaft Bild auf den Namen der Bilddatei ohne Dateiendung ein. »» Oder ändern Sie das in einem Bildsteuerelement angezeigte Bild, indem Sie seine Eigenschaft Bild anklicken und über das Kombinationsfeld eines der in der Bildergalerie gespeicherten Bilder auswählen. Auf die gleiche Weise verfahren Sie mit weiteren Steuerelementen, die über die Bild-Eigenschaft verfügen. Bilder zur Bildergalerie hinzufügen Wenn Sie bereits Bilder für Schaltflächen, Bildsteuerelemente et cetera festgelegt haben, können Sie auch diese in die Bildgalerie der Anwendung überführen. Dazu wählen Sie das betroffene Steuerelement aus und stellen für die Eigenschaft Bildtyp den Wert Freigegeben ein (siehe Abbildung 11.2). Access fügt das Bild zur Bildergalerie hinzu und gibt diesem den Namen des Steuerelements, aus dem das Bild stammt. Hier ist also noch ein wenig Nacharbeit erforderlich. Leider hat Microsoft dies nicht optimal gelöst: Sie können die Bezeichnungen von Bildern in der Bildergalerie zwar über den entsprechenden Kontextmenüeintrag ändern, aber der neue Name wird nicht automatisch in die betroffenen Steuerelemente übernommen. Bilder aktualisieren Sie können jedoch ein in der Bildergalerie gespeichertes Bild durch ein anderes Bild ersetzen. Dazu verwenden Sie den Kontextmenüeintrag Aktualisieren des jeweiligen Elements der Bildergalerie. Access zeigt hier auch gleich das aktuelle Bild im Formular oder Bericht an. 643 Kapitel 11 Bilder und binäre Dateien Abbildung 11.2: Hinzufügen eines eingebetteten Bildes zur Bildergalerie Bilder aus anderen Datenbanken Wenn Sie ein Formular oder einen Bericht aus einer anderen Datenbank importieren, werden die darin verwendeten Bilder ebenfalls zur aktuellen Datenbank hinzugefügt. Im Hintergrund Die zur Bildergalerie hinzugefügten Bilddateien landen in einer zwar ausgeblendeten, aber ungeschützten Systemtabelle namens MSysResources. Diese enthält ein AnlagenFeld sowie einige weitere Felder zum Speichern des Namens und der Endung der Bild datei (siehe Abbildung 11.3). Weiter unten unter »Mehrere Bilder gleichzeitig zur Bildergalerie hinzufügen« ab Seite 655 stellen wir eine Funktion vor, mit der Sie mehr als eine Datei gleichzeitig zu dieser Tabelle hinzufügen können. Sie können damit beispielsweise ein komplettes IconVerzeichnis einlesen und brauchen dies nicht für jedes Icon einzeln zu erledigen. Bilder per VBA zur Bildergalerie hinzufügen Das Access-Objektmodell bietet eine neue Methode des Objekts CurrentProject beziehungsweise CodeProject, mit dem Sie per VBA schnell eine Bilddatei zur Bildergalerie hinzufügen können. Die Methode heißt AddSharedImage und verwendet zwei Parameter: »» SharedImageName: Name, der in der Bildergalerie angezeigt werden soll »» Filename: Name der Quelldatei Der folgende Aufruf fügt beispielsweise eine im gleichen Verzeichnis gespeicherte Datei namens cats.jpg unter den Namen Katzen zur Bildergalerie hinzu: CurrentProject.AddSharedImage "Katzen", CurrentProject.Path & "\cats.jpg" 644 Bilder und Dateien als Anlage speichern Abbildung 11.3: Die Tabelle MSysResources speichert die Bilder der Bildergalerie Auf die gespeicherten Resourcen greifen Sie dann über die Auflistung Resources der Objekte CurrentProject beziehungsweise CodeProject zu. Die folgenden Anweisungen geben beispielsweise alle Resourcen mit Name und Typ aus: Dim objSharedResource As SharedResource For Each objSharedResource In CurrentProject.SharedResources With objSharedResource Debug.Print .Name, .Type End With Next objSharedResource Die Eigenschaft Type kann die folgenden beiden Werte annehmen: »» acResourceTheme (0): Schema »» acResourceImage (1): Bilddatei Sie können die Elemente der Bildergalerie auch löschen, und zwar mit der Delete-Me thode des jeweiligen SharedResource-Objekts. 11.2 Bilder und Dateien als Anlage speichern Die einfachste und naheliegendste Möglichkeit zum Speichern von Bildern bietet der neue Datentyp Anlage. Abbildung 11.4 zeigt eine Tabelle mit einem Anlage-Feld in der Entwurfsansicht. In Aktion sieht dieser neue Felddatentyp wie in Abbildung 11.5 aus. Statt eines Feldnamens zeigt die Tabelle ein Büroklammer-Symbol an, das Sie aber durch die Angabe eines Wertes für die Eigenschaft Bezeichnung überschreiben können. Im Feld selbst sehen Sie das gleiche Symbol, gefolgt von einer in Klammern eingefassten Zahl. Diese gibt die Anzahl der enthaltenen Anhänge an. Klicken Sie doppelt auf das Feld, erscheint der Dialog Anlagen, mit dem Sie Folgendes erledigen können: »» Anlagen hinzufügen »» Anlagen entfernen 645 Kapitel 11 Bilder und binäre Dateien »» Eine Anlage öffnen »» Eine Anlage aus dem Anlage-Feld auf der Festplatte speichern »» Alle Anlagen auf der Festplatte speichern Abbildung 11.4: Eine Tabelle mit einem Anlage-Feld in der Entwurfsansicht Abbildung 11.5: Das Anlage-Feld in Aktion Diese Aktionen sind sämtlich selbsterklärend, weshalb das Buch an dieser Stelle nicht weiter darauf eingeht. Als Hinweis nur dies: Als Anlage können Sie beliebige Dateien aufnehmen, bis auf jene, die aus Sicherheitsgründen verboten sind und von Access blockiert werden. Dabei ist ausschließlich die Dateiendung von Belang. Welche Endungen das sind, erfahren Sie in der Onlinehilfe von Access unter Anlagen|Anfügen von Da teien|Referenzinformationen zu Anlage|Blockierte Dateiformate. Wie Sie später erfahren werden, lässt sich diese Limitierung mit VBA-Tricks umgehen (siehe »Importieren von Dateien in Anlage-Felder« ab Seite 653). 646 Bilder und Dateien als Anlage speichern Eigenschaften des Anlage-Steuerelements Das neue Anlage-Steuerelement finden Sie in der Entwurfsansicht von Formularen und Berichten im Ribbon Entwurf|Steuerelemente|Anlage (Büroklammer). Neben den üblichen Eigenschaften, die auch andere Steuerelemente besitzen, sind die folgenden erwähnenswert: »» Standardbild (VBA: DefaultPicture): Laden Sie über diese Eigenschaft eine Bilddatei, die das Steuerelement standardmäßig anzeigen soll. Die Eigenschaft wirkt sich immer dann aus, wenn das Anlage-Feld des angezeigten Datensatzes leer ist – also auch beim Anlegen eines neuen Datensatzes. »» Hintergrundart (BackStyle): Kann die Werte Normal (1) oder Transparent (0) annehmen. Wenn Transparenz eingestellt ist, scheint der Formularhintergund durch transparente Bereiche von Bildern (32bit) hindurch. »» Ereignis On Attachment Current (AttachmentCurrent): Das Ereignis wird ausgelöst, wenn per Kontextmenü oder mit dem beim Klicken des Bildes erscheinenden Navi gationsbar auf eine andere Anlage des Datensatzes geschaltet wird. Das ist nützlich, weil damit etwa der Dateiname des aktuell angezeigten Bildes wie im folgenden Lis ting in einem Bezeichnungsfeld oder einem anderen Steuerelement angezeigt werden kann: Private Sub Bildanlage_AttachmentCurrent() Me!lblFilename.Caption = Me!Bildanlage.FileName End Sub Die folgenden Eigenschaften stehen nur unter VBA zur Verfügung: »» FileName: Dateiname der aktuell angezeigten Anlage (schreibgeschützt) »» FileData: Binärdaten der aktuell angezeigten Anlage (Byte-Array, ebenfalls schreibgeschützt) »» AttachmentCount: Anzahl der im Datensatz gespeicherten Anlagen »» CurrentAttachment: Index der aktuell angezeigten Anlage »» PictureDisp (versteckte schreibgeschützte Eigenschaft): Gibt bei Bildanhängen das Picture-Objekt des aktuell angezeigten Bildes zurück. Dieses können Sie dann zur weiteren Verarbeitung in VBA verwenden. »» Back, Forward: Mit diesen Methoden schalten Sie per VBA zwischen den einzelnen Anhängen eines Datensatzes um. Unter Zuhilfenahme der Eigenschaften Attachment Count und CurrentAttachment können Sie diese Methoden dazu verwenden, um über selbst gebaute Navigationsschaltflächen zwischen den Bildern eines Datensatzes zu navigieren. 647 Kapitel 11 Bilder und binäre Dateien »» SizeToFit: Die Methode weist das Anlage-Feld an, sich den Ausmaßen des angezeigten Bildes anzupassen. Stellen Sie die Eigenschaft AutoHeight des Detailbereichs außerdem auf True, dann passt Access auch die Formulargröße dem Bild an. 11.3 Bilder aus Anlage-Feldern in Formularen anzeigen In Formularen zeigt sich das neue Anlage-Steuerelement genauso benutzerfreundlich wie in der Datenblattansicht von Tabellen. Sie brauchen für ein jungfräuliches Formular nur eine Tabelle mit einem Anlage-Feld als Datensatzquelle auszuwählen und das Feld Bildanlage dieser Tabelle in den Formularentwurf zu ziehen (siehe Abbildung 11.6). Mit diesem Entwurf erhalten Sie beispielsweise die Formularansicht aus Abbildung 11.7. Abbildung 11.6: Hinzufügen des Primärschlüssels und des Anlage-Feldes zu einem Formular Abbildung 11.7: Über das Kontextmenü des Anlage-Feldes können Sie den Anlagen-Dialog öffnen oder, wenn mehrere Anlagen vorhanden sind, darin blättern 648 Bilder aus Anlage-Feldern in Formularen a nzeigen Es geht aber auch anders als im ersten Beispiel: Vielleicht möchten Sie einmal direkt durch alle im Anlage-Feld verschiedener Datensätze enthaltenen Bilder blättern. Dann ziehen Sie nicht das komplette Anlage-Feld, sondern die in der dahinter verborgenen Tabelle enthaltenen Felder aus der Feldliste in den Formularentwurf (siehe Abbildung 11.8). Das Ergebnis sieht ähnlich aus (siehe Abbildung 11.9), unterscheidet sich aber in zwei wesentlichen Punkten: »» Sie können direkt im Formular durch alle Bilder blättern, egal, ob mehrere Bilder im Anlage-Feld eines einzigen Datensatzes verborgen sind. »» Sie können so nicht auf den Anlage-Dialog zugreifen. Abbildung 11.8: Entwurf eines Formulars mit Anlage-Feld Abbildung 11.9: Ein Bild im Anlage-Steuerelement in einem Formular 649 Kapitel 11 Bilder und binäre Dateien Sie müssen beachten, dass Access einen Datensatz mit mehreren Bildern hier wie zwei verknüpfte Tabellen behandelt: Eine Tabelle mit den Basisdaten (also den Daten aus den Feldern, die nicht die Anlage enthalten) und eine Tabelle mit den im Anlage-Feld enthaltenen Dateien. Diese sind über eine 1:n-Beziehung miteinander verknüpft. Wenn die oben verwendete Tabelle tblBildanlagen also im ersten Datensatz zwei Bildanlagen enthält, zeigt das Formular zwei Datensätze mit den gleichen Basisdaten, aber unterschiedlichen Anlagen beziehungsweise Anlage-Informationen an. 11.4 Bilder aus Anlage-Feldern in Berichten anzeigen In Berichten funktioniert dies genauso einfach: Sie stellen die passende Tabelle als Datensatzquelle des Berichts ein und fügen das Anlage-Feld selbst zum Bericht hinzu. Das Ergebnis überzeugt nicht – zumindest nicht in der Seitenansicht: Der Bericht zeigt nur das jeweils erste Bild zu jedem Datensatz an (siehe Abbildung 11.10). Abbildung 11.10: Berichte zeigen nur je ein Bild eines Anlage-Feldes pro Datensatz an, auch wenn dieses mehrere Bilder enthält Wenn Sie alle Bilder der enthaltenen Datensätze in der Seitenansicht des Berichts ausgeben möchten, fügen Sie wiederum das dem eigentlichen Anlage-Feld untergeordne- 650 Bilder aus Anlage-Feldern in Berichten a nzeigen te <Name des Anlage-Felds>.FileData-Feld zum Berichtsentwurf hinzu. Auch hier zeigt Access die Grunddaten von Datensätzen, die mehr als ein Bild enthalten, jeweils doppelt an (siehe Abbildung 11.11). Dem können Sie entgegenwirken, indem Sie die Eigenschaft Duplikate ausblenden für solche Steuerelemente, die je Datensatz nur einfach angezeigt werden sollen, auf Ja einstellen und – falls diese Steuerelemente sich allein in einer Zeile befinden – mit dem Wert Ja für die Eigenschaft Verkleinerbar dafür sorgen, dass keine unschönen Lücken entstehen. Abbildung 11.11: Alle Bilder eines Anlage-Felds in der Berichtsansicht eines Berichts Komfortabler geht dies mit einer Gruppierung nach dem Primärschlüssel der Tabelle mit dem Anlage-Feld. Das kann im Entwurf etwa so wie in Abbildung 11.12 aussehen. Dass die Berichtsansicht so wie in Abbildung 11.13 aussieht, ist ein weiteres Indiz dafür, dass Access die Anlagen intern in einer verborgenen Tabelle speichert. Abbildung 11.12: Entwurf eines nach dem Primärschlüssel der Datensatzquelle gruppierten Be richts mit Bildanlagen ... 651 Kapitel 11 Bilder und binäre Dateien 11.5 Bilder und Dateien aus Anlage-Feldern auf der Festplatte speichern Das Speichern von Anlagen aus dem Anlage-Feld ist äußerst trivial: Sie öffnen einfach den Anlagen-Dialog per Doppelklick auf das jeweilige Feld im Formular und führen dort die erforderlichen Schritte aus. Das funktioniert aber leider nur, wenn Sie das AnlageFeld wie in der ersten in »Bilder aus Anlage-Feldern in Formularen anzeigen« ab Seite 648 beschriebenen Methode anlegen. Abbildung 11.13: ... der in der Seitenvorschau so aussieht 11.6 Dateien per VBA in Anlage-Felder importieren und exportieren Natürlich können Sie Bilder und Dateien auch per VBA in einem Anlage-Feld speichern und von dort aus wieder ins Dateisystem exportieren. BEISPIELDATENBANK Die Prozeduren dieses Abschnitts finden Sie im Modul mdlBLOBs0710 der Beispiel datenbank DAO2010.accdb auf www.acciu.de/aeb2010. 652 Dateien per VBA in Anlage-Felder importieren und exportieren 11.6.1 Importieren von Dateien in Anlage-Felder Die nachfolgend vorgestellte Funktion können Sie universell für das Importieren beliebiger Dateien in Anlage-Felder verwenden. Sie erwartet die folgenden Parameter: »» strFileName: Verzeichnis und Name der zu importierenden Datei »» strTable: Name der Tabelle, in der sich das Anlage-Feld befindet »» strFieldAttach: Name des Anlage-Feldes »» boolEdit: Gibt an, ob die Anlage in einem bestehenden Datensatz gespeichert werden soll »» strIDField: Name des Primärschlüsselfeldes der Tabelle »» varID: Wert des Primärschlüsselfeldes für den Datensatz, zu dem eine Anlage hinzugefügt werden soll »» strAttachment: Name des Attachments, das gegebenenfalls überschrieben werden soll Wenn Sie eine Datei in einem neuen Datensatz speichern möchten, verwenden Sie etwa diesen Aufruf: StoreBLOB0710 CurrentProject.Path & "\Bilder_01.tif", "tblAnlagen", "Anlagen" Zum Anfügen einer Datei in einem vorhandenen Datensatz setzen Sie diese Anweisung ab – wobei 2 der Primärschlüsselwert des Zieldatensatzes ist: StoreBLOB0710 CurrentProject.Path & "\Bilder_01.tif", "tblAnlagen", "Anlagen", True, "AnlageID", 2 Schließlich können Sie auch ein Attachment mit einem bestimmten Dateinamen in der Tabelle überschreiben. Auch dies erfordert die Angabe des Datensatzes und zusätzlich den Namen des bereits vorhandenen Attachments: StoreBLOB0710 CurrentProject.Path & "\Bilder_02.tif", "tblAnlagen", "Anlagen", True, "AnlageID", 2, "Bilder_01.tif" Die Routine hat übrigens noch ein weiteres Feature, das eine völlig unverständliche Eigenschaft von Anlage-Feldern ausschaltet: Und zwar dürfen nur Dateien mit bestimmten Dateiendungen importiert werden. Und das ist wörtlich zu nehmen: Der Inhalt der Datei spielt keine Rolle, Sie brauchen die Datei nur vor dem Speichern im Anlage-Feld umzubenennen und dies anschließend wieder rückgängig zu machen. Und genau das macht diese Funktion auch, indem sie den beim Speichern einer Datei mit einer nicht erlaubten Dateiendung auftretenden Fehler entsprechend behandelt. 653 Kapitel 11 Bilder und binäre Dateien Der Zugriff auf die Anlagen erfolgt mit DAO. Der grundlegende Ablauf ist, dass Sie zunächst ein Recordset-Objekt auf Basis der Tabelle mit dem Anlage-Feld erstellen und ein weiteres auf Basis der Value-Eigenschaft des Anlage-Feldes. Letzteres muss unbedingt ein Recordset2-Objekt sein, ebenso wie das Field-Objekt mit dem Anlage-Feld die neue Version Field2 braucht. Die Prozedur sieht wie folgt aus: Function StoreBLOB0710(strFilename As String, strTable As String, µ strFieldAttach As String, Optional boolEdit As Boolean, µ Optional strIDField As String, Optional varID As Variant, µ Optional strAttachment As String) As Boolean Dim fld2 As DAO.Field2 Dim rstDAO As DAO.Recordset2 Dim rstACCDB As DAO.Recordset2 On Error GoTo ErrHandler Set rstDAO = CurrentDb.OpenRecordset("SELECT * FROM [" & strTable & "]", µ dbOpenDynaset) If boolEdit Then If IsNull(varID) Then Err.Raise vbObjectError + 1, , µ "Keine Datensatz-ID angegeben!" rstDAO.FindFirst "CStr([" & strIDField & "])='" & CStr(varID) & "'" If rstDAO.NoMatch Then Err.Raise vbObjectError + 2, , µ "Datensatz mit ID " & varID & " nicht gefunden!" rstDAO.Edit Else rstDAO.AddNew End If Set rstACCDB = rstDAO(strFieldAttach).Value If boolEdit Then If rstACCDB.EOF Then 'Fall 1: Es gibt noch keine Anlagen; > neue Anlage rstACCDB.AddNew Else Do While Not rstACCDB.EOF rstACCDB.MoveNext Loop rstACCDB.FindFirst "[FileName]='" & strAttachment & "'" 'Fall2: Es gibt keine Anlage mit dem Namen in sAttachment: > neue Anlage 'Fall3: Anlage gefunden; dann editieren If rstACCDB.NoMatch Then rstACCDB.AddNew Else rstACCDB.Edit End If Else 654 Dateien per VBA in Anlage-Felder importieren und exportieren rstACCDB.AddNew End If Set fld2 = rstACCDB.Fields!FileData On Error Resume Next fld2.LoadFromFile (strFilename) If Err.Number = -2146697202 Then 'Unerlaubte Dateiendung! Spezialbehandlung... On Error GoTo ErrHandler Name strFilename As strFilename & ".dat" 'Datei mit Endung ".dat" anfügen fld2.LoadFromFile (strFilename & ".dat") 'Datei laden Name strFilename & ".dat" As strFilename 'Umbenennung rückgängig machen 'Anlagename setzen rstACCDB.Fields!FileName = Mid(strFilename, InStrRev(strFilename, "\") + 1 rstACCDB.Update Else On Error GoTo ErrHandler rstACCDB.Update End If rstDAO.Update StoreBLOB0710 = True 'Rückgabe True = Alles ok. Finally: On Error Resume Next rstACCDB.Close rstDAO.Close Set rstACCDB = Nothing Set rstDAO = Nothing Set fld2 = Nothing Exit Function ErrHandler: MsgBox Err.Description, vbCritical Resume Finally End Function Listing 11.1: Flexibles Speichern von Dateien im Anlage-Feld Mehrere Bilder gleichzeitig zur Bildergalerie hinzufügen Die Bildergalerie von Access 2010, die wir weiter oben vorgestellt haben, hat einen entscheidenden Nachteil: Es lassen sich nicht mal eben mehrere Bilder gleichzeitig hinzufügen. Das ist insbesondere ärgerlich, weil der Dialog zur Auswahl eines Bildes immer wieder den Ordner der aktuellen Datenbank anzeigt. Mit der obigen Funktion und einer kleinen Erweiterung fügen Sie schnell beliebig viele Bilder eines Verzeichnisses zur Bildergalerie hinzu. Dazu brauchen Sie zunächst eine Funktion, die einen Dateidialog 655 Kapitel 11 Bilder und binäre Dateien zur Auswahl der hinzuzufügenden Dateien öffnet und für jede ausgewählte Datei eine weitere Funktion aufruft: Public Sub BilderEinfuegen() Dim objFiledialog As FileDialog Dim obj As Object Dim i As Integer Set objFiledialog = Application.FileDialog(msoFileDialogFilePicker) With objFiledialog .AllowMultiSelect = True .Filters.Add "Images", "*.png; *.gif; *.jpg; *.jpeg", 1 .Show For i = 1 To .SelectedItems.count BildEinfuegen .SelectedItems.Item(i) Next i End With End Sub Die zweite Funktion erwartet schlicht den Namen der einzulesenden Bilddatei. Sie verwendet dann die oben vorgestellte Prozedur StoreBLOB0710, um das Bild in einem neuen Datensatz der Tabelle MSysResources zu speichern. Zu unserem Glück fehlen noch ein paar weitere Felder, nämlich Extension, Name und Type. Um die entsprechenden Werte zum soeben angelegten Datensatz hinzuzufügen, ermittelt die Funktion zunächst die ID des zuletzt angelegten Datensatzes. Die folgenden Anweisungen lesen dann den Namen und die Erweiterung aus der kompletten Pfadangabe aus. Diese Informationen landen dann schließlich per Execute-Anweisung in der Tabelle MSysResource: Public Function BildEinfuegen(strBild As String) Dim db As DAO.Database Dim lngID As Long Dim strName As String Dim strFilename As String Dim strExtension As String Set db = CurrentDb StoreBLOB0710 strBild, "MSysResources", "Data", False lngID = db.OpenRecordset("SELECT @@IDENTITY").Fields(0) strFilename = Mid(strBild, InStrRev(strBild, "\") + 1) strName = Split(strFilename, ".")(0) strExtension = Split(strFilename, ".")(1) 656 Dateien per VBA in Anlage-Felder importieren und exportieren db.Execute "UPDATE MSysResources SET Extension = '" & strExtension & "', _ Name = '" & strName & "', Type = 'img' WHERE id = " & lngID, dbFailOnError End Function 11.6.2 Exportieren von Dateien aus dem Anlage-Feld Für den umgekehrten Weg gibt es ebenfalls eine passende und universell einsetzbare Funktion. Der Grund, warum die Datei wesentlich kompakter ist als die zum Importieren von Dateien, ist folgender: Die Routine greift direkt mit einer geeigneten SQL-Abfrage auf die in der Untertabelle des Anlage-Feldes enthaltenen Felder zu. Die Parameter sind weitgehend mit denen der vorherigen Funktion identisch. Auch der Export mit der Me thode SaveToFile prüft die Dateiendung, weshalb auch hier ein Workaround eingebaut ist. Ein Beispielaufruf sieht etwa so aus: RestoreBLOB0710 "tblAnlagen", "Anlagen", "AnlageID", 20, µ CurrentProject.Path & "\CatsExportiert.jpg", "cats.jpg" Die Funktion ist wie folgt aufgebaut: Function RestoreBLOB0710(strTable As String, strFieldAttach As String, µ strIDField As String, varID As Variant, strFilename As String, µ Optional strAttachment As String = "*") As Boolean Dim rstDAO As DAO.Recordset Dim rstACCDB As DAO.Recordset2 On Error GoTo ErrHandler Set rstACCDB = CurrentDb.OpenRecordset("SELECT [" & strFieldAttach & "].FileData µ FROM " & strTable & " WHERE [" & strIDField & "]=" & varID & " AND [" µ & strFieldAttach & "].FileName LIKE '" & strAttachment & "'", dbOpenSnapshot) If rstACCDB.EOF Then Err.Raise vbObjectError + 3, "RestoreBLOB0710", "Das Anlagefeld ist leer" End If If Dir(strFilename) <> "" Then Kill strFilename DoEvents End If On Error Resume Next 'Fehlerbehandlung ausschalten, da nachfolgende Zeile 'Fehler bei blockierten Dateiendungen erzeugt rstACCDB(0).SaveToFile strFilename If Err.Number = (-2146697202) Then 'Spezialbehandlung: 'Datei wird mit Endung .dat versehen, was erlaubte Endung ist 'Anschließend wird wiederhergestellte Datei wieder korrekt umbenannt 657 Kapitel 11 Bilder und binäre Dateien rstACCDB(0).SaveToFile strFilename & ".dat" DoEvents Name strFilename & ".dat" As strFilename End If RestoreBLOB0710 = True Finally: On Error Resume Next Set rstACCDB = Nothing rstDAO.Close Set rstDAO = Nothing Exit Function ErrHandler: MsgBox Err.Number & "/" & Err.Description, vbCritical Resume Finally End Function Listing 11.2: Speichern von Anlagen im Dateisystem 11.7 Bilder und Dateien im OLE-Feld einbetten oder verknüpfen Dieses Thema ist andernorts so oft besprochen und beschrieben worden, dass hier nur kurz auf die Vor- und Nachteile eingegangen werden soll. Damit keine Verwechslungen mit der nachfolgend beschriebenen Vorgehensweise zum Speichern von Bildern und Dateien im binären Format in der Datenbank auftreten: Mit »Einbetten« und »Ver knüpfen« sind die Methoden der Benutzeroberfläche – etwa von gebundenen Objekt feldern – gemeint, um externe Dateien in einem OLE-Feld zu speichern oder damit zu verknüpfen. Der Einsatz des OLE-Feldes zum Einbetten oder Verknüpfen etwa von Office-Dateien macht beispielsweise Sinn, wenn Sie diese Dateien nur in der Datenbank speichern und gegebenenfalls mit der passenden Anwendung bearbeiten möchten. Nachteile beim Speichern von Bildern in OLE-Feldern Einmal in ein OLE-Feld eingebettete Dateien lassen sich dort nicht ohne Weiteres wieder herausholen. Es gibt zwar verschiedene Verfahren, aber der Aufwand ist relativ hoch. Sie könnten das Objekt etwa per Doppelklick in der dafür vorgesehenen Anwendung öffnen und dann speichern, aber wenn Sie diesen Vorgang mehrere hundert oder gar tausend Mal durchführen müssen, um beispielsweise Ihre Urlaubsbildersammlung auf CD zu brennen, werden Sie wünschen, die Dateien niemals in der Datenbank untergebracht zu haben. 658 Bilder und Dateien als Binärstrom im OLE-Feld speichern Außerdem kann es hier weitere Probleme geben: Dateien, die über die Objekt einfügenFunktion von Access in ein Tabellenfeld eingefügt wurden, benötigen einen OLE-Server, um bearbeitet oder wieder in eine Datei umgewandelt werden zu können. Das ist genau die Anwendung, die auf dem aktuellen Windows-System für die jeweilige Dateiendung zuständig ist. Wenn keine für diese Dateiendung passende Anwendung eingetragen ist, wird die Datei als »Paket« in dem Feld gespeichert. In diesem Fall ist der »Objekt-Manager« von Windows der für das Paket zuständige OLE-Server und damit auch für die Wiederherstellung der Datei verantwortlich. Das ist der günstigere Fall, denn falls die Datei auf dem Ausgangssystem mit einem konkreten OLE-Server verknüpft ist, muss die entsprechende Anwendung auch auf anderen Rechnern vorhanden sein, um die Datei öffnen oder speichern zu können. Bilder lassen die Datenbank wachsen Wenn es sich bei den zu speichernden Dateien um Bilder handelt, gibt es noch einen weiteren Nachteil: Bilddateien werden datenbankintern in einem speziellen bitmapähnlichen Format gespeichert, das wesentlich mehr Speicherplatz frisst als etwa das gängige für Fotos verwendete Format .jpg. Die Datenbank und die darin enthaltenen Bilder nehmen dann einen um ein Vielfaches größeren Platz auf der Festplatte ein als die Bilder im Ursprungsformat. 11.8 Bilder und Dateien als Binärstrom im OLE-Feld speichern In den folgenden Abschnitten lernen Sie einige Funktionen kennen, mit denen Sie Dateien in ein Feld einer Access-Datenbank importieren und die Datei wieder im Dateisystem speichern können. BEISPIELDATENBANK Die Prozeduren dieses Abschnitts finden Sie im Modul mdlOLE der Beispieldatenbank BilderUndBinaereDateien.accdb unter www.acciu.de/aeb2010. Importieren einer Datei in ein OLE-Feld einer Tabelle Die Funktion SaveFileToOLEField importiert eine Datei in ein OLE-Feld einer Tabelle. Die Funktion erwartet folgende Parameter: 659 Kapitel 11 Bilder und binäre Dateien »» strFilename: Dateiname der zu importierenden Datei »» strTable: Name der Zieltabelle »» strOLEField: Name des Zielfelds »» boolEdit: Angabe, ob die Datei in das OLE-Feld eines bestehenden Datensatzes eingefügt werden soll »» strIDField: Primärschlüsselfeld der Zieltabelle »» lngID: Primärschlüsselwert des Zieldatensatzes Die Routine erzeugt zunächst einen SQL-Ausdruck, der entweder den kompletten oder einen eingeschränkten Recordset auf die Zieltabelle zurückgibt – das hängt davon ab, ob boolEdit wahr ist und die Datei somit in das OLE-Feld eines bestimmten Datensatzes eingefügt werden soll. Nach einer Prüfung, ob die angegebene Datei vorhanden ist, aktiviert die Funktion entweder den aktuellen Datensatz zum Bearbeiten oder legt einen neuen Datensatz an. Schließlich erfolgt das Schreiben der Datei in Form eines Binärstroms in das Tabellenfeld – siehe auch die Kommentare im Listing. Public Function SaveFileToOLEField(strFilename As String, strTable As String, µ strOLEField As String, Optional boolEdit As Boolean, µ Optional strIDField As String, Optional lngID As Long) As Long Dim db As DAO.Database Dim rst As DAO.Recordset Dim lngFileID As Long Dim Buffer() As Byte Dim lngFileLen As Long Dim strSQL As String On Error GoTo SaveFileToOLEField_Err Set db = CurrentDb strSQL = "SELECT " & strOLEField & " FROM " & strTable If boolEdit = True Then strSQL = strSQL & " WHERE " & strIDField & " = " & lngID End If 'Datensatzgruppe mit dem Zielfeld für die Datei öffnen Set rst = db.OpenRecordset(strSQL, dbOpenDynaset) 'Meldung, falls Datei nicht vorhanden ist If Dir(strFilename) = "" Then MsgBox "Die Datei '" & strFilename & "' existiert nicht." Exit Function End If 660 Bilder und Dateien als Binärstrom im OLE-Feld speichern 'Bearbeitung des Datensatzes beginnen If boolEdit = True Then rst.Edit Else rst.AddNew End If 'Funktion zum Füllen des Feldes aufrufen On Error GoTo SaveFileToOLEField_Err 'Dateinummer für die Dateioperationen festlegen lngFileID = FreeFile 'Zu importierende Datei für den binären Zugriff öffnen Open strFilename For Binary Access Read Lock Read Write As lngFileID 'Dateigröße ermitteln lngFileLen = LOF(lngFileID) 'Größe der Variablen für den Dateiinhalt anpassen ReDim Buffer(lngFileLen) 'Zielfeld leeren rst(strOLEField) = Null 'Inhalt der Datei in Variable "Buffer" schreiben Get lngFileID, , Buffer 'Inhalt der Variablen in das Zielfeld schreiben rst(strOLEField).AppendChunk Buffer 'Recordset aktualisieren rst.Update SaveFileToOLEField = True SaveFileToOLEField_Exit: On Error Resume Next Close lngFileID rst.Close Set rst = Nothing Set db = Nothing Exit Function SaveFileToOLEField_Err: SaveFileToOLEField = Err.Number Resume SaveFileToOLEField_Exit End Function Listing 11.3: Funktion zum Importieren einer Datei in ein OLE-Feld einer Tabelle Das Speichern einer Datei in einen neuen Datensatz erfolgt beispielsweise mit dem folgenden Aufruf: SaveFileToOLEField CurrentProject.Path & "\bilder_01.tif", "tblOLE", "OLEFeld" 661 Kapitel 11 Bilder und binäre Dateien Wenn Sie die Datei in einem bestimmten Datensatz speichern wollten, müssen Sie auch noch die letzten drei Parameter angeben: SaveFileToOLEField CurrentProject.Path & "\bilder_02.tif", "tblOLE", "OLEFeld", µ True, "OLEFeldID", 1 Ob das Speichern funktioniert, können Sie zuverlässig nur mit der nachfolgend vorgestellten Funktion feststellen. Die soll nämlich die Datei aus dem OLE-Feld wiederherstellen. 11.9 Bilder und Dateien im binären Format aus einem OLE-Feld wiederherstellen Die Funktion SaveOLEFieldToFile stellt die in der Datenbank gespeicherten Dateien wieder her. Sie ist sehr ähnlich wie die oben beschriebene Funktion aufgebaut und tauscht im Wesentlichen Quelle und Ziel der Datei gegeneinander aus. Für weitere Informationen sei daher auf die Kommentare im Quelltext des folgenden Listings verwiesen: Public Function SaveOLEFieldToFile(strTable As String, strIDField As String, µ lngID As Long, strOLEField As String, strFilename As String) As Boolean Dim db As DAO.Database Dim rst As DAO.Recordset Dim lngFileID As Long Dim Buffer() As Byte Dim lngFileLen As Long On Error GoTo SaveOLEFieldToFile_Err Set db = CurrentDb 'Datensatz mit der angegebenen ID öffnen Set rst = db.OpenRecordset("SELECT " & strOLEField & " FROM " µ & strTable & " WHERE " & strIDField & " = " & lngID, dbOpenDynaset) On Error GoTo SaveOLEFieldToFile_Err 'Dateinummer für den Zugriff auf die zu erzeugende Datei ermitteln lngFileID = FreeFile 'Dateigröße ermitteln lngFileLen = Nz(LenB(rst(strOLEField)), 0) 'Wenn die Dateigröße größer 0 ist: If lngFileLen > 0 Then 'Größe der Variable "Buffer" anpassen ReDim Buffer(lngFileLen) 'Datei zum Schreiben öffnen Open strFilename For Binary Access Write As lngFileID 662 Bilder von der Festplatte in Formularen und Berichten anzeigen 'Feldinhalt in die Variable "Buffer" schreiben Buffer = rst(strOLEField).GetChunk(0, lngFileLen) 'Inhalt der Variablen "Buffer" in die Datei schreiben Put lngFileID, , Buffer End If SaveOLEFieldToFile = True SaveOLEFieldToFile_Exit: On Error Resume Next Close lngFileID rst.Close Set rst = Nothing Set db = Nothing Exit Function SaveOLEFieldToFile_Err: SaveOLEFieldToFile = False Resume SaveOLEFieldToFile_Exit End Function Listing 11.4: Die Funktion SaveOLEFieldToFile speichert den Inhalt des angegebenen OLE-Felds in einer Datei Beispiele für das Speichern einer als OLE-Objekt vorliegenden Datei Der nachfolgende Aufruf setzt voraus, dass die Tabelle einen Datensatz enthält, dessen OLE-Feld eine Datei im binären Format beinhaltet. Er speichert den Inhalt des Feldes OLEFeld des Datensatzes mit dem Primärschlüsselwert 1 der Tabelle tblOLE in der Datei ExportOLE.tif im aktuellen Datenbankverzeichnis. SaveOLEFieldToFile "tblOlE", "OLEFeldID", 1, "OLEFeld", µ CurrentProject.Path & "\ExportOLE.tif" Beachten Sie, dass die Prozedur vorhandene Dateien ohne Rückfrage überschreibt. 11.10 Bilder von der Festplatte in Formularen und Berichten anzeigen Je nachdem, ob Sie die gewünschten Dateien einfach nur in Formularen oder Berichten der Datenbank anzeigen, aber nicht in der Datenbank speichern möchten, können Sie sich mit sehr einfachen Mitteln behelfen. Speichern Sie die Dateien einfach gar nicht erst in der Datenbank, sondern belassen Sie diese im Dateisystem. Statt der Datei speichern Sie lediglich den Pfad zu der gewünschten Datei und verwenden die Pfadangabe, um eine Datei in einem entsprechenden Steuerelement zu öffnen. Praktisch ist es da- 663 Kapitel 11 Bilder und binäre Dateien bei, wenn man die Dateien im Datenbankverzeichnis oder in einem darunter liegenden Verzeichnis speichert.Auf diese Weise können Sie die Daten zusammen weitergeben und gleichzeitig sicherstellen, dass die Datenbank die Dateien am gleichen Ort (relativ zum Datenbankverzeichnis) wie beim Anlegen der Datei findet. Ab Access 2000 liefert die Funktion CurrentProject.Path den gewünschten Pfad zurück. Wohin mit dem Dateipfad? Wenn Sie nur den Dateipfad in der Datenbank speichern, stehen Sie vor der Frage, wie Sie die enthaltenen Informationen aufteilen. Es gibt verschiedene Varianten: »» Sie speichern den kompletten Pfad inklusive Dateiname in einem Feld. Nachteil: Änderungen am Verzeichnis erfordern immer den Einsatz von ZeichenkettenFunktionen. »» Sie speichern Verzeichnis und Dateiname in zwei Feldern. »» Sie legen zwei eigene Tabellen für Verzeichnisse und Dateinamen an und verknüpfen diese per 1:n-Beziehung. Vorteil: Änderungen an Verzeichnissen erfolgen auf einen Schlag. Nachteil: Änderungen an einzelnen Verzeichnissen bereiten mehr Aufwand. 11.10.1 Anzeigen externer Bilddateien im Formular Da sich Bilddateien nicht nur für das Speichern in einer Datenbank, sondern besonders zur Anzeige in Formularen und Berichten eignen, beschäftigt sich das folgende Beispiel mit der Anzeige von extern gespeicherten Bilddateien. Die Tabelle aus Abbildung 11.14 enthält neben dem Primärschlüsselfeld BildID nur ein Feld für eine Bezeichnung des Bildes und eines für den Dateinamen. In das Feld Dateiname tragen Sie am besten den kompletten Pfad inklusive Dateiname ein oder teilen diese beiden Informationen auf zwei getrennte Felder auf. Zum Anzeigen der Bilder verwenden Sie ein Formular mit der Tabelle tblVerknuepf teBilder als Datensatzquelle. Das Formular soll alle Felder der Tabelle enthalten und – falls vorhanden – das angegebene Bild anzeigen. Dazu fügen Sie zusätzlich ein Bildsteuerelement hinzu. Dabei müssen Sie direkt die anzuzeigende Datei angeben – wählen Sie dazu irgendein Dummybild aus. Anschließend lassen Sie dieses wieder verschwinden, indem Sie in der Entwurfsansicht des Formulars die Eigenschaft Bild des Bildsteuerelements leeren. Stellen Sie außerdem den Namen des Bildsteuerelements auf imgBild ein (siehe Abbildung 11.15). 664 Bilder von der Festplatte in Formularen und Berichten anzeigen Abbildung 11.14: Tabelle zum Speichern von Bildverknüpfungen Abbildung 11.15: Formular mit ungebundenem Bildsteuerelement Nun müssen Sie noch dafür sorgen, dass das Bildsteuerelement die im Feld Dateiname angegebene Bilddatei anzeigt. Das Bildsteuerelement besitzt bereits seit Access 2007 die Eigenschaft Steuerelementinhalt, für die Sie direkt das Feld mit dem kompletten Pfad angeben können. Es zeigt dann automatisch das angegebene Bild an. Sie brauchen nur noch ein wenig Code, um einen Dialog zum Auswählen der Bilddatei anzuzeigen: 665 Kapitel 11 Bilder und binäre Dateien Private Sub cmdDateiOeffnen_Click() Me!Dateiname = WZHOpenFileName(CurrentProject.Path) End Sub Listing 11.5: Aufruf der Prozedur BildAktualisieren durch verschiedene Ereignisprozeduren Das Bildsteuerelement aktualisiert die angegebene Datei direkt nach dem Ändern des im Feld Dateiname angegebenen Wertes. Im Unterschied zu früheren Versionen von Access sind nun übrigens nicht mehr die Office-Grafikfilter für das Laden der Bilder verantwortlich, sondern das in Access integrierte GDIPlus. Damit entfällt die lästige bisherige Fortschrittsanzeige beim Laden, die Access manchmal zum Absturz brachte. Sie müssen sich nun auch keine Sorgen mehr machen, dass auf einem anderen Rechner die erforderlichen Grafikfilter nicht installiert sein könnten. 11.10.2 Anzeige externer Bilddateien in Berichten In Berichten ist die Vorgehensweise durch die Steuerelementinhalt-Eigenschaft des Bildsteuerelements ebenfalls viel einfacher geworden (siehe Abbildung 11.16). Abbildung 11.16: Entwurfsansicht eines Berichts mit verknüpften Bilddateien Wenn Bilder nicht angezeigt werden wollen … Manche .jpg-Datei und Bilddatei anderer Formate kann Access nicht in seinem Bild steuerelement anzeigen. Das Problem liegt entweder tatsächlich in einem fehlenden Grafikformat oder aber das Format der Datei entspricht nicht exakt den Spezifikationen. Dann hilft nur das Konvertieren der Bilder in ein anderes Format. 666 Icons und Co. 11.10.3 Alternative zum Bildsteuerelement von Access Eine Alternative zum Bildsteuerelement ist das Image-Steuerelement der MSForms-Bib liothek, die bei jeder Access-Installation – auch bei Runtimes – mitinstalliert wird. Die ses Steuerelement fügen Sie über den Dialog ActiveX-Steuerelement einfügen ein (Rib bon-Eintrag Entwurf|Steuerelemente|ActiveX-Steuerelement einfügen). Damit das Steu erelement die Bilder passend zur Steuerelementfläche zoomt (siehe Abbildung 11.17), fügen Sie noch zwei Zeilen Code hinzu, sodass die Prozedur aus dem vorherigen Bei spiel nun so aussieht: Private Sub Detailbereich_Print(Cancel As Integer, PrintCount As Integer) Dim strDateiname As String Dim strPicture As String Dim objImage As MSForms.Image 'Prüfen, ob Dateiname vorhanden ist If Not IsNull(Me!Dateiname) Then strDateiname = Me!Dateiname 'Prüfen, ob das angegebene Bild existiert... If Not Dir(strDateiname) = "" Then '... und Bild festlegen strPicture = strDateiname End If End If 'Bildname dem Bildsteuerelement zuweisen Set objImage = Me!imgBild.Object objImage.PictureSizeMode = fmPictureSizeModeZoom Set objImage.Picture = LoadPicture(strDateiname) End Sub Listing 11.6: Anzeigen verknüpfter Bilddateien im Image-Steuerelement der MSForms-Bibliothek 11.11 Icons und Co. Mittlerweile gibt es verschiedene DLLs für Grafikoperationen. Diese liegen auch noch in verschiedenen Versionen vor. Welche Sie auf Ihrem System finden, hängt von Betriebs system- und Office-Version sowie von gegebenenfalls weiterer installierter Software ab. GDIPlus und OGL, eine Office-spezifische Version von GDIPlus liefern eine Reihe von Funktionen für den Zugriff auf und die Bearbeitung von Bildern. Sascha Trowitzsch hat ein Modul programmiert, das diese Funktionen einfach zugänglich macht. Darüber hinaus prüft dieses Modul sogar noch die verfügbaren DLLs für Grafikoperationen und ruft in Abhängigkeit davon die richtigen API-Funktionen auf. 667 Kapitel 11 Bilder und binäre Dateien Abbildung 11.17: Bilder im Image-Steuerelement der MSForms-Bibliothek BEISPIELDATENBANK Die Module mdlOGL0710 sowie mdlOLE finden Sie in der Beispieldatenbank Bilder UndBinaereDateien.accdb unter www.acciu.de/aeb2010. Achtung: Dieses Modul funktioniert nur in der 32-bit-Variante von Access 2010. Nachfolgend finden Sie einige Beispiele für die Anwendung dieser genialen Funktions sammlung. Vorneweg der Hinweis, dass der Datentyp StdPicture der Bibliothek OLE Automation in den meisten Fällen als Container für ein Bild dient. So lesen Sie etwa zunächst eine Bilddatei in ein Objekt des Typs StdPicture der Bibliothek OLE Automation ein, bevor Sie dieses im OLE-Feld einer Tabelle speichern – Sie benötigen also in Ihrer Datenbank einen entsprechenden Verweis. 11.11.1 Icons in ListView-/TreeView-Steuerelementen Wenn Sie ein ListView- oder TreeView-Steuerelement einsetzen und darin Icons anzeigen möchten, müssen diese in einem ImageList-Steuerelement zur Verfügung stehen. Normalerweise ist das kein Problem, denn Sie können die Bilder manuell hinzufügen. 668 Icons und Co. Besser ist es jedoch, die in einer Anwendung verwendeten Bilddateien zentral in einer Tabelle der Datenbank zu speichern. Damit die Bilder überall in der Anwendung immer aktuell sind (immerhin kann es ja mal vorkommen, dass sich ein Icon ändert), sollten Sie diese dynamisch aus einer Bildertabelle in das ImageList-Steuerelement einlesen. Dabei helfen die Methoden des Moduls mdlOGL0710, in diesem Fall PicFromField. Im folgenden Beispiel haben wir im Formular frmBildbeispiele ein ImageList-Steuerelement namens ctlImageList sowie ein TreeView-Steuerelement namens ctlTreeView angelegt (siehe Abbildung 11.18). Abbildung 11.18: Icons im TreeView-Steuerelement Zur Vereinfachung der Programmierung haben wir auch hier wieder Objektvariablen für das ImageList- und das TreeView-Steuerelement vorgesehen: Dim objTreeView As MSComctlLib.TreeView Dim objImageList As MSComctlLib.ImageList Beim Laden des Formulars werden diese beiden Variablen mit den Verweisen auf die entsprechenden Steuerelemente gefüllt. Außerdem ruft diese Prozedur zwei weitere Prozeduren auf, die erst das ImageList- und dann das TreeView-Steuerelement mit den benötigten Daten füllen: Private Sub Form_Load() Set objTreeView = Me!ctlTreeView.Object Set objImageList = Me!ctlImageList.Object ImageListFuellen TreeViewFuellen End Sub Die erste dieser beiden Prozeduren füllt das ImageList-Steuerelement mit den im OLEFeld ImageObjekt der Tabelle USysImages enthaltenen Bilddateien. Dabei stellt die Prozedur zunächst die Höhe und Breite für die im ImageList-Steuerelement zu speichernden Bilder ein und leert eventuell noch enthaltene Bilder. Schließlich durchläuft die Prozedur eine Datensatzgruppe und fügt für jeden Datensatz ein Bild zum ImageList- 669 Kapitel 11 Bilder und binäre Dateien Steuerelement hinzu. Dies geschieht mit der Add-Methode dieses Steuerelements. Diese erwartet unter anderem einen Verweis auf ein StdPicture-Objekt mit dem Bildobjekt als Inhalt. Diesen liefert die Funktion PicFromField aus dem Modul mdlOGL0710, die nur einen Verweis auf das Herkunftsfeld benötigt: Public Sub ImageListFuellen() Dim db As DAO.Database Dim rstImages As DAO.Recordset Dim i As Integer Set db = CurrentDb With ctlImageList .ListImages.Clear .ImageHeight = 16 .ImageWidth = 16 Set rstImages = db.OpenRecordset("SELECT * FROM USysImages", dbOpenSnapshot) Do While Not rstImages.EOF i = i + 1 .ListImages.Add i, rstImages!Imagename, PicFromField(rstImages!ImageObject) rstImages.MoveNext Loop End With End Sub Schließlich folgt die Prozedur zum Füllen des TreeView-Steuerelements. Der Einfachheit halber wollen wir einfach nur für jedes im ImageList-Steuerelement enthaltene Element einen Node im TreeView anlegen. Damit dies möglich ist, weist die Prozedur der Eigenschaft ImageList des TreeView-Steuerelements zunächst einen Verweis auf das ImageList-Steuerelement zu. Danach ermittelt die Prozedur mit der Eigenschaft Count der Auflistung ListImages des ImageList-Steuerelements die Anzahl der enthaltenen Bilder. Dieser Wert bildet die obere Grenze der nachfolgenden For...Next-Schleife. Die einzige Anweisung innerhalb dieser Schleife legt mit der Add-Methode jeweils einen neuen Node im TreeView-Steuerelement an. Zum Hinzufügen eines Icons brauchen Sie lediglich den Schlüsselwert des entsprechenden Eintrags mit dem vorletzten Parameter zu übergeben: Public Sub TreeViewFuellen() Dim i As Integer With objTreeView Set .ImageList = Me.ctlImageList.Object For i = 1 To objImageList.ListImages.count .Nodes.Add , , "a" & i, objImageList.ListImages.Item(i).Key, objImageList. ListImages.Item(i).Key 670 Icons und Co. Next i End With End Sub 11.11.2 Icons in Kontextmenüs Wer Wert auf eine benutzerfreundliche Anwendung legt, wird Funktionen, die sich auf bestimmte Elemente der Benutzeroberfläche beziehen, zusätzlich in Kontextmenüs unterbringen. Auch in Kontextmenüs können Sie Icons darstellen (siehe Abbildung 11.19). Allerdings sind dazu einige Tricks nötig, die unser Modul mdlOGL0710 jedoch auch abbildet. Abbildung 11.19: Icons im Kontextmenü Dazu brauchen wir eine kleine Prozedur, welche das Kontextmenü anlegt und durch die Beim Laden-Ereignisprozedur ausgelöst wird: Private Sub Form_Load() PopupAnlegen End Sub Die Prozedur PopupAnlegen deaktiviert zunächst die Anzeige der eingebauten Kontext menüs. Danach löscht sie ein eventuell noch vorhandenes Exemplar der nun neu zu erstellenden Symbolleiste. Die Add-Methode der CommandBars-Auflistung erzeugt diese gleich neu. Genau wie im vorherigen Beispiel soll das Kontextmenü schlicht mit den in der Tabelle USysImages gespeicherten Bilddateien gefüllt werden. Dazu brauchen wir ein Database-Objekt und ein Recordset, das den Zugriff auf diese Tabelle ermöglicht. In einer Schleife durchläuft die Prozedur dann alle Datensätze dieses Recordsets. Die erste Anweisung innerhalb der Schleife erzeugt schlicht ein neues Element des Typs msoButtonIcon, also eine Schaltfläche mit Icon. Die Caption-Eigenschaft nimmt den Text für den Kontextmenüeintrag entgegen, die Picture-Eigenschaft einen Verweis auf ein StdPicture-Objekt. 671 Kapitel 11 Bilder und binäre Dateien Dieses StdPicture-Objekt müssen Sie zunächst noch erstellen. Wie im vorherigen Beispiel hilft dabei die Funktion PicFromField. Kontextmenüs zeigen standardmäßig nicht die transparenten Teile von Icons an, was aber kein Problem ist: Der Eigenschaft Mask können Sie einen Verweis auf ein weiteres StdPicture-Objekt übergeben, das definiert, welche Bereiche des für Picture angegebenen StdPicture-Objekts transparent angezeigt werden sollen. Glücklicherweise gibt es im Modul mdlOGL0710 eine Funktion, die ein solches Maskenbildobjekt auf Basis der transparenten Teile des anzuzeigenden Images erzeugen kann und die lediglich einen Verweis auf das zu maskierende StdPicture-Objekt erwartet. Auf diese Weise ist das Kontextmenü schnell erzeugt und mit den gewünschten Bildern gefüllt: Private Sub PopupAnlegen() Dim cbr As CommandBar Dim cbb As CommandBarButton Dim objPicture As StdPicture Dim db As DAO.Database Dim rstImages As DAO.Recordset Me.ShortcutMenu = False On Error Resume Next CommandBars("Popup_Icons").Delete On Error GoTo 0 Set cbr = CommandBars.Add("Popup_Icons", msoBarPopup, False, True) Set db = CurrentDb Set rstImages = db.OpenRecordset("SELECT * FROM USysImages", dbOpenDynaset) Do While Not rstImages.EOF Set cbb = cbr.Controls.Add(1) With cbb .Caption = rstImages!ImageName Set objPicture = PicFromField(rstImages!ImageObject) If Not objPicture Is Nothing Then cbb.Picture = objPicture cbb.Mask = MaskFromPicture(objPicture) End If End With rstImages.MoveNext Loop End Sub Fehlt nur noch ein Mechanismus, der das Kontextmenü bei Bedarf anzeigt. Im Beispiel formular soll es immer angezeigt werden, wenn der Benutzer mit der rechten Maustaste auf den Detailbereich klickt. Dies erledigt eine entsprechende Ereignisprozedur: 672 Icons und Co. Private Sub Detailbereich_MouseDown(Button As Integer, Shift As Integer, X As Single, _ Y As Single) Select Case Button Case 2 CommandBars("Popup_Icons").ShowPopup End Select End Sub 11.11.3 Icons im Ribbon Auch für das Anzeigen von Icons im Ribbon können Sie die PicFromField-Funktion verwenden (siehe Abbildung 11.20). Wir greifen auf das kommende Kapitel vor, wo Sie erfahren, dass Sie einem Ribbon-Steuerelement über eine Prozedur mit einer speziellen Syntax einen Verweis auf ein StdPicture-Objekt übergeben können, das dann im Steuerelement angezeigt wird. Abbildung 11.20: Ein Icon im Ribbon Eine solche Prozedur sieht im einfachsten Fall wie folgt aus. Die einzige Zeile dieser Prozedur ist recht unübersichtlich: Public Sub loadImage(control, ByRef image) Set image = PicFromField(CurrentDb.OpenRecordset("SELECT ImageObject FROM µ USysImages WHERE ImageName = '" & control & "'").Fields(0), &HFFFFFF) End Sub Also liefern wir nachfolgend eine etwas übersichtlichere Variante. Diese verdeutlicht die notwendigen Schritte. Die Prozedur liefert mit dem Parameter control den Wert der Eigenschaft image des Ribbon-Steuerelements, für das diese Prozedur aufgerufen wird. Das anschließend geöffnete Recordset enthält genau den Datensatz für dieses Bild. Ein Verweis auf das Feld ImageObjekt dieses Datensatzes landet in der Objektvariablen fld, welches wiederum als Parameter eines Aufrufs der PicFromField-Prozedur dient. In diesem Falle müssen Sie dem Aufruf noch einen weiteren Parameter übergeben, der Informationen über die Transparenz enthält: Public Sub loadImage(control, ByRef image) Dim db As DAO.Database 673 Kapitel 11 Bilder und binäre Dateien Dim rst As DAO.Recordset Dim fld As DAO.Field Set db = CurrentDb Set rst = db.OpenRecordset("SELECT ImageObject FROM USysImages µ WHERE ImageName = '" & control & "'") Set fld = rst!ImageObject Set image = PicFromField(fld, &HFFFFFF) End Sub 11.11.4 Bilder aus dem OLE-Feld in einem Formular anzeigen Genau wie die Bilddateien aus Anlagefeldern können Sie auch solche aus OLE-Feldern in Formularen anzeigen. Mit dem StdPicture-Objekt und dem Image-Steuerelement der MSForms 2.0-Bibliothek ist es möglich, in OLE-Feldern gespeicherte Bilder direkt – und zwar ohne Umweg über das Dateisystem – in einem Formular anzuzeigen. Der Entwurf des Formulars sieht wie in Abbildung 11.21 aus, die Anzeige der Bilder erfolgt mit besagtem Image-Steuerelement. Als Datensatzquelle dient die Tabelle tblOLEBilder. Um den Inhalt des OLE-Feldes der Tabelle anzuzeigen, lesen Sie zunächst den Binär strom aus dem passenden Tabellenfeld – hier das Feld OLEBild der Tabelle tblOLEBilder – aus und wandeln es in ein StdPicture-Objekt um. Dazu verwenden Sie ebenfalls die Funktion PicFromField aus dem Modul mdlOGL0710. Abbildung 11.21: Das Formular zum Anzeigen von Bildern direkt aus der Tabelle in der Entwurfs ansicht 674 Icons und Co. Die Routine BildAnzeigen macht sich die Funktion PicFromField zu Nutze, indem sie das Field-Objekt mit dem Bild ermittelt und dieses an die Funktion PicFromField übergibt, um ein passendes StdPicture-Objekt zurückzuerhalten. Dieses wiederum lässt sich einfach im Image-Steuerelement des Formulars anzeigen. Private Sub BildAnzeigen() Dim objPicture As stdole.StdPicture Dim db As DAO.Database Dim rst As DAO.Recordset Dim fld As DAO.Field Set db = CurrentDb Set rst = db.OpenRecordset("SELECT OLEBild FROM tblOLEBilder WHERE OLEBildID = " _ & Me.OLEBildID, dbOpenDynaset) Set fld = rst.Fields(0) InitGDIP Set objPicture = PicFromField(fld) Set Me.imgBild.Picture = objPicture ShutDownGDIP End Sub Listing 11.7: Diese Routine ermittelt das Field-Objekt mit dem Bild zum aktuell im Formular angezeigten Datensatz und wandelt dessen Inhalt in ein Format um, das vom Image-Steuerelement angezeigt werden kann Wann nun wird das in der Tabelle enthaltene Bild in das Image-Steuerelement geladen? Natürlich in der Ereignisprozedur, die beim Anzeigen eines jeden Datensatzes ausgelöst wird. Diese prüft, ob das Feld OLEBild überhaupt gefüllt ist, und ruft dann die Routine BildAnzeigen auf, um das Image-Steuerelement damit zu füllen: Private Sub Form_Current() If Not IsNull(Me.OLEBild) Then BildAnzeigen Else Set Me.imgBild.Picture = Nothing End If End Sub Listing 11.8: Bild im Formular anzeigen Wenn bereits ein Bild im ersten Datensatz der Datensatzquelle des Formulars enthalten ist, sieht dieses nun beim Öffnen wie in Abbildung 11.22 aus. In der Abbildung sehen Sie noch eine Schaltfläche zum Hinzufügen eines Bildes zu einem Datensatz. Diese füllen Sie natürlich auch noch mit Leben – Sie wollen ja schließlich auch komfortabel Daten zur Tabelle hinzufügen. Dazu legen Sie die folgende Ereignisprozedur an, die zunächst 675 Kapitel 11 Bilder und binäre Dateien die Funktion WZHOpenFileName zum Auswählen der gewünschten Bilddatei aufruft (Sie finden diese Funktion im Modul mdlOpenSaveFile der Beispieldatenbank). Anschließend speichert die Routine den aktuellen Datensatz, weil gegebenenfalls noch keiner in der zugrunde liegenden Tabelle enthalten ist. Das ist notwendig, weil das Bild direkt in der Tabelle gespeichert wird. Schließlich speichert die Routine das Bild mit der Funktion SaveFileToOLEField in der passenden Tabelle und aktualisiert die Anzeige durch einen Aufruf der Funktion BildAnzeigen. Abbildung 11.22: Ein direkt aus der Tabelle geladenes Bild im Binärformat Private Sub cmdBildHinzufuegen_Click() Dim strFilename As String strFilename = WZHOpenFileName(CurrentProject.Path, "Bildddatei auswählen", , , False, ofnThumbs) Me!Dateiname = Mid(strFilename, InStrRev(strFilename, "\") + 1) DoCmd.RunCommand acCmdSaveRecord SaveFileToOLEField strFilename, "tblOLEBilder", "OLEBild", True, "OLEBildID", Me.OLEBildID BildAnzeigen End Sub Listing 11.9: Einfügen eines Bildes aus einer Datei in ein OLE-Feld 11.11.5 Bild aus einem OLE-Feld wiederherstellen Auf diese Art gespeicherte Bilder können Sie natürlich auch wieder in eine Datei umwandeln. Auch dazu können Sie eine passende Schaltfläche im Formular anlegen. Für diese hinterlegen Sie dann den folgenden Code. Die Routine ermittelt zunächst aus dem 676 Icons und Co. im Feld Dateiname gespeicherten Wert die Dateiendung und daraus weiter unten das Dateiformat, unter dem die Bilddatei gespeichert werden soll. Zwischendurch erzeugt sie ein Recordset, das genau den soeben im Formular angezeigten Datensatz enthält, und schreibt aus dem darin enthaltenen OLE-Feld die passende Datei auf die Festplatte (in das Verzeichnis der aktuellen Anwendung). Private Sub cmdBildSpeichern_Click() Dim objPicture As stdole.StdPicture Dim db As DAO.Database Dim rst As DAO.Recordset Dim fld As DAO.Field Dim strFileType As String Dim intPicType As PicFileType strFileType = Mid(Me.Dateiname, InStrRev(Me.Dateiname, ".") + 1) Set db = CurrentDb Set rst = db.OpenRecordset("SELECT OLEBild FROM tblOLEBilder " _ & "WHERE OLEBildID = " & Me.OLEBildID, dbOpenDynaset) Set fld = rst.Fields(0) Set objPicture = PicFromField(fld) InitGDIP Select Case strFileType Case "bmp" intPicType = pictypeBMP Case "gif" intPicType = pictypeGIF Case "png" intPicType = pictypePNG Case "jpg" intPicType = pictypeJPG End Select SaveImage objPicture, CurrentProject.Path & "\" & Me!Dateiname, _ intPicType ShutDownGDIP Set fld = Nothing rst.Close Set rst = Nothing Set db = Nothing End Sub Listing 11.10: Speichern eines Bildes aus einem OLE-Feld in eine Datei 11.11.6 Speichern in verschiedenen Formaten Sie sind beim Speichern eines Bildes, das in einem OLE-Feld als Binärstrom vorliegt, keinesfalls daran gebunden, es im ursprünglichen Format zu speichern. Ändern Sie ein- 677 Kapitel 11 Bilder und binäre Dateien mal die Dateiendung im passenden Feld im Formular und klicken Sie dann auf die Bild speichern-Schaltfläche – Sie werden sehen, dass die Routine SaveImage aus dem Modul mdlOGL0710 Bilder auch in andere Formate umwandeln kann. 11.11.7 Ersatz für Anlagen? Das Anlage-Feld ist zweifellos eine Erleichterung für Access-Anwender. Allerdings behält das Speichern der Dateien in einer verborgenen Datei einen faden Beigeschmack. Warum also nicht Bilder und binäre Dateien als Binärstrom in einem OLE-Feld speichern? Mit den genannten Funktionen des Moduls mdlOGL0710 erhalten Sie Möglichkeiten, die das Anlage-Feld nicht liefern kann. Oder doch? Aber natürlich. Sascha Trowitzsch liefert auch noch eine Routine, mit der Sie den Inhalt eines Anlage-Feldes so in ein ByteArray einlesen, dass Sie es leicht mit der Funktion AttachmentToPicture in ein StdPictureObjekt umwandeln können. Und somit können Sie auch alle anderen in mdlOGL0710 enthaltenen Funktionen einsetzen. Und schließlich liefert die Beispieldatenbank auch noch eine Funktion, die ein Byte-Array wieder in einem Anlage-Feld speicher (siehe Array2Attachment). Performance pro und kontra Bei der Entscheidung, ob Sie Anlage- oder OLE-Felder zum Speichern Ihrer Bilder oder Dateien verwenden, sollten Sie auch die Performance berücksichtigen. Access komprimiert den Inhalt von Anlage-Feldern, wenn es sinnvoll ist – etwa beim .bmp-Format. Damit benötigen Sie weniger Speicherplatz und vor allem lassen sich Daten schneller über das Netzwerk transportieren. Andererseits kostet das Packen und Entpacken ebenfalls Ressourcen. Das binäre Speichern im OLE-Feld erfolgt unkomprimiert, kostet also beim Anlegen und Laden weniger Zeit. Dafür haben die Dateien Originalgröße – schlecht also etwa beim .bmp-Format – und führen gegebenenfalls zu mehr Netzwerktraffic. Für Bilder im .jpg-, .gif- oder .png-Format, die per Definition bereits komprimiert sind, spielt das hingegen keine Rolle. Ebenfalls wichtig ist die Frage, ob die Datenbank einmal nach SQL Server et cetera migriert werden soll. Das OLE-Feld macht in diesem Fall weniger Schwierigkeiten, da es sich direkt in der entsprechenden Tabelle befindet, während das Anlagefeld ja intern in einer separaten Tabelle gespeichert wird. 678