none
Office 2010 VSTO Shared Addin - Toolbar Button Handler issue RRS feed

  • Question

  • Hi Everyone,

    I have written an Application Level Shared Office Addin which runs in Word 2010, Excel 2010 and Outlook 2010.

    I put a button in a toolbar which is displayed under the "Addins" tab in all 3 applications.

    The issue I am having is that the code to create the toolbar, add the button to the toolbar and add the handler to the button all gets done in the OnConnection routine and therefore by the time I have launched Word/Excel/Outlook and attached the debugger to the application instance all the code has been executed already so I'm struggling to find out exactly what is happening.

     

    The code below is pretty standard I would imagine - The indexPaneButton_Click Event is where all my functionality happens.

    Public Sub OnConnection(ByVal application As Object, ByVal connectMode As Extensibility.ext_ConnectMode, ByVal addInInst As Object, ByRef custom As System.Array) Implements Extensibility.IDTExtensibility2.OnConnection
            Try
                ' Setup code for the add-in.
                SetApplicationFields(application)
                addInInstance = addInInst
    
                ' More setup code for the add-in.
                toolBar = AddToolbar(wordApp, excelApp, outlookApp)
    
                ' Create a button to add text.
                indexPaneButton = MakeANewButton(toolBar, applicationName,
                    New Microsoft.Office.Core._CommandBarButtonEvents_ClickEventHandler(AddressOf indexPaneButton_Click))
            Catch ex As System.Exception
                ' Add exception handling here.
            End Try
        End Sub
    
        Private Sub SetApplicationFields(ByVal application As Object)
            If TypeOf application Is Microsoft.Office.Interop.Word.Application Then
                wordApp = DirectCast(application, Microsoft.Office.Interop.Word.Application)
                excelApp = Nothing
                outlookApp = Nothing
            ElseIf TypeOf application Is Microsoft.Office.Interop.Excel.Application Then
                excelApp = DirectCast(application, Microsoft.Office.Interop.Excel.Application)
                wordApp = Nothing
                outlookApp = Nothing
            ElseIf TypeOf application Is Microsoft.Office.Interop.Outlook.Application Then
                outlookApp = DirectCast(application, Microsoft.Office.Interop.Outlook.Application)
                wordApp = Nothing
                excelApp = Nothing
            Else
                wordApp = Nothing
                excelApp = Nothing
                outlookApp = Nothing
            End If
        End Sub
    
        Private Function AddToolbar(ByVal word As Microsoft.Office.Interop.Word.Application, _
                                    ByVal excel As Microsoft.Office.Interop.Excel.Application, _
                                    ByVal outlook As Microsoft.Office.Interop.Outlook.Application) As Microsoft.Office.Core.CommandBar
            Try
                ' Create a command bar for the add-in
                If word IsNot Nothing Then
                    toolBar = DirectCast(wordApp.CommandBars.Add(buttonName, Microsoft.Office.Core.MsoBarPosition.msoBarTop, missing, True), Microsoft.Office.Core.CommandBar)
                    toolBar.Visible = True
                    Return toolBar
                ElseIf excel IsNot Nothing Then
                    toolBar = DirectCast(excelApp.CommandBars.Add(buttonName, Microsoft.Office.Core.MsoBarPosition.msoBarTop, missing, True), Microsoft.Office.Core.CommandBar)
                    toolBar.Visible = True
                    Return toolBar
                ElseIf outlook IsNot Nothing Then
                    toolBar = DirectCast(outlookApp.ActiveExplorer().CommandBars.Add(buttonName, Microsoft.Office.Core.MsoBarPosition.msoBarTop, missing, True), Microsoft.Office.Core.CommandBar)
                    toolBar.Visible = True
                    Return toolBar
                End If
                Return Nothing
            Catch ex As System.Exception
                ' Add exception handling here.
                Return Nothing
            End Try
        End Function
    
        Private Function MakeANewButton(ByVal commandBar As Microsoft.Office.Core.CommandBar, ByVal caption As String, ByVal clickHandler As Microsoft.Office.Core._CommandBarButtonEvents_ClickEventHandler) As Microsoft.Office.Core.CommandBarButton
            Try
                Dim newButton As Microsoft.Office.Core.CommandBarButton
                newButton = DirectCast(commandBar.Controls.Add(Microsoft.Office.Core.MsoControlType.msoControlButton, missing, missing, missing, missing), Microsoft.Office.Core.CommandBarButton)
                newButton.Caption = caption
                newButton.Style = Microsoft.Office.Core.MsoButtonStyle.msoButtonIconAndWrapCaptionBelow
                newButton.Enabled = True
    
                'Stream imgageStream =
                newButton.Picture = DocumotiveOfficeAddInIDS2010.MyHost.GetIPictureDispFromPicture(My.Resources.icon)
    
                AddHandler newButton.Click, AddressOf indexPaneButton_Click
                Return newButton
            Catch ex As System.Exception
                ' Add code here to handle the exception.
                Return Nothing
            End Try
        End Function
    

    The issue is this......I can open word with a blank page and open and close my windows form (by clicking the button) and this happens without issue. If I then click on the "File" tab and open a new document my Addin no longer responds to button clicks.

    I have a break point on my button click event and it just never fires. So clearly I am losing the handler but I don't know where or at what point nor can I work out how to re-add the handler if it has gone.

     

    has anyone experienced this issue themselves? Better still does anyone have a solution. This addin is supposed to be a commercial product so it's not really acceptable to expect a user to close and re-open word with a document each time they want to use the product.

     

    Ideally once the addin has loaded, it should be available for the life of the instance of Word/Excel/Outlook and not only for the life of the currently loaded document, afterall it is an Application Addin and not a document addin.

    Wednesday, October 26, 2011 6:05 PM

Answers

  • Hi Joseph

    your remark about Word's multiple windows has been niggling at the back of my mind and I was just able to chase it down.

    for Word, we need to set the TAG property of button objects in order to ensure they remain available for all document windows. I don't see that Subtle 81 sets a value to the Tag property...


    Cindy Meister, VSTO/Word MVP
    • Marked as answer by Subtle81 Thursday, October 27, 2011 3:00 PM
    Thursday, October 27, 2011 1:27 PM
    Moderator

All replies

  • I may be way off, but perhaps the way you've done it, when you first open office, a memory address is assigned for indexPaneButton_Click. When you open a new document the existing indexPaneButton_Click memory address is used to hold the event of a new button that is created. That new button takes the place of the old as the class member variable, and the garbage collector deletes the the handler as it occupies the address of the old button.

    Even if I'm wrong, which I probably am, what happens if you only let the setup calls of your code run once? Try putting a bool/flag in the Addin.

    Thursday, October 27, 2011 12:49 AM
  • Hi Subtle

    The problem, I believe, is that you're not declaring the objects that represent the toolbar and its buttons at the class level. As soon as OnConnection and the other procedures end, all the objects you declare go out of scope. And at some indeterminate point in time, the .NET Garbage Collection goes through and cleans them up. When that happens, they no longer exist in memory and thus the event is "disconnected".

    Declare toolbar and new button outside the procedures and I think this problem will go away...

    With Word you should also be setting the CustomizationContext (the document-container in which the toolbar and anything to do with it are saved). If you don't set that, it could happen that you'll start seeing buttons twice, or when your application isn't running...

    You, as a developer, should not be writing things to the user's Normal Template. Since you want these things to be global, best would be to distribute a template as part of your application. Use Word's Application.Addins collection to Load and Unload your template, as required. Use this as the repository for your toolbar customizations in Word.


    Cindy Meister, VSTO/Word MVP
    Thursday, October 27, 2011 12:41 PM
    Moderator
  • My initial thought was they weren't declared at class level, but from Subtle 81's posts they are, aren't they? 'toolBar' and 'indexPaneButton' are, and everything else is only relevant to the construction of those objects.

    Thursday, October 27, 2011 12:47 PM
  • Hi Joseph

    Well, I don't find declarations for toolbar or indexpanebutton, but I do for

    Dim newButton As Microsoft.Office.Core.CommandBarButton

    And this is inside MakeANewButton...

    Of course, if this procedure (and object) is going to be used to make multiple buttons, then what's needed is a class-level array or collection to store multiple such objects.


    Cindy Meister, VSTO/Word MVP
    Thursday, October 27, 2011 1:12 PM
    Moderator
  • Subtle 81 hasn't posted declarations, but he appears to have posted the entirety of those methods, so by elmination I take the declarations to be at class level.

    That button is returned in the method; it is assigned to indexPaneButton, which I assume to be a class level variable.

    Subtle 81, care to clarify?


    (I am taking great joy in this Cindy, because it is so rare that my knowledge/insight into an Office programming situation exceedes yours).

    • Edited by JosephFox Thursday, October 27, 2011 1:27 PM
    Thursday, October 27, 2011 1:20 PM
  • Hi Joseph

    your remark about Word's multiple windows has been niggling at the back of my mind and I was just able to chase it down.

    for Word, we need to set the TAG property of button objects in order to ensure they remain available for all document windows. I don't see that Subtle 81 sets a value to the Tag property...


    Cindy Meister, VSTO/Word MVP
    • Marked as answer by Subtle81 Thursday, October 27, 2011 3:00 PM
    Thursday, October 27, 2011 1:27 PM
    Moderator
  • Well, predictably the moment was fleeting. Likely it is the tag thing, and/or customisation context. Although it's strange it works for one window, and my impression is that when another window is opened the button stops working entirely.


    • Edited by JosephFox Thursday, October 27, 2011 1:32 PM Typo
    Thursday, October 27, 2011 1:31 PM
  • Hi Both,

     

    Firstly thanks for replying.....it gives me hope. To clarify here are my declarations I left them out originally as I thought I'd provided enough detail. Never enough detail!

    Public Class Connect
    
        Implements Extensibility.IDTExtensibility2
    
    
        Private applicationObject As Object
        Private addInInstance As Object
        Private missing As Object = System.Reflection.Missing.Value
        Private wordApp As Microsoft.Office.Interop.Word.Application = Nothing
        Private excelApp As Microsoft.Office.Interop.Excel.Application = Nothing
        Private outlookApp As Microsoft.Office.Interop.Outlook.Application = Nothing
        Private toolBar As Microsoft.Office.Core.CommandBar = Nothing
        Private indexPaneButton As Microsoft.Office.Core.CommandBarButton = Nothing
        Private buttonName As String = "Documotive 4"
    


    I also imported

     

    Imports Extensibility
    Imports System.Runtime.InteropServices

     

    Hopefully this will clarify if my application is Application or Document level. This is my first add in so my knowledge is limited. 

    Thursday, October 27, 2011 2:33 PM
  • Sure. Jolly good.

    So, what happens if you set the TAG property of the button, like Cindy proposed?

    And an minor thing, when you stay it stops working when you open a new document, is that application-wide? Or, it still works on the original document you opened?

    Yes, never enough detail :)

    Thursday, October 27, 2011 2:39 PM
  • Hi,

    I have added "MyButton" to the tag property (I'm not sure what should actually go in this property and MSDN only confused me further).

     

    And this has indeed seemed to resolve the issue.

     

    I can now open word with a blank document, open my addin, close my addin, open a new document (File Tab > Recent Docs) and now if I click on my toolbar button my addin is launched whereas before the button clicks would not fire the event.

     

    Thanks very much

    • Proposed as answer by JosephFox Thursday, October 27, 2011 3:01 PM
    • Unproposed as answer by JosephFox Thursday, October 27, 2011 3:01 PM
    Thursday, October 27, 2011 3:00 PM