RibbonX-Workshop - Dynamisches Menü
In diesem Teil lernen wir, wie man ein Menü mit dynamischen Inhalten füllen kann.
Zum Füllen des Menüs nutzen wir eine Tabelle mit dem Namen
Natürlich können Sie das dynamicMenu auch als Untermenü in ein vorhandenes Menü einbauen, oder dem dynamicMenu auch ein normales Menü oder ein weiteres dynamicMenu hinzufügen.
RibbonX-Code:
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="olLoad_DNM">
<ribbon>
<tabs>
<tab id="tab0" label="DynamicMenu">
<group id="grp0" label="Gruppe 1">
<dynamicMenu id="dmnu0" label="Sub Menu" getContent="GetContent_Menu1"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
Inhalt der Tabelle:
![Tabelle Dynamic Menu DynamicMenu](../img/images/inhalt_tabelle_dynamicmenu.png)
VBA-Code:
Option Private Module
Option Explicit
Public objRibbon As IRibbonUI
Public Sub olLoad_DNM(ribbon As IRibbonUI)
Set objRibbon = ribbon
End Sub
Sub GetContent_Menu1(control As IRibbonControl, ByRef XMLString)
Dim lngInhalt As Long
Dim strStartZeile As String
Dim strInhalt As String
Dim strEndZeile As String
Dim xlLastCell As Long
With ThisWorkbook.Sheets("MenuDescript")
xlLastCell = .Range("A" & Rows.Count).End(xlUp).Row
strStartZeile = "<menu xmlns=""http://schemas.microsoft.com/office/2009/07/customui"">"
For lngInhalt = 2 To xlLastCell
strInhalt = strInhalt & _
"<button id=""" & .Range("A" & lngInhalt).Value & """" & _
" label=""" & .Range("B" & lngInhalt).Value & """" & _
" screentip=""" & .Range("D" & lngInhalt).Value & """" & _
" onAction=""" & .Range("C" & lngInhalt).Value & _
"""/>"
Next
End With
strEndZeile = strStartZeile & strInhalt & " </menu>"
XMLString = strEndZeile
End Sub
Public Sub onAction_Button1(control As IRibbonControl)
MsgBox "Element " & control.ID & " gedrückt", 64, "Hinweis"
End Sub
Dynamisches Menü - Teil 2
In Teil 1 haben wir gelernt wie man ein Menü mit dynamischen Inhalten füllen kann. In diesem Teil gehen wir ein klein wenig weiter. Wir fügen mehrere dynamische Menüs ein in die wir Dateinamen aus verschiendenen Ordnern einlesen. Diese Dateien können dann über das erstellte Menü geöffnet werden. Im Beispiel benutze zwei auf Laufwerk C gespeicherte und mit Excel-Dateien gefüllte Ordner. Der erste Ordner trägte den Namen AAA und der zweite Ordner den Namen BBA.
Erstellen Sie zuerst eine neue Datei. Bennen Sie eine Tabelle in Datei Übersicht. Alle anderen Blätter bitte löschen.
Gehen Sie nun in den VBE und fügen Sie ein neues Modul ein. In dieses Modul den folgenden Code. Dieser Code wird später die Ordner auslesen und die Dateinamen temporär in der Tabelle Datei Übersicht in Spalte A ablegen.
Private lngCount As Long
Private sPfad As String
Public Function DateienAuflisten(sPfad As String)
Dim i As Long
With Application
.ScreenUpdating = False
.DisplayAlerts = False
End With
lngCount = 0
SearchFiles sPfad, "*.xl*"
If lngCount = 0 Then
MsgBox "Es wurde in der Ordnerstruktur" & sPfad & " keine Dateien gefunden!"
Exit Function
End If
DateienAuflisten = strList
With ThisWorkbook.Worksheets("Datei Übersicht")
.Range(.Cells(1, 1), .Cells(lngCount, 1)) = _
WorksheetFunction.Transpose(strList)
End With
With Application
.ScreenUpdating = False
.DisplayAlerts = False
End With
End Function
Private Sub SearchFiles(strFolder As String, strFileName As String)
Dim objFolder As Object
Dim objFile As Object
Dim objFSO As Object
Set objFSO = CreateObject("Scripting.FileSystemObject")
For Each objFile In objFSO.GetFolder(strFolder).Files
If objFile.Name Like strFileName Then
Redim Preserve strList(0 To 1, lngCount)
strList(0, lngCount) = objFile.Name
strList(1, lngCount) = objFile.Path
lngCount = lngCount + 1
End If
Next
For Each objFolder In objFSO.GetFolder(strFolder).Subfolders
SearchFiles strFolder & "\" & objFolder.Name, strFileName
Next
End Sub
Fügen Sie nun ein weiteres Modul ein und in diesen den folgenden Code. Dieser Code wird die dynamicMenu später mit den Dateinamen füllen. Damit man aber die Dateiendungen nicht erscheint trennen wir diese per Code einfach ab.
(Anmerkung: Das onLoad-Ereignis über der Prozedur "GetContent_Menu1" muss stehen bleiben).
Option Private Module
Option Explicit
Public objRibbon As IRibbonUI
Public Sub olLoad_DNM(ribbon As IRibbonUI)
Set objRibbon = ribbon
End Sub
Sub GetContent_Menu1(control As IRibbonControl, ByRef XMLString)
Dim lngInhalt As Long
Dim strStartZeile As String
Dim strInhalt As String
Dim strEndZeile As String
Dim xlLastCell As Long
Dim UOrdner As String
Dim wbName As String
Rem Ordnername
UOrdner = Replace(control.Tag, "/", "\")
Rem Hauptordner & Ordner angeben
ThisWorkbook.Worksheets("Datei Übersicht").Cells.ClearContents
DateienAuflisten (UOrdner)
Rem Prüfen ob Tabelle leer und aussteigen wenn leer
If ThisWorkbook.Worksheets("Datei Übersicht").Range("A1").Value = "" Then Exit Sub
Rem Letzte beschriebene Zelle suchen
xlLastCell = ThisWorkbook.Worksheets("Datei Übersicht").Range("A" & Rows.Count).End(xlUp).Row
With tbMenuscript
strStartZeile = "<menu xmlns=""http://schemas.microsoft.com/office/2009/07/customui"">"
For lngInhalt = 1 To xlLastCell 'Letzte Zeile 1.00
wbName = Left(ThisWorkbook.Worksheets("Datei Übersicht").Range("A" & lngInhalt).Value, _
InStr(1, ThisWorkbook.Worksheets("Datei Übersicht").Range("A" & lngInhalt).Value, ".") - 1)
strInhalt = strInhalt & _
"<button id=""" & "btn" & lngInhalt & """" & _
" label=""" & wbName & """" & _
" screentip=""" & ThisWorkbook.Worksheets("Datei Übersicht").Range("A" & lngInhalt).Value & """" & _
" imageMso=""" & "FooterInsertGallery" & """" & _
" onAction=""" & "xlOpenFile" & """" & " tag=""" & UOrdner & ThisWorkbook.Worksheets("Datei Übersicht").Range("A" & lngInhalt).Value & _
"""/>"
Next
End With
strEndZeile = strStartZeile & strInhalt & " </menu>"
XMLString = strEndZeile
End Sub
Die folgende Prozedur in ein beliebiges (allgemeines) Modul einfügen. Diese Prozedur wird von den Schaltflächen im dynamischen Menü aufgerufen (die entsprechende Datei geöffnet).
Public Sub xlOpenFile(control As IRibbonControl)
Workbooks.Open control.Tag
End Sub
Kommen wir nun zum RibbonX-Code. Im RibbonX-Code geben wir im TAG-Attribut den Pfad zu den Ordnern an. Da in RibbonX kein Backslash erlaubt ist müssen wir den einfachen Slash nutzen. Dieser Slash wird in der Prozedur wieder in einen Backslash umgewandelt. Sonderzeichen und Umlaute müssen maskiert werden (UNICODE).
Öffnen Sie die Datei mit dem CustomUI-Editor. In diese dann eine UI-Part einfügen und in diesen den folgenden Code.
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="olLoad_DNM">
<ribbon>
<tabs>
<tab id="tab0" label="Ordner">
<group id="grp0" label="Dateien">
<menu id="mnu0" label="1.0" size="large" imageMso="_1">
<dynamicMenu id="dmnu01" label="Ordner 1" tag="C:/AAA/" getContent="GetContent_Menu1"/>
<dynamicMenu id="dmnu02" label="Ordner 2" tag="C:/BBA/" getContent="GetContent_Menu1"/>
</menu>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
Dynamisches Menü - Teil 3
Im dritten Teil fügen wir dem dynamischen Menü dynamische Untermenüs hinzu. Im Beispiel wird das Menü vier mal "verschachtelt".
Der RibbonX-Code entspricht dem aus Teil 2. Für jedes dynamische Untermenü gibt es eine eigene Tabelle und eine eigene Prozedur ("xl07_dynamicmenu_Teil 3" im Download "Download Dynamic-Menu").
Ein Beispielcode für ein dynamisches Menü mit einem dynamischen Untermenü.
Sub GetContent_Menu1(control As IRibbonControl, ByRef XMLString)
Dim lngInhalt As Long
Dim strStartZeile As String
Dim strInhalt As String
Dim strEndZeile As String
Dim xlLastCell As Long
With ThisWorkbook.Sheets("Untermenu_1")
xlLastCell = .Range("A" & Rows.Count).End(xlUp).Row
strStartZeile = "<menu xmlns=""http://schemas.microsoft.com/office/2009/07/customui"">"
For lngInhalt = 2 To xlLastCell
strInhalt = strInhalt & _
"<button id=""" & .Range("A" & lngInhalt).Value & """" & _
" label=""" & .Range("B" & lngInhalt).Value & """" & _
" screentip=""" & .Range("D" & lngInhalt).Value & """" & _
" onAction=""" & .Range("C" & lngInhalt).Value & _
"""/>"
Next
End With
strEndZeile = strStartZeile & "<dynamicMenu id=""dmnu4"" label=""Sub Menu 2"" imageMso=""_2"" getContent=""GetContent_Menu2"" />" & strInhalt & " </menu>"
XMLString = strEndZeile
End Sub
Sub GetContent_Menu2(control As IRibbonControl, ByRef XMLString)
Dim lngInhalt As Long
Dim strStartZeile As String
Dim strInhalt As String
Dim strEndZeile As String
Dim xlLastCell As Long
With ThisWorkbook.Sheets("Untermenu_2")
xlLastCell = .Range("A" & Rows.Count).End(xlUp).Row
strStartZeile = "<menu xmlns=""http://schemas.microsoft.com/office/2009/07/customui"">"
For lngInhalt = 2 To xlLastCell
strInhalt = strInhalt & _
"<button id=""" & .Range("A" & lngInhalt).Value & """" & _
" label=""" & .Range("B" & lngInhalt).Value & """" & _
" screentip=""" & .Range("D" & lngInhalt).Value & """" & _
" onAction=""" & .Range("C" & lngInhalt).Value & _
"""/>"
Next
End With
strEndZeile = strStartZeile & strInhalt & " </menu>"
XMLString = strEndZeile
End Sub
Dynamisches Menü - Teil 4
Der 4. Teil ist an Teil 3 angelehnt. Hier erstellen wir mehrere dynamische Untermenüs, die wiederum mehrere Untermenüs und Schaltflächen enthalten. Nutzen Sie dafür die Beispieldatei "xl07_dynamicmenu_Teil 4" im ZIP-Archiv "xl07_DynamicMenu". Da der Code nahezu identisch ist verzichte ich hier auf das Zeigen eines Beispielcodes.
Dynamisches Menü - Teil 5
Im fünften Teil erstellen wir ein dynamisches Menü mit Hilfe einer Konfigurationsdatei.
Kommen wir zunächst zum Aufbau der Konfigurationsdatei.
[Menü1Eintrag1]
isSeparator=0
separatorID=mnu1Separator
label=Eintrag 1
MacroName=onAction_Button
ButtonID=mnu1Button
imageMSO=FilesToolAddFiles
tag=Tag1
screentip=
supertip=
keytip=
getLabel=
getScreentip=
getSupertip=
getImage=
[Menü1Eintrag2]
isSeparator=0
separatorID=mnu1Separator
label=Eintrag 2
MacroName=onAction_Button
ButtonID=mnu1Button
imageMSO=FileInternetFax
tag=Tag2
screentip=
supertip=
keytip=
getLabel=
getScreentip=
getSupertip=
getImage=
[Menü1Eintrag3]
isSeparator=0
separatorID=mnu1Separator
label=Eintrag 3
MacroName=onAction_Button
ButtonID=mnu1Button
imageMSO=FontEmboss
tag=Tag3A;Tag3A2
screentip=
supertip=
keytip=
getLabel=
getScreentip=
getSupertip=
getImage=
[Menü1Eintrag4]
isSeparator=0
separatorID=mnu1Separator
label=Eintrag 4
MacroName=onAction_Button
ButtonID=mnu1Button
imageMSO=FontSizeDecrease
tag=Tag4
screentip=
supertip=
keytip=
getLabel=
getScreentip=
getSupertip=
getImage=
Beschreibung:- In den Klammern tragen wir die Kennung für das Menü ein. Hier "Menü1Eintrag1", der Eintrag setz sich zusammen aus der "Menükennung" und dem Menüeintrag (Nummer). Das kann hilfreich sein wenn mehrere dynamische Menüs erstellt werden sollen.
- isSeparator = Soll bei dem Menüeintrag eine neue Gruppe beginnen setzen wir "isSeparator" auf 1.
- separatorID = Die ID für den Trennstrich. Die ID wird beim Zusammenstellen durch einen Zähler ergänzt. Dieses Attribut muss gefüllt sein wenn isSeparator auf 1 gesetzt ist..
- label = Hier geben wir an was auf der Schaltfläche stehen soll (Label). Dieses Attribut muss gefüllt sein. Nicht zusammen mit getLabel verwendbar.
- Macroname = Die auszuführende Prozedur geben wir hier an. Dieses Attribut muss gefüllt sein.
- ButtonID = Die ID für die Schaltfläche. Die ID wird beim Zusammenstellen durch einen Zähler ergänzt. Dieses Attribut muss gefüllt sein.
- imageMso = Das Icon welches die Schaltfläche bekommen soll. Die Namen bekommen Sie aus den bekannten Übersichten. Dieses Attribut darf leer sein.
- Tag = Das Tag-Attribut. Hier können Sie auch mehrere durch Semikolon getrennte Angaben machen. Dieses Attribut darf leer sein.
- screentip = Kurzbeschreibung (wird angezeigt beim draufzeigen). Dieses Attribut darf leer sein. Nicht zusammen mit getScreentip verwendbar.
- supertip = Langbeschreibung (wird angezeigt beim draufzeigen). Dieses Attribut darf leer sein. Nicht zusammen mit getSupertip verwendbar.
- keytip = Tastenkürzel. Maximal drei Zeichen. Nicht bereits verwendete Tastenkürzel nutzen. Dieses Attribut darf leer sein.
- getLabel = Variables Label (zur Laufzeit per Callback). Dieses Attribut darf leer sein. Nicht zusammen mit label verwendbar.
- getScreentip = Variabler Screentip (zur Laufzeit per Callback). Dieses Attribut darf leer sein. Nicht zusammen mit screentip verwendbar.
- getSupertip = Variabler Supertip (zur Laufzeit per Callback). Dieses Attribut darf leer sein. Nicht zusammen mit supertip verwendbar.
- getImage = Variables Icon (zur Laufzeit per Callback). Dieses Attribut darf leer sein. Nicht zusammen mit imageMso verwendbar.
Öffnen Sie nun Ihre Officedatei und wechseln Sie in dessen VBA-Projekt. Fügen Sie ein neues Modul ein und dort den folgenden Code. Wichtig: Bitte lassen Sie den Code möglichst unverändert.
Option Private Module
Option Explicit
Private Declare Function ReadXMLLayout Lib "kernel32.dll" Alias "GetPrivateProfileStringA" ( _
ByVal MenueName As String, ByVal MenueAttribut As String, ByVal NullString As String, _
ByVal LeerZeichen As String, ByVal Groesse As Long, ByVal INIPfad As String) As Long
Private Const MAX_COUNT = 255
Public Function MenueinhaltEinlesen(varMenueName As Variant, varMenueAttribut As Variant, varINIPfad As Variant) As String
Dim lngInhalt As Long
Dim strReturn As String
strReturn = Space$(MAX_COUNT)
lngInhalt = ReadXMLLayout(varMenueName, varMenueAttribut, vbNullString, strReturn, MAX_COUNT, varINIPfad)
MenueinhaltEinlesen = Left$(strReturn, lngInhalt)
End Function
Public Function ErstelleMenueInhalt(strConfigPath As String, strMenuPoint As String, lngFrom As Long, lngTo As Long) As String
Dim lngConfKeyCount As Long
Dim lngMenuPos As Long
Dim varXMLParams As Variant
Dim varConfKeyName As Variant
Dim varXMLParamsPos As Variant
Dim varConfKeys As Variant
Dim varSepCount As Variant
varConfKeyName = Split("separatorID isSeparator ButtonId MacroName tag imageMSO label screentip supertip keytip getImage getLabel getScreentip getSupertip")
varXMLParams = Split("<menuSeparator id_isSeparator_<button id_onAction_tag_imageMso_label_screentip_supertip_keytip_getImage_getLabel_getScreentip_getSupertip", "_")
For lngMenuPos = lngFrom To lngTo
varXMLParamsPos = varXMLParams
For lngConfKeyCount = 0 To UBound(varXMLParamsPos)
varConfKeys = MenueinhaltEinlesen(strMenuPoint & lngMenuPos, varConfKeyName(lngConfKeyCount), strConfigPath)
If lngConfKeyCount = 1 Then
If varConfKeys <> "1" Then varXMLParamsPos(0) = ""
varConfKeys = ""
End If
If varConfKeys <> "" Then varXMLParamsPos(lngConfKeyCount) = varXMLParamsPos(lngConfKeyCount) & "=""" & _
varConfKeys & IIf(lngConfKeyCount < 3, lngMenuPos, "") & IIf(lngConfKeyCount = 0, """/>", """")
Next lngConfKeyCount
For lngConfKeyCount = UBound(varXMLParamsPos) - 3 To UBound(varXMLParamsPos)
If InStr(varXMLParamsPos(lngConfKeyCount), "=") Then varXMLParamsPos(lngConfKeyCount - 5) = ""
Next lngConfKeyCount
ErstelleMenueInhalt = ErstelleMenueInhalt & Join(Filter(varXMLParamsPos, "=")) & "/>"
Next lngMenuPos
End Function
- Die Funktion ErstelleMenueInhalt erstellt den XML-Code für das dynamische Menü. Diese Funktion berücksichtigt automatisch sich gegenseitig ausschließende Attribute. Sollten Sie z.B. versehentlich label und getLabel in der Konfigurationsdatei füllen wird nur getLabel berücksichtig und label ignoriert.
- Die Funktion MenueinhaltEinlesen liest die Parameter aus der Konfigurationsdatei aus und übergibt sie an die Funktion ErstelleMenueInhalt
Fügen Sie nun ein weiteres Modul für die Rückruffunktionen (Callbacks) der Standardeinträge ein.
Option Private Module
Option Explicit
Public Sub onAction_Button(Optional control As IRibbonControl)
MsgBox "Es wurde die Schaltfläche mit der ID " & control.ID & " gedrückt." & vbCrLf & vbCrLf & _
"Das Tag-Attribut enthält den Eintrag:" & vbCrLf & control.Tag, 64, control.ID
End Sub
Fügen Sie nun ein weiteres Modul ein. In dieses legen wir die Rückruffunktion für das dynamische Menü.
Option Private Module
Option Explicit
Public objRibbon As IRibbonUI
Public Sub olLoad_DNM(ribbon As IRibbonUI)
Set objRibbon = ribbon
End Sub
Sub GetContent_Menu1(control As IRibbonControl, ByRef XMLString)
'######## Beginn Deklarationen ######
Dim strConfPath As String
Dim strStartZeile As String
Dim strInhalt As String
Dim strEndZeile As String
'######## Ende Deklarationen ######
'######## Konfigurationsdatei ######
strConfPath = ThisDocument.Path & "\" & Left(ThisDocument.Name, InStr(1, ThisDocument.Name, ".") - 1) & ".ini"
'######## Startzeile erstellen ######
strStartZeile = "<menu xmlns=""http://schemas.microsoft.com/office/2009/07/customui"">"
'######## Menü setzen ######
strInhalt = ErstelleMenueInhalt(strConfPath, "Menü1Eintrag", 1, 4)
'######## Endzeile erstellen ######
strEndZeile = strStartZeile & strInhalt & " </menu>"
'######## XML-String zurückgeben ######
XMLString = strEndZeile
End Sub
- In der Zeile Konfigurationsdatei wird der Pfad zur Konfigurationsdatei angegeben. Im Beispiel befindet sie sich im Pfad der Worddatei und trägt den Namen der Worddatei.
- In der Zeile Menü setzen übergeben wir den Pfad, das gewünschte Menü (hier Menü1) mit den Zusatz "Eintrag" (ohne die Nummer die in der Konfiguration hinter "Eintrag" steht) und Start und Ziel der Menüeinträge (wenn Sie wie im Beispiel 4 Einträge haben geben Sie 1 als Start und 4 als Ziel ein) an die Funktion ErstelleMenueInhalt welche uns den XML-Code für den Menüinhalt zurückgibt.
Speichern und schließen Sie die Officedatei und öffnen Sie sie im Custom-UI-Editor. Fügen Sie das Office 2010 Office UI Part ein (für Office 2007 das entsprechende Office-UI-Part mit Anpassung des Namespaces) und dorthinein den XML-Code.
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui">
<ribbon>
<tabs>
<tab idMso="TabHome">
<group id="grp0" label="Menü1" insertBeforeMso="GroupClipboard">
<dynamicMenu id="dmnu0" screentip="Menütest" imageMso="CreateSiteWorkflow" size="large" getContent="GetContent_Menu1"/>
<separator id="spr2" />
<button id="btn0" screentip="Schaltfläche 1" tag="Tag1" imageMso="FilePrint" size="normal" onAction="onAction_Button" />
<button id="btn1" screentip="Schaltfläche 2" tag="Tag2A1;Tag2A2;Tag2A3" imageMso="FilePrintQuick" size="normal" onAction="onAction_Button" />
<button id="btn2" screentip="Schaltfläche 3" tag="Tag3" imageMso="FilePermission" size="normal" onAction="onAction_Button" />
<button id="btn3" screentip="Schaltfläche 4" tag="Tag4" imageMso="PictureColorMenu" size="normal" onAction="onAction_Button" />
<button id="btn4" screentip="Schaltfläche 5" tag="Tag5A1;Tag5A2" imageMso="PictureRemovePicture" size="normal" onAction="onAction_Button" />
<button id="btn5" screentip="Schaltfläche 6" tag="Tag6" imageMso="PictureBackgroundRemovalMarkForeground" size="normal" onAction="onAction_Button" />
<button id="btn6" screentip="Schaltfläche 7" tag="Tag7" imageMso="PictureBackgroundRemovalMarkBackground" size="normal" onAction="onAction_Button" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
Natürlich können Sie auch hier Menüs kaskadieren. Auch dafür finden Sie Beispiele im ZIP-Arciv "xl07_DynamicMenu", dort im Ordner "wd_dynamicmenu_Teil 5". Beispiele herunterladen