Visual Studio 2017扩展:工具栏隐藏后仍接收IVsSelectionEvents事件
解决Visual Studio扩展工具栏隐藏后仍接收IVsSelectionEvents的问题
我来帮你搞定这个头疼的问题——工具栏隐藏后还持续收到选择事件,核心就是没法及时感知工具栏的状态变化来动态调整订阅。这里给你两个靠谱的解决方案:
方案一:直接监听CommandBar的可见性变化
VS的工具栏本质是基于Office的CommandBar模型,你可以在创建工具栏的时候,给它加个VisibleChanged事件监听,就能精准捕获显示/隐藏的时机:
- 先拿到你的工具栏对应的
CommandBar实例(一般在Package的初始化代码里):
var commandService = GetService(typeof(IMenuCommandService)) as OleMenuCommandService; // 替换成你自己的工具栏命令ID和命令集GUID CommandBar toolbar = commandService.GetCommandBar(new Guid("你的命令集GUID"), 你的工具栏CmdId);
- 订阅它的
VisibleChanged事件:
toolbar.VisibleChanged += Toolbar_VisibleChanged;
- 在事件处理里动态管理选择事件的订阅:
private uint _selectionCookie; // 保存订阅的Cookie,用于取消订阅 private void Toolbar_VisibleChanged(object sender, EventArgs e) { var toolbar = sender as CommandBar; var monitorSelection = GetService(typeof(SVsShellMonitorSelection)) as IVsMonitorSelection; if (toolbar.Visible) { // 工具栏显示时,订阅选择事件 monitorSelection.AdviseSelectionEvents(你的SelectionEvents实现类实例, out _selectionCookie); } else { // 工具栏隐藏时,立刻取消订阅 monitorSelection.UnadviseSelectionEvents(_selectionCookie); } }
方案二:通过广播消息监听全局状态变化
如果拿不到CommandBar实例,还可以通过VS的广播消息机制来监听工具栏的状态变更:
- 先实现
IVsBroadcastMessageEvents接口来处理广播消息:
public class ToolbarVisibilityListener : IVsBroadcastMessageEvents { private readonly IVsMonitorSelection _monitorSelection; private readonly IVsSelectionEvents _selectionHandler; private uint _selectionCookie; public ToolbarVisibilityListener(IVsMonitorSelection monitorSelection, IVsSelectionEvents selectionHandler) { _monitorSelection = monitorSelection; _selectionHandler = selectionHandler; } public int OnBroadcastMessage(uint msg, IntPtr wParam, IntPtr lParam) { // 监听工具栏可见性变更的内部消息 if (msg == (uint)VSConstants.VSSPROPID_ToolbarVisible) { // 这里可以根据wParam判断是否是你的工具栏(wParam是工具栏的命令ID) if (wParam == (IntPtr)你的工具栏CmdId) { bool isVisible = Marshal.ReadInt32(lParam) != 0; if (isVisible) { _monitorSelection.AdviseSelectionEvents(_selectionHandler, out _selectionCookie); } else { _monitorSelection.UnadviseSelectionEvents(_selectionCookie); } } } return VSConstants.S_OK; } }
- 在Package里订阅这个广播事件:
private uint _broadcastCookie; protected override void Initialize() { base.Initialize(); var shell = GetService(typeof(SVsUIShell)) as IVsUIShell; var monitorSelection = GetService(typeof(SVsShellMonitorSelection)) as IVsMonitorSelection; shell.AdviseBroadcastMessageEvents(new ToolbarVisibilityListener(monitorSelection, 你的SelectionEvents实例), out _broadcastCookie); }
额外提醒
- 记得在Package销毁时(比如重写
Dispose方法),把所有订阅的Cookie都取消掉,避免内存泄漏。 - 方案一的CommandBar获取要确保工具栏已经被正确创建,建议在
Initialize方法的后半段执行。
内容的提问来源于stack exchange,提问作者subcoder
相关产品推荐
相关产品推荐

