Addin-Programmierung - Seite 7
von Frank Schüler
Dockendes Addin
Dockendes AddIn mit Todo-Funktion
Außer der Möglichkeit normale AddIns zu entwickeln, gibt es noch die Möglichkeit dockende AddIns zu entwickeln. Ein dockendes AddIn wird von der Entwicklungsumgebung so behandelt, als wäre es ein eigenes Fenster der Entwicklungsumgebung wie die Werkzeugleiste oder der Projektexplorer. Wie der Name ja schon sagt, handelt es sich um ein dockendes AddIn. Das bedeutet, das AddIn kann, wie die anderen Fenster von VB, an jeder beliebigen Stelle in der Entwicklungsumgebung an andere Fenster andocken. Ein dockendes AddIn kann über die VBIDE-Schnittstelle angesprochen werden z.B. über das Windows-Objekt.
Ein dockendes AddIn hat im eigentlichen nur zwei Unterschiede. Anstelle einer Form wird in einem dockenden AddIn-Projekt ein Userdokument eingesetzt. Der zweite Unterschied ist, das das Userdokument in ein Fenster der Entwicklungsumgebung umgewandelt wird. Dieses Umwandeln geschieht im Connect-Modul durch die Funktion "CreateToolWindow" des Window-Objektes und zwar beim Laden des AddIn. Dazu muss der Code im Connect-Modul etwas abgeändert werden. Hier der abgeänderte Code.
Option Explicit '------------------------------------------------------ ' Instanzzieren bzw. Implementieren der Verweise und Events '------------------------------------------------------ ' Variable zum zwischenspeichern ob Form gerade ' sichtbar ist oder nicht Public FormDisplayed As Boolean ' Variable zum Verweis auf die VBIDE-Schnittstelle ' (Microsoft Visual Basic x.x Extensibility) Public VBInstance As VBIDE.VBE ' Variable zum Verweis auf die Menü-Schnittstelle ' (Microsoft Office x.x Object Library) Dim mcbMenuCommandBar As Office.CommandBarControl ' Variable auf das Userdokument Dim mdobAddIn As dobAddIn ' Variable auf das Window-Objekt Dim mWindow As VBIDE.Window ' Implementierung der Menü-Events Schnittstelle ' Ereignisbehandlungsroutine für die Befehlsleiste Public WithEvents MenuHandler As CommandBarEvents 'ein dockendes AddIn benötigt eine GUID 'wird beim Laden es AddIns erzeugt Private DockingAddInGUID As String Sub Hide() On Error Resume Next FormDisplayed = False ' Fenster verstecken mWindow.Visible = False End Sub Sub Show() On Error Resume Next 'Variablen im Userdokument setzen Set mdobAddIn.Connect = Me Set mdobAddIn.VBInstance = VBInstance FormDisplayed = True 'Fenster anzeigen mWindow.Visible = True End Sub '------------------------------------------------------ 'this method adds the Add-In to VB '------------------------------------------------------ Private Sub AddinInstance_OnConnection(ByVal Application As Object, _ ByVal ConnectMode As AddInDesignerObjects.ext_ConnectMode, _ ByVal AddInInst As Object, custom() As Variant) On Error Goto error_handler 'save the vb instance Set VBInstance = Application 'Eintrag im Menü erzeugen Set mcbMenuCommandBar = AddToAddInCommandBar("DockingAddIn") 'Übergeben des Ereignisses an die Behandlungsroutine. Set Me.MenuHandler = VBInstance.Events.CommandBarEvents(mcbMenuCommandBar) If GetSetting(App.Title, "Settings", "DockingAddInGUID", "0") = "0" Then 'freie GUID ermittel, wenn noch keine vorhanden DockingAddInGUID = GUIDGen SaveSetting App.Title, "Settings", "DockingAddInGUID", DockingAddInGUID Else 'GUID laden DockingAddInGUID = GetSetting(App.Title, "Settings", "DockingAddInGUID", "0") End If 'Hier wird das Userdokument in ein Fenster umgewandelt Set mWindow = VBInstance.Windows.CreateToolWindow(AddInInst, _ "MyAddIn.dobAddIn", "DockingAddIn", DockingAddInGUID, mdobAddIn) If ConnectMode = ext_cm_AfterStartup Then If GetSetting(App.Title, "Settings", "DisplayOnConnect", "0") = "1" Then 'set this to display the form on connect Me.Show End If End If Exit Sub error_handler: MsgBox Err.Description End Sub '------------------------------------------------------ 'this method removes the Add-In from VB '------------------------------------------------------ Private Sub AddinInstance_OnDisconnection(ByVal RemoveMode As AddInDesignerObjects.ext_DisconnectMode, custom() As Variant) On Error Resume Next 'delete the command bar entry mcbMenuCommandBar.Delete 'shut down the Add-In If FormDisplayed Then SaveSetting App.Title, "Settings", "DisplayOnConnect", "1" FormDisplayed = False Else SaveSetting App.Title, "Settings", "DisplayOnConnect", "0" End If Unload mdobAddIn Set mdobAddIn = Nothing End Sub Private Sub IDTExtensibility_OnStartupComplete(custom() As Variant) If GetSetting(App.Title, "Settings", "DisplayOnConnect", "0") = "1" Then 'set this to display the form on connect Me.Show End If End Sub 'this event fires when the menu is clicked in the IDE Private Sub MenuHandler_Click(ByVal CommandBarControl As Object, handled As Boolean, CancelDefault As Boolean) Me.Show End Sub Function AddToAddInCommandBar(sCaption As String) As Office.CommandBarControl Dim cbMenuCommandBar As Office.CommandBarControl 'command bar object Dim cbMenu As Object On Error Goto AddToAddInCommandBarErr 'see if we can find the Add-Ins menu Set cbMenu = VBInstance.CommandBars("Add-Ins") If cbMenu Is Nothing Then 'not available so we fail Exit Function End If 'add it to the command bar Set cbMenuCommandBar = cbMenu.Controls.Add(1) 'set the caption cbMenuCommandBar.Caption = sCaption Clipboard.SetData LoadResPicture(101, 0) cbMenuCommandBar.PasteFace Set AddToAddInCommandBar = cbMenuCommandBar Exit Function AddToAddInCommandBarErr: End Function
Hier der Code zum ermitteln der nächsten freien GUID im Modul "modGenerateGUID".
'dieses Modul dient zum ermitteln 'der nächsten freien GUID Option Explicit Private Type GUID Data1 As Long Data2 As Long Data3 As Long Data4(8) As Byte End Type Private Declare Function CoCreateGuid Lib "ole32.dll" _ (pguid As GUID) As Long Private Declare Function StringFromGUID2 Lib "ole32.dll" _ (rguid As Any, ByVal lpstrClsId As Long, _ ByVal cbMax As Long) As Long Public Function GUIDGen() As String Dim uGUID As GUID Dim sGUID As String Dim tGUID As String Dim bGUID() As Byte Dim lLen As Long Dim RetVal As Long lLen = 40 bGUID = String(lLen, 0) CoCreateGuid uGUID RetVal = StringFromGUID2(uGUID, VarPtr(bGUID(0)), lLen) sGUID = bGUID If (Asc(Mid$(sGUID, RetVal, 1)) = 0) Then RetVal = RetVal - 1 GUIDGen = Left$(sGUID, RetVal) End Function
Der Code im Userdokument ist im Urzustand der gleiche wie in der Form.
'------------------------------------------------------ ' Instanzieren bzw. Implementieren der Verweise '------------------------------------------------------ ' Variable zum Verweis auf die VBIDE-Schnittstelle ' (Microsoft Visual Basic x.x Extensibility) Public VBInstance As VBIDE.VBE ' Variable auf den Designer bzw. Klassenmodul "Connect" Public Connect As Connect Option Explicit Private Sub CancelButton_Click() ' Verstecken der Form ' Ruft im Modul "Connect" die Sub "Hide" auf Connect.Hide End Sub Private Sub OKButton_Click() ' Gibt den vollständigen Pfad zur 'Visual Basic-Anwendung zurück. MsgBox "AddIn operation on: " & VBInstance.FullName End Sub
Einen kleine Nachteil hat aber dieses dockende AddIn: Wenn das Fenster des AddIn in der Entwicklungsumgebung den Fokus hat, werden einige Buttons in der Menüleiste deaktiviert. Verliert das Fenster den Fokus, ist alles wieder aktiviert. Dieses Problem tritt nur in der VB6-Entwicklungsumgebung auf. Ein dockendes AddIn in der VB5-Entwicklungsumgebung verursacht diese Problem nicht. Eine Lösung für das VB6 Focus-Problem gibt es aber bisher noch nicht. Eventuell handelt es sich hier um einen Bug in der VB6-IDE-Schnittstelle.
Eine Todo-Funktion im Projekt einbauen
Einige Programmierer vermissen eine Art Todo-Funktion für Ihre Projekte mit der man z.B. noch nicht erledigte Programmieraufgaben im Projekt abspeichern kann um später beim Laden des Projektes eine Überblick zu bekommen, was noch gemacht werden muss. Man könnte so eine Todo-Liste in eine separate Datei schreiben oder eine Funktion des VBProject-Objekt nutzen. Gemeint ist die ReadProperty- und die WriteProperty-Funktion dieses Objektes. Nicht zu verwechseln mit der Property-Funktion in anderen Objekten, mit der man die Eigenschaften eines Objektes auslesen und schreiben kann. Hier handelt es sich um eine Funktion, mit der man Benutzerdefinierte Einträge in die VBP-Datei eines Projektes schreiben kann. Diese Benutzerdefinierten Einträge werden nicht in der Entwicklungsumgebung angezeigt. Der Aufbau dieser Einträge entspricht der von INI-Dateien mit einer Sektion, Schlüssel und einem Wert. Eigenartigerweise tritt beim Auslesen dieser Einträge in VB ein Fehler auf, wenn noch kein Eintrag mit entsprechender Sektion usw. vorhanden ist. Bei INI-Dateien wird automatisch dieser fehlende Eintrag ohne Fehler angelegt. Bei VB leider nicht und so muss dieser Fehler per "On Error Resume Next" abgefangen werden und der fehlende Eintrag selbst erzeugt werden. Hier die beiden wichtigsten Sub für das Auslesen und Schreiben von benutzerdefinierten Einträgen. (Der komplette Code befindet sich zusammen im Projekt für das dockende AddIn)
Private Sub ReadTodo() ' Diese Sub liest das Benutzerdefinierte Property "Todo" ' aus der VBP-Datei des aktuellen Projektes aus 'diese Zeile muss sein On Error Resume Next tempStr = vbNullString 'aktuelles Projekt Set VBProj = VBInstance.ActiveVBProject 'beim auslesen tritt ein Fehler auf, 'wenn die Section, Key und Value noch nicht 'vorhanden sind, daher "On Error Resume Next" 'auslesen der Section "Todo" mit dem Key "List" 'Rückgabewert ist der Value von "List" tempStr = VBProj.ReadProperty("Todo", "List") 'wenn der Rückgabewert = vbNullString ist, 'dann eine Section, Key und Value anlegen If tempStr = vbNullString Then VBProj.WriteProperty "Todo", "List", "Empty" tempStr = "Empty" End If 'anzeigen für welches Projekt Label1.Caption = "ToDo für das Projekt: " & VBProj.Name 'wenn beim auslesen der Rückgabestring = "Empty" ist, 'dann tempStr = vbNullstring If Left$(tempStr, 5) = "Empty" Then tempStr = vbNullString 'wenn beim auslesen der Rückgabestring <> "Empty" ist, 'dann das Zeichen CHR(255) durch vbNewLine ersetzen 'und in der Textbox anzeigen Text1.Text = Replace(tempStr, Chr$(255), vbNewLine) 'wenn beim auslesen der Rückgabestring <> "Empty" ist, 'dann den "Löschen-Button" aktivieren If tempStr <> vbNullString Then Command1.Enabled = True 'Textänderungen isChanged = False 'Textbox bekommt den Focus Text1.SetFocus End Sub
Private Sub SaveTodo() ' Diese Sub schreibt das Benutzerdefinierten Property "Todo" ' in die VBP-Datei des aktuellen Projektes 'wenn Textlänge=0, dann schreibe "Empty" If Len(Text1.Text) = 0 Then 'VBProj.WriteProperty "Todo", "List", "Empty" VBProj.WriteProperty "Todo", "List", "Empty" Else 'wenn Textlänge<>0, dann ersetze vbNewLine durch CHR(255) 'schreibe in die VBP-Datei VBProj.WriteProperty "Todo", "List", Replace(Text1.Text, vbNewLine, Chr$(255)) End If End Sub
Das Zeichen CHR(255) wird hier benutzt, um den Zeilenumbruch (vbNewLine) umzuwandeln, da der Value-Wert keine Zeilenumbrüche erlaubt. Ein löschen der Sektion, wie bei INI-Dateien, ist über diese Funktionen aber nicht möglich.