I had this interesting problem the other day when creating an add-in for Office (Outlook specifically), but I believe it would apply to any Office add-in. The problem goes as follows:
I would create a CommandBar and embed controls on it. I would then wire up an event procedure for each control to fire accordingly. The events would run properly once. After that they wouldn't run. Upon loading the first time, the add-in would create the CommandBar, and that's the only time it would work. If I closed Outlook and reloaded it, the events would not get raised. This was a tad frustrating.
So how did I solve it? Well, to solve it, I had to dig a bit lower than the actual appearances and try to figure out what is really going on. First of all, this is managed code (that is, the add-in is written in C#). On the other hand, add-ins are COM components. So there's this inherent COM-Interop layer between my add-in and the Office environment. When an event gets wired up, you're really establishing a delegate instance (which is a managed component). So this is what happened:
- call procedure to create CommandBar and constituent controls
- COM component CommandBar created via RCW (Runtime Callable Wrapper)
- COM components for each CommandBarControl created and each one added to the CommandBar via RCW (Runtime Callable Wrapper)
- .NET managed delegate wired up for each event
- procedure ends, local variables for each COM component goes out of scope
- Garbage collector (GC) determines that RCWs are no longer used so .NET references to COM components deleted (along with delegates)
So the solution was, effectively, to create a container of some sort simply to persist the references to the COM components beyond the creation procedure for the duration the add-in is loaded. I opted for an ArrayList for ease and simplicity.
private outlook.Application _app;
private ArrayList _controls = new ArrayList();
private void createControls() {
CommandBar bar = _app.ActiveExplorer().CommandBars.Add(“Test CommandBar“, MsoBarPosition.msoBarTop, Type.Missing, true);
CommandBarButton btn = (CommandBarButton)bar.Controls.Add(MsoControlType.msoControlButton, Type.Missing, “Test“, Type.Missing, true);
btn.Tag = “TestButton“;
btn.Caption = “Click Me“;
btn.Click += new _CommandBarButtonEvents_ClickEventHandler(btn_Click);
btn.Visible = true;
// add the control to the collection so its reference persists
_controls.Add(btn);
bar.Visible = true;
}
private void btn_Click(CommandBarButton Ctrl, ref bool CancelDefault) {
MessageBox.Show(“You clicked the button!“);
}
All in all, it's a pretty simple solution, but without knowing what's going on (or just a lucky guess), it's not the most straightforward problem to solve. Hope this comes back to help someone who may be experiencing a similar problem.