Instantiating the TaskPanes

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

Instantiating the TaskPanes
 
Kirill Lapin


Guest


Hi there. Happy 2019 to everyone!

Here goes my first query of the year :)

Setup:
- My application uses 3 task panes in Excel
- Each task pane contains a user control (1. Data Form, 2. TreeView and 3. DataGridView)
- The three user controls interact between them (e.g. on DataGridView_CurrentCellChange event a node is selected in the TreeView)
- I want my application to be able to run independently in as many instances as the workbooks opened by the user
- I am declaring the task pane objects from inside each User Control like so:
        Private TaskPaneItem1 As AddinExpress.XL.ADXExcelTaskPanesCollectionItem
        Private TaskPaneItem2 As AddinExpress.XL.ADXExcelTaskPanesCollectionItem
        Private TaskPaneItem3 As AddinExpress.XL.ADXExcelTaskPanesCollectionItem

        Private taskPane1 As ADXExcelTaskPane1
        Private taskPane2 As ADXExcelTaskPane2
        Private taskPane3 As ADXExcelTaskPane3

then I assign the instances to the variables at the UC Load event:
        Private Sub UserControl1_Load(sender As Object, e As EventArgs) Handles Me.Load
            'Assign task pane object instances
            TaskPaneItem1 = AddinModule.CurrentInstance.AdxExcelTaskPanesCollectionItem1
            TaskPaneItem2 = AddinModule.CurrentInstance.AdxExcelTaskPanesCollectionItem2
            TaskPaneItem3 = AddinModule.CurrentInstance.AdxExcelTaskPanesCollectionItem3

            taskPane1 = TryCast(TaskPaneItem1.TaskPaneInstance, ADXExcelTaskPane1)
            If taskPane1 Is Nothing Then
                taskPane1 = TaskPaneItem1.CreateTaskPaneInstance()
            End If

            taskPane2 = TryCast(TaskPaneItem2.TaskPaneInstance, ADXExcelTaskPane2)
            If taskPane2 Is Nothing Then
                taskPane2 = TaskPaneItem2.CreateTaskPaneInstance()
            End If

            taskPane3 = TryCast(TaskPaneItem3.TaskPaneInstance, ADXExcelTaskPane3)
            If taskPane3 Is Nothing Then
                taskPane3 = TaskPaneItem3.CreateTaskPaneInstance()
            End If
        End Sub

and then use the variable like so:
taskPane3.UserControl1.Method1()
TaskPaneItem1.Position = AddinExpress.XL.ADXExcelTaskPanePosition.Top


The above seems to work just fine at the runtime. So far so good.

The problem is the designer :) When I go to the task pane design view I get the "lovely" 'To prevent possible data loss before loading the designer, the following errors must be resolved:' window and the error message is:
Object reference not set to an instance of an object.

If I comment out the above code, the error goes away and the design view becomes available.

Any ideas on what I am doing wrong and/or how should I go about this please?

Thanks,
KL

Update: Interestingly enough, this doesn't happen to the user controls themselves, which contain the code and where one would expect the error to occur. Somehow these declarations affect the next level (Task Pane)
Posted 08 Jan, 2019 11:07:01 Top
Andrei Smolin


Add-in Express team


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

Happy New Year.

First off, you should understand that a UserControl put on a form is loaded when the form is opened at the design time. This is why you get that event triggered at the design time.

The actual cause of the issue is: ADXAddinModule.CurrentInstance is null at the design time. But read the below.

You shouldn't store references to ADXExcelTaskPane instances in this way. The point is (it mostly relates to the run time): the ADXExcelTaskPanesManager controls every aspect of creating and disposing such instances. In particular, it may dispose an pane instance at any moment and create (or not create) a new instance basing on conditions external to your code. If you store such a reference in this way, it may turn out to be broken in any moment. We suggest the following approach. Create a class containing the logic common to these three panes (and probably to the workbook/window): each pane registers with that class when being created and unregisters when being disposed; registering also means subscribing to the events that the common logic class provides. That is, when required, that class triggers an event and this causes all pane instances subscribed to that event to handle it.

The events to use to register/unregister the pane depend on your actual goals: 1) if you need to communicate with such a pane only when it it visible, you use the ADXExcelTaskPane.ADXBeforeTaskPaneShow and ADXExcelTaskPane.ADXAfterTaskPaneHide events, 2) if you need to communicate with such a pane even if it is only created (not embedded into the Excel window), you use System.Windows.Forms.Form.Load and System.Windows.Forms.Form.HandleDestroyed events.


Andrei Smolin
Add-in Express Team Leader
Posted 10 Jan, 2019 06:37:10 Top
Kirill Lapin


Guest


Hi Andrei,

Thank you very much for your time, this is helpful. I do get that the UC is loaded at the design time and kind of suspected it had to do with the way ADX task panes are instantiated, so thanks for confirming. While I broadly understand the approach you are suggesting, I am going to need some more help with it as figuring out the UI is becoming too much of a time consuming exercise and I seem to struggle getting the grips of the ADX task pane object model (despite reading and re-reading the manual several times). I didn't realize that the learning curve was going to be that steep for this functionality when I chose it, and now I am almost at a no-return point of the project :) (going back to WinForms UI would be a disaster). Do you have any sample code of such class maybe. I think I need a kick-starter in the form of a simple working example (the simpler the better). If you need more information about how it is expected to work, I am happy to have a 15-30 min Skype call and show you the prototype at work. Please advise.

Thanks,
KL
Posted 10 Jan, 2019 07:23:51 Top
Andrei Smolin


Add-in Express team


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

I'll consider creating the following add-in:
- it contains the BaseLogic class, a singleton
- *all* task pane instances register with it as described above
- when you activate a window, BaseLogic is called to produce an event that each task pane instance handle to show a label containing some info about the active window
- you can click a button on a pane and all panes will reflect some info about the pane which is the parent pane for that button

I'll look into creating such a sample tomorrow or on Monday.


Andrei Smolin
Add-in Express Team Leader
Posted 10 Jan, 2019 08:41:46 Top
Kirill Lapin


Guest


Andrei, thanks a lot. It sounds exactly like what I am after. I look forward to your sample.

Regards,
KL
Posted 10 Jan, 2019 09:03:39 Top
Andrei Smolin


Add-in Express team


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

Please see http://temp.add-in-express.com/support/MyAddin10-howto-multiple-taskpane-instances-interacting-vb.zip.

The example provided as-is. It works for me. Please let me know if it works for you.

In the event handlers of the pane instances that this example creates, you may want to filter out events related to a specific Excel window, say to the window in which the pane is shown (ADXExcelTaskPane.WindowObj).

The ImRegistered call reflects the fact that the panes (=instances of the panes) may be created out of sync with Excel events. Say, for a new Excel 2016-2019 window, the WindowActivated event occurs before the panes are created in that window; calling ImRegistered allows the pane to receive events providing the current data.


Andrei Smolin
Add-in Express Team Leader
Posted 14 Jan, 2019 09:05:54 Top
Kirill Lapin


Guest


Thanks a lot Andrei. Works like charm. I think I can make it work for my add-in :) The Singleton doesn't seem to be thread-safe. Is there any risk with the way the task panes are instantiated and should I use SyncLock statement maybe? I can't imagine a scenario where this lazy load could fail in my app, but then I am not sure I understand well the task panes mechanics either.

Thanks again,
KL
Posted 15 Jan, 2019 08:29:22 Top
Andrei Smolin


Add-in Express team


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

Kirill Lapin writes:
The Singleton doesn't seem to be thread-safe


It is thread-safe because *all* Office object model should be used on the main thread only.


Andrei Smolin
Add-in Express Team Leader
Posted 15 Jan, 2019 08:36:13 Top
Kirill Lapin


Guest


Perfect! Thanks
Posted 15 Jan, 2019 08:37:39 Top
Andrei Smolin


Add-in Express team


Posts: 18821
Joined: 2006-05-11
You are welcome!


Andrei Smolin
Add-in Express Team Leader
Posted 15 Jan, 2019 08:54:47 Top