Im Beitrag „Icons in Access-Formularen und Berichten“ (www.access-im-unternehmen.de/1235) haben wir schon einmal eine Möglichkeit aufgezeigt, wie man Formulare und Berichte in Access mit individuellen Icons ausstatten kann. So kann man beispielsweise ein Formular zum Bearbeiten eines Kunden mit dem gleichen Icon ausstatten, das man auch für die Schaltfläche zum Öffnen dieses Formulars im Ribbon untergebracht hat. Der Benutzer kann so noch besser erkennen, worum es im Formular geht. In der vorherigen Fassung der Lösung hatten wir allerdings noch das Problem, dass wir die Icons, die links oben in Formularen und Berichten erscheinen sollten, noch im Dateisystem speichern mussten. Das kann aus diversen Gründen zu Problem führen und daher sind wir froh, hier den nächsten Schritt gehen zu können: Das direkte Einlesen der Icons aus der Tabelle „MSysResources“ und anschließendes Anzeigen in Formulare und Berichten.
Ziel: Formular-Icons ohne externe Dateien
Wie bereits erwähnt, sind wir bereits in der Lage, Formulare mit einem eigenen Icon auszustatten (siehe Bild 1). Das Problem ist jedoch bisher gewesen, dass die entsprechende Icon-Datei sich auf der Festplatte befinden muss, damit wir diese laden und per API dem Formular als Icon zuweisen konnten. Damit wir nicht für jede Anwendung einen eigenen Ordner bereitstellen mussten, der die benötigten Icon-Dateien enthält oder sogar die Icon-Dateien lose im Verzeichnis der Access-Datenbank gespeichert haben, befanden sich die .ico-Dateien bis zur Verwendung in der Tabelle USysResources und wurden erst dann zur Anwendung exportiert.
Bild 1: Formular mit individuellem Icon
Nunmehr haben wir den noch fehlenden Schritt realisiert: Wir können nun die Icon-Dateien direkt aus dem Anlagefeld der Ressourcen-Tabelle auslesen und dem jeweiligen Formular zuweisen. Wie das gelingt, lesen Sie in diesem Beitrag.
Code zum Anzeigen eines Formularicons
Der Code sieht im Wesentlichen wie in Listing 1 aus. Im oberen Teil finden wir einige Konstanten-Deklarationen, die wir im weiteren Verlauf benötigen.
Option Compare Database Option Explicit Public Const C_IMAGE_ICON = 1 Public Const C_LR_LOADFROMFILE = &H10 Public Const C_WM_SETICON = &H80 Public Const ICRESVER As Long = &H30000 Public Const LR_DEFAULTSIZE As Long = &H40 Public Const WM_SETICON As Long = &H80 Public Const ICON_SMALL As Long = 0 Public Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As LongPtr, _ ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As Any) As LongPtr Public Declare PtrSafe Function CreateIconFromResourceEx Lib "user32.dll" (presbits As Any, dwResSize As Any, _ ByVal fIcon As Long, ByVal dwVer As Long, ByVal cxDesired As Long, ByVal cyDesired As Long, _ ByVal flags As Long) As LongPtr Public Function SetFormIconFromMSysResources(ByVal hWnd As Long, ByVal strIconName As String) As Boolean Dim hIcon As LongPtr Dim bData() As Byte Dim dwOffset As LongPtr Dim dwSize As LongPtr Dim lngIndex As Long Dim lngID As Long lngIndex = 0 lngID = Nz(DLookup("id", "MSysResources", "name=''" & strIconName & "''"), 0) If Not lngID = 0 Then bData() = BLOB2Binary0710("MSysResources", "Data", "Id", lngID, True) dwSize = VarPtr(bData(16& * lngIndex + 14&)) dwOffset = VarPtr(bData(bData(16& * lngIndex + 18&))) hIcon = CreateIconFromResourceEx(ByVal dwOffset, ByVal dwSize, 1, ICRESVER, 0&, 0&, LR_DEFAULTSIZE) If hIcon Then SendMessage hWnd, WM_SETICON, ICON_SMALL, ByVal hIcon End If Else MsgBox "Icon ''" & strIconName & "'' fehlt in der Tabelle MSysResources", vbExclamation + vbOKOnly, "Icon fehlt" End If End Function
Listing 1: Code zum Bereitstellen von Icons in Formularen und Berichten
Darunter sehen wir die Deklaration von zwei API-Funktionen, die uns in der folgenden Prozedur unterstützen.
Schließlich folgt die Funktion SetFormIconFromMSysResources, welche die eigentliche Arbeit übernimmt. Die Funktion hat zwei Parameter:
- hWnd: Das Handle des Fensters, für das das Icon gesetzt werden soll. Das ist normalerweise das Handle des Access-Formulars oder -Berichts.
- strIconName: Der Name des Icons, dessen ID in der MSysResources-Tabelle gesucht wird.
Die Funktion verwendet die folgenden Variablen:
- hIcon: Speichert das Handle des eingelesenen Icons.
- bData: Ein Byte-Array, das die Binärdaten des Icons aus der MSysResources-Tabelle aufnimmt.
- dwOffset: Speichert das Offset des Icons.
- dwSize: Speichert die Größe des Icon-Ressourcenblocks.
- lngIndex: Index des Icons in der .ico-Datei. Eine .ico-Datei kann mehrere Icons enthalten.
- lngID: Primärschlüsselwert des Eintrags der Tabelle MSysResources mit dem gesuchten Icon.
Die Funktion setzt den Index für das aus der .ico-Datei zu verwendende Icon mit lngIndex auf 0. Der Parameter strIconName der Funktion liefert den Wert des Feldes Name der Tabelle MSysResources, dessen Anlage aus dem Feld Data als Icon verwendet werden soll.
Die Funktion holt per DLookup den Wert des Feldes id für diesen Datensatz und speichert diesen in der Variablen lngID.
Die folgende If…Then-Bedingung prüft, ob der gesuchte Datensatz gefunden werden konnte, anderenfalls erscheint eine entsprechende Meldung.
Anschließend folgt die Extraktion der Icon-Daten aus dem Feld Data. Das erledigen wir mit einem Aufruf der Funktion BLOB2Binary0710 aus dem Modul mdlImages, der wir den Namen der Tabelle, das Anlagefeld, das Feld mit dem Primärschlüssel und den Primärschlüsselwert übergeben. Die Icon-Daten landen danach in einem Byte-Array namens bData. Für dieses Array ermitteln wir nun die Größe und das Offset im Speicher und speichern beides in den Variablen dwSize und dwOffset.
Schließlich folgt der Einsatz der API-Funktionen. Die erste heißt CreateIconFromResourceEx und liefert uns ein Handle auf das Icon. Dieses Handle setzen wir schließlich mit der Funktion SendMessage als Icon des Formulars oder Berichts mit dem Handle aus hWnd ein.
Einsatz der Funktion
Den Aufruf der Funktion SetFormIconFromMSysResources fügen wir nun in die Ereignisprozedur Form_Load des Formulars oder Berichts ein, der ein Icon aus der Tabelle MSysResources anzeigen soll. Das sieht beispielsweise wie folgt aus:
Private Sub Form_Open(Cancel As Integer) Call SetFormIconFromMSysResources(Me.hWnd, "alarmclock") End Sub
Dazu muss nun nur noch die Tabelle MSysResources einen Datensatz enthalten, der für das Feld Name den Wert alarmclock aufweist und dessen Feld data eine entsprechende .ico-Datei enthält. Das ist in unserer Datenbank der Fall (siehe Bild 2).
Bild 2: Das anzuzeigende Icon in der Tabelle MSysResources
Voraussetzungen für die .ico-Dateien
Nun sind allerdings leider nicht alle .ico-Dateien für die Anzeige als Icon eines Formulars oder Berichts geeignet. Wir benötigen eine bestimmte Auflösung, sonst wird das Icon nicht korrekt angezeigt. Dazu gehören beispielsweise Auflösungen mit 8- oder 24-Bit. 4-Bit reichen nicht aus.
Hinzufügen der .ico-Dateien
Leider können wir die .ico-Dateien nicht auf die gleiche einfache Weise wie .png-Dateien zur Tabelle MSysResources hinzufügen. Dazu brauchten wir einfach nur ein Formular in der Entwurfsansicht zu öffnen und dann ein Bild zum Formular oder zu einer Schaltfläche hinzuzufügen.
Die dabei ausgewählten Dateien werden automatisch als .png-Dateien in der Tabelle MSysResources hinterlegt und können dann in den entsprechenden Eigenschaften der Steuerelemente ausgewählt werden. Wenn wir hier .ico-Dateien auswählen, werden diese als .png-Dateien importiert. .png-Dateien können wir leider nicht als Icon von Formularen und Berichten einsetzen.
.ico-Dateien manuell hinzufügen
Also haben wir zwei Möglichkeiten. Die erste ist, die .ico-Dateien manuell zur Tabelle MSysResources hinzuzufügen. Das gelingt mit den folgenden Schritten:
- Wir öffnen die Tabelle MSysResources in der Datenblattansicht.
- Wir klicken für einen neuen, leeren Datensatz doppelt auf das Anlage-Feld.
- Im Dialog Anlegen klicken wir auf Hinzufügen… und wählen die gewünschten .ico-Datei aus, zum Beispiel apple.ico.
- Danach tragen wir für das Feld Extension den Wert ico, für Name den Wert apple und für Type den Text img ein.
Danach können wir die Funktion SetFormIconFromMSysResources für dieses Icon aufrufen:
Call SetFormIconFromMSysResources(Me.hWnd, "apple")
Diese Vorgehensweise ist allerdings nicht besonders komfortabel, wenn wir mehrere .ico-Dateien zur Tabelle MSysResources hinzufügen wollen. Also schreiben wir uns schnell Code, um den Vorgang zu automatisieren.
Mehrere .ico-Dateien gleichzeitig in die Tabelle MSysResources einlesen
Was benötigen wir dazu als Erstes? Einen Dateiauswahl-Dialog, mit dem wir mehrere Dateien aus einem Verzeichnis auswählen können. Die notwendigen Elemente finden wir in der Office-Bibliothek, die wir wie in Bild 3 per Verweis referenzieren.
Bild 3: Verweis auf die Office-Bibliothek
.ico-Dateien auswählen
Danach können wir uns eine Funktion namens SelectICOFiles wie in Listing 2 zusammenstellen. Diese nimmt mit dem Parameter strFolder ein Verzeichnis entgegen, das als Startverzeichnis des Dateiauswahl-Dialogs verwendet wird. Zuerst werden das Objekt objFiledialog und zwei Variablen für die Verarbeitung der Pfade zu den .ico-Dateien deklariert. Mit der Variablen objFiledialog referenzieren wir den Dateiauswahl-Dialog, den wir mit der Methode Filedialog des Application-Objekts initialisieren. Dabei übergeben wir den Wert msoFileDialogFilePicker als Parameter.
Public Function SelectICOFiles(Optional strFolder As String) As String Dim objFiledialog As Office.FileDialog Dim varFilename As Variant Dim strFilenames As String Set objFiledialog = Application.FileDialog(msoFileDialogFilePicker) objFiledialog.AllowMultiSelect = True objFiledialog.InitialFileName = strFolder objFiledialog.Filters.Clear objFiledialog.Filters.Add "Icon-Files", "*.ico" If objFiledialog.Show = True Then For Each varFilename In objFiledialog.SelectedItems strFilenames = strFilenames & ";" & varFilename Next varFilename If Len(strFilenames) > 0 Then strFilenames = Mid(strFilenames, 2) End If End If SelectICOFiles = strFilenames End Function
Listing 2: Funktion zum Auswählen der .ico-Dateien
Für den Dialog legen wir mit AllowMultiSelect fest, das mehrere Dateien gleichzeitig ausgewählt werden können. Außerdem stellen wir den Startordner mit InitialFileName ein. Wir leeren die Filter mit der Clear-Methode der Filters-Auflistung und fügen einen neuen Filter für .ico-Dateien hinzu. Dann zeigen wir den Dialog mit der Show-Methode an. Liefert diese den Wert True zurück, hat der Benutzer den Dialog geschlossen und wir werten die gewählten Einträge in einer For Each-Schleife über die Elemente der Auflistung SelectedItems aus.
Dabei stellen wir in strFilenames eine durch Semikola getrennte Liste von Elementen zusammen und geben diese als Funktionsergebnis zurück.
Damit können wir nun .ico-Dateien wie in Bild 4 auswählen.
Bild 4: Auswahldialog für .ico-Dateien
.ico-Dateien einlesen
Danach verwenden wir eine Prozedur namens ICOToMSysResources, um die mit dem Dateiauswahl-Dialog selektierten Dateien in die Tabelle MSysResources einzulesen (siehe Listing 3).
Public Sub ICOToMSysResources(Optional bolOverwrite As Boolean) Dim db As DAO.Database Dim rst As DAO.Recordset Dim rstData As DAO.Recordset Dim strFileList As String Dim strFiles() As String Dim strFile As String Dim strName As String Dim i As Integer Set db = CurrentDb strFileList = SelectICOFiles(CurrentProject.Path) If Len(strFileList) > 0 Then strFiles = Split(strFileList, ";") Set rst = db.OpenRecordset("SELECT * FROM MSysResources", dbOpenDynaset) For i = LBound(strFiles) To UBound(strFiles) strFile = strFiles(i) strName = Mid(strFile, InStrRev(strFile, "\") + 1) strName = Replace(strName, ".ico", "") rst.FindFirst "Name = ''" & strName & "''" If Not (rst.NoMatch = False And bolOverwrite = False) Then If rst.NoMatch = True Then rst.AddNew Else rst.Edit End If rst!Name = strName rst!Extension = "ico" rst!Type = "img" Set rstData = rst("Data").Value On Error Resume Next rstData.Delete On Error Goto 0 rstData.AddNew rstData("FileData").LoadFromFile strFile rstData.Update rst.Update End If Next i End If End Sub
Listing 3: Prozedur zum Einlesen von .ico-Dateien in die Tabelle MSysResources
Die Prozedur hat einen Parameter namens bolOverwrite. Mit diesem legen wir fest, ob vorhandene Elemente, die den gleichen Namen im Feld Name aufweisen, überschrieben werden sollen.
Neben der Variablen db zum Referenzieren des aktuellen Database-Objekts verwenden wir zwei Recordset-Variablen. Die Erste namens rst referenziert ganz normal die Tabelle MSysResources.
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
den kompletten Artikel im PDF-Format mit Beispieldatenbank
diesen und alle anderen Artikel mit dem Jahresabo