ADXExcelTaskPane & Excel Window

Add-in Express™ Support Service
That's what is more important than anything else

ADXExcelTaskPane & Excel Window
How to pick or know which window the TaskPane is attached to 
Michael Thrift




Posts: 41
Joined: 2015-06-09
In adapting our existing Add-In to support Office 2013 and above, we are having to contend with the switch from MDI to SDI.

Part of our current process with Office 2010 is to create a TaskPaneCollectionItem & a TaskPaneInstance when a user opens up an "Application" from our Add-In ribbon.

The term "application" in this context represents a TaskPane with a UserControl and an EXCEL Workbook. The TaskPane and the embedded UserControl is directly tied to the workbook, and is not relevant for any other workbook open in EXCEL.

When an application is opened, it also results in the dynamic creation and opening of a new workbook. The workbook and the TaskPane are then tied together so that we can activate the TaskPane when the workbook is activated, and we can dispose of the TaskPane when the workbook is closed.

What is currently happening, is the TaskPane is created BEFORE the workbook, so the TaskPane gets attached to the "Book 1" window, while the Application window gets a blank copy of a task pane. It occurs before the workbook is created, because right now, I pass the TaskPane instance into my constructor of the method that builds the workbook, so the workbook can have a reference to it.

My thought is to PREVENT Add-In Express from creating a TaskPane instance automatically when a new window is created, because we only want the task pane to appear for the specific application it is tied to. I know that I can cancel the Task Pane creation in ADXBeforeTaskPaneInstanceCreate. However, I want to create my own instance of a TaskPane and ensure that it is attached to the new workbook window.

When is the best time to do this to ensure it gets attached to the correct window?
Posted 28 Sep, 2016 10:59:36 Top
Andrei Smolin


Add-in Express team


Posts: 18821
Joined: 2006-05-11
Hello Michael,

The link between an Excel workbook and a given instance of the task pane may exist only if the task pane instance contains some information related to the workbook. I suggest that you broke this link by introducing another UserControl (#2) containing this information as well as the UserControl (#1) that you currently have. This allows you to show the UserControl #2 on any task pane suitable. For instance, if the user opens the same workbook in several windows, Add-in Express will create several (e. g. N) task pane instances and your code may show several (e.g. M) instances of the UserControl #2 on some or all of these task pane instances.

Also, this allows you to show/hide an Excel task pane when you need this. To show/hide the task you call the Show and hide methods available on the task pane class. To prevent a task pane instance from being shown, you set this.Visible=true (Me.Visible = True in VB.NET) in the ADXTaskPaneBeforeShow event of the task pane class (in your case this is ADXExcelTaskPane).

What do you think?


Andrei Smolin
Add-in Express Team Leader
Posted 29 Sep, 2016 06:48:17 Top
Michael Thrift




Posts: 41
Joined: 2015-06-09
See code below. How does the CreateTaskPaneInstance() method know which window to associate itself with? The only place that I have found where the TaskPane gets associated with the correct window, is if I place the code in the Window.Activate() or Workbook.Activate() event. The problem is that the event first fires when the workbook is created, and I haven't yet identified that workbook as an application that needs a taskpane attached to it. I certainly don't want my logic to fire everytime a workbook gets activiated.

Right now I am employing a CancelTaskPane Boolean to suppress the creation of task pane instances unless I create them.


 Public Function CreateTaskPane(pPos As AddinExpress.XL.ADXExcelTaskPanePosition) As PwrToolsTaskPane
        Dim objUL As New clsUserLog
        objUL.StartUserLog()
        Try
            'Create The Task Pane
            CancelTaskPane = False
            If pPos <> ADXExcelTaskPanePosition.Right And pPos <> ADXExcelTaskPanePosition.Left Then
                pPos = ADXExcelTaskPanePosition.Right
            End If
            Dim TaskPaneCI As ADXExcelTaskPanesCollectionItem = _
           PwrToolsTaskPaneManager.Items.Add(New ADXExcelTaskPanesCollectionItem With { _
                                             .TaskPaneClassName = "FastPowerTools.PwrToolsTaskPane", _
                                             .AlwaysShowHeader = True, _
                                             .IsDragDropAllowed = True, _
                                             .CloseButton = False, _
                                             .Position = pPos, _
                                             .AllowedDropPositions = (ADXExcelAllowedDropPositions.Right Or ADXExcelAllowedDropPositions.Left), _
                                             .DefaultRegionState = ADXRegionState.Normal, _
                                             .Enabled = True})

            'Get Index Of TaskPaneCI In Collection
            Dim x As Integer = PwrToolsTaskPaneManager.Items.Count - 1

            'Create The Task Pane Instance
            Dim TaskPane As PwrToolsTaskPane = PwrToolsTaskPaneManager.Items(x).CreateTaskPaneInstance()
            CancelTaskPane = True
            Return TaskPane
            objUL.EndUserLog()
        Catch ex As Exception
            objUL.EndUserLog(, , clsUserLog.LOGTYPE.EROR, objUL.GetExMessage(ex))
            Throw New Exception(objUL.GetExMessage(ex))
        End Try
    End Function
Posted 29 Sep, 2016 08:18:53 Top
Alexander Solomenko




Posts: 140
Joined: 2009-02-27
Hi Michael,

The CreateTaskPaneInstance() method simply creates an instance of the form. The form is associated with the Excel window at the moment when it is being shown, i.e. when the Show method is called. In this case, the form is shown in the active Excel window. I will send you (to a personal email) a sample project that displays a TaskPane on a ribbon button click in any Excel window. Also, I'd like to draw your attention to the fact that after the creation of a TaskPane instance, it will remain in the TaskpaneInstances collection until you close the Excel window. TaskpaneManager will destroy the TaskPane instance on its own. No calls of the Dispose method should be done.
Regards,
Aleksandr Solomenko
Posted 30 Sep, 2016 09:36:53 Top
Michael Thrift




Posts: 41
Joined: 2015-06-09
This is helpful information, and thank you for sending the sample project. See code below.

Your button created and displayed a taskpane on the excel window that the user was on when they clicked the button. Our Add-In opens an "App" which requires a new workbook to be created. In the code below, a new workbook is opened, but the TaskPane gets attached to the original window, NOT the window associated with xlWB. What is interesting, is the Window.Activate() and Workbook.Activate() event fires immediately after you execute ExcelApp.Workbooks.Add(), and yet the taskpaneinstance does not recognize this as the active workbook.

Is there a way to associate the TaskPane instance with xlWB all in this same method?


 Private Sub AdxRibbonButton1_OnClick(sender As Object, control As IRibbonControl, pressed As Boolean) Handles AdxRibbonButton1.OnClick
        'I added this code ******
        Dim xlWB As Excel.Workbook = ExcelApp.Workbooks.Add()
        '***********************
        Dim taskPane = Me.AdxExcelTaskPanesCollectionItem1.TaskPaneInstance
        If IsNothing(taskPane) Then
            CancelTaskpane = False
            taskPane = Me.AdxExcelTaskPanesCollectionItem1.CreateTaskPaneInstance
            CancelTaskpane = True
        End If
        If Not taskPane.Visible Then
            taskPane.Show()
        End If
    End Sub
Posted 30 Sep, 2016 13:35:55 Top
Michael Thrift




Posts: 41
Joined: 2015-06-09
The TaskPaneCollectionItem.FormsManager.HostHandle is set when the TaskPaneCollectionItem is created. Once the HostHandle is set, it doesn't retry to get the window, it just uses the window it has. If the TaskPaneCollectionItem is instantiated when the first window is active, it will not matter that a different window is active when the taskpaneinstnace is created, it will get the HostHandle from the FormsManager. And if it has already been set, it will be the original window.

I may be wrong of course, but if that is true, it would mean that the taskpanecollectionitem needs to be instantiated after the window is activated?
Posted 30 Sep, 2016 14:06:18 Top
Alexander Solomenko




Posts: 140
Joined: 2009-02-27
Hi Michael,

Michael Thrift writes:
Once the HostHandle is set, it doesn't retry to get the window, it just uses the window it has.


Whenever you switch to another Excel window, FormsManager.HostHandle is set to reflect the change; it always contains the handle of the active Excel window.

I added one more button to the sample, which opens a new workbook and adds a TaskPane there. Please check your email box.
Regards,
Aleksandr Solomenko
Posted 03 Oct, 2016 08:57:35 Top
Michael Thrift




Posts: 41
Joined: 2015-06-09
Thank you again for working with me on this. I need to clarify one additional point. While the TaskPane is technically managed at the Add-In / Excel Application level, the functionality of any single task pane we build is truly unique to the workbook being created, and therefore has a unique UserControl added to it, along with a unique title etc.

In your example, it is simply creating an instance of an existing TaskPaneCollectionItem that was pre-compiled. My CollectionItems are generated at runtime, when the user selects the application. When a user opens an "App" we want to do the following (in whatever order will work):

1. Dynamically create a workbook from code that is named after the App, and creates the worksheets needed. (this is something I am doing)
2. Create a TaskPane that is labeled after the App Name and has a custom user control(wpf) loaded into it. This has been done in the past by creating a new TaskPaneCollectionItem. (I believe my issue is that this item doesn't get re-created, only the 1st one, which is a blank collection item.)
3. Display the Task Pane with the newly opened workbook.

See code below. Consider how you would accomplish opening a TaskPane in a new Workbook if the TaskPaneManager had no collection items in it.

 Private Sub AdxRibbonButton2_OnClick(sender As Object, control As IRibbonControl, pressed As Boolean) Handles AdxRibbonButton2.OnClick


        Dim books As Excel.Workbooks = ExcelApp.Workbooks
        If Not IsNothing(books) Then
            CancelTaskpane = False
            Dim book As Excel.Workbook = books.Add()
            If Not IsNothing(book) Then
                Marshal.ReleaseComObject(book)
            End If
            Marshal.ReleaseComObject(books)
        End If
    End Sub



Thoughts?
Posted 03 Oct, 2016 10:19:59 Top
Andrei Smolin


Add-in Express team


Posts: 18821
Joined: 2006-05-11
Hello Michael,

TaskPaneCollectionItem is an entity that only contains a description of properties common for all task pane instances, e.g. you can set the TaskPaneCollectionItem so that all instances of the specified task pane class are docked to the specified side of the Excel window. If no other logic is involved, the TaskPanesManager creates a new instance of the specified task pane class and show it whenever it detects a new Excel window. To prevent a given task pane instance from displaying, you set this.Visible=false (Me.Visible = false in VB.NET) in the ADXTaskPaneBeforeShow event of the task pane class (in your case this is ADXExcelTaskPane).

To show a unique UI on a pane shown in an Excel windows, you can populate instances of a *single* task pane class with different controls, e.g. with different UserControls. This doesn't require creating a new TaskPaneCollectionItem which simplifies the logic a lot. To put a UserControl on the pane, you can use the Load, ADXBeforeTaskPaneShow, and ADXAfterTaskPaneShow events of the task pane class.

Does this make sense?


Andrei Smolin
Add-in Express Team Leader
Posted 04 Oct, 2016 06:22:49 Top
Michael Thrift




Posts: 41
Joined: 2015-06-09
Yes, I have been able to make this work now. When a user opens an "App", I store the necessary configuration information into a global object. I then modify the TaskPaneInstance after the InitalizeComponents() call in the constructor of the TaskPane.

The disconnect for me was an inability to get a hold of the TaskPaneInstance within the same method that created the workbook.

A small inconvenience is that we previously saved the panel location for each app independently. So if a user wanted one App's Task Pane on the left, while another App's TaskPane on the right, we accomplished this by having separate TaskPaneCollectionItems for each app. As of now, for Office 16, we are no longer doing this.
Posted 04 Oct, 2016 13:56:19 Top