Using Microsoft.Vbe.Interop
in C#, I can access CommandBarEvents
and ReferencesEvents
via VBE.Events
.
However the ever-so helpful MSDN documentation seems to indicate that there's a VBProjectsEvents
that I could use to notify my add-in when a project is added or removed to/from the VBE... which is exactly what I'm trying to achieve here.
I can see that _VBProjectsEvents
interface in the object browser, but no implementation for it (as opposed to the _CommandBarControlsEvents
interface, which is implemented by the CommandBarEventsClass
), using ReSharper's go to implementation feature.
Is there an implementation of the _VBProjectsEvents
interface anywhere? If not, then how does one go about being notified of a VBProject being removed from the IDE?
You need to create a sink for these events.
Implement the _dispVBProjectsEvents
dispatch interface - here's an implementation that responds to these events by invoking regular managed .net events, effectively "wrapping" the VBProjects
events:
public class VBProjectsEventsSink : _dispVBProjectsEvents
{
public event EventHandler<DispatcherEventArgs<VBProject>> ProjectAdded;
public void ItemAdded(VBProject VBProject)
{
if (VBProject.Protection == vbext_ProjectProtection.vbext_pp_none)
{
OnDispatch(ProjectAdded, VBProject);
}
}
public event EventHandler<DispatcherEventArgs<VBProject>> ProjectRemoved;
public void ItemRemoved(VBProject VBProject)
{
if (VBProject.Protection == vbext_ProjectProtection.vbext_pp_none)
{
OnDispatch(ProjectRemoved, VBProject);
}
}
public event EventHandler<DispatcherRenamedEventArgs<VBProject>> ProjectRenamed;
public void ItemRenamed(VBProject VBProject, string OldName)
{
var handler = ProjectRenamed;
if (handler != null && VBProject.Protection == vbext_ProjectProtection.vbext_pp_none)
{
handler.Invoke(this, new DispatcherRenamedEventArgs<VBProject>(VBProject, OldName));
}
}
public event EventHandler<DispatcherEventArgs<VBProject>> ProjectActivated;
public void ItemActivated(VBProject VBProject)
{
if (VBProject.Protection == vbext_ProjectProtection.vbext_pp_none)
{
OnDispatch(ProjectActivated, VBProject);
}
}
private void OnDispatch(EventHandler<DispatcherEventArgs<VBProject>> dispatched, VBProject project)
{
var handler = dispatched;
if (handler != null)
{
handler.Invoke(this, new DispatcherEventArgs<VBProject>(project));
}
}
}
The DispatcherEventArgs
class is just a convenient way to expose the VBProject
item involved with the event, and it can be reused for other sinks:
public class DispatcherEventArgs<T> : EventArgs
where T : class
{
private readonly T _item;
public DispatcherEventArgs(T item)
{
_item = item;
}
public T Item { get { return _item; } }
}
The client code needs to register the sink - and for that you need to keep an IConnectionPoint
field and its int
cookie:
private readonly IConnectionPoint _projectsEventsConnectionPoint;
private readonly int _projectsEventsCookie;
The VBProjects
collection implements the IConnectionPointContainer
interface, which you need to use to find the connection point:
var sink = new VBProjectsEventsSink();
var connectionPointContainer = (IConnectionPointContainer)_vbe.VBProjects;
var interfaceId = typeof (_dispVBProjectsEvents).GUID;
connectionPointContainer.FindConnectionPoint(ref interfaceId, out _projectsEventsConnectionPoint);
Once you have the IConnectionPoint
, use the Advise
method to "connect" your sink and retrieve a cookie:
_projectsEventsConnectionPoint.Advise(sink, out _projectsEventsCookie);
And then you can handle the sink events as you would any "normal" .net events:
sink.ProjectAdded += sink_ProjectAdded;
sink.ProjectRemoved += sink_ProjectRemoved;
sink.ProjectActivated += sink_ProjectActivated;
sink.ProjectRenamed += sink_ProjectRenamed;
When you want to disconnect your sink, you pass the cookie to the Unadvise
method of the IConnectionPoint
instance:
_projectsEventsConnectionPoint.Unadvise(_projectsEventsCookie);
"Simple as that!"