问题描述
我正在尝试在 VS2019 扩展中获取活动的 C# 编辑器 IWpfTextView。我正在使用小型 MEF 服务将视图注入 VSAsyncPackage。但它不是很可靠 - 有时注入的视图是错误的(例如从另一个视图)或丢失。这是服务:
public interface IActiveViewAccessor
{
IWpfTextView? ActiveView { get; }
}
[Export(typeof(IWpfTextViewConnectionListener))]
[Export(typeof(IActiveViewAccessor))]
[ContentType("text")]
[TextViewRole(PredefinedTextViewRoles.Document)]
internal sealed class ActiveViewConnectionListener : IWpfTextViewConnectionListener,IActiveViewAccessor
{
public IWpfTextView? ActiveView { get; private set; }
public void SubjectBuffersConnected(IWpfTextView textView,ConnectionReason reason,Collection<ITextBuffer> subjectBuffers)
{
this.ActiveView = textView;
}
public void SubjectBuffersdisconnected(IWpfTextView textView,Collection<ITextBuffer> subjectBuffers)
{
this.ActiveView = null;
}
}
这个服务被注入到 VSPackage 中:
this.viewAccessor = this.exportProvider.GetExportedValue<IActiveViewAccessor>();
它被用作:
var view = this.viewAccessor?.ActiveView;
是否有更好更稳定的方式在异步 VSPackage 中获取 IWpfTextView?
到目前为止,这里有一些相关的问题,但并不完全符合我的预期:
- How to get IWpfTextView from command Visual Studio Extension 2017 (2017)
- How can I get IWpfTextView for EnvDte.ActiveDocument? (2013)
- Access current code pane in Visual Studio Extension (2012)
解决方法
经过一些调试和探索,我得出结论,我最初的方法非常幼稚。因为 IWpfTextViewConnectionListener
在简单情况下每个编辑器窗口仅触发一次,并且在已连接视图之间切换期间不会触发。
在尝试并潜入 VsVim > VsAdapter.cs 之后,我将 IActiveViewAccessor
更改为:
public interface IActiveViewAccessor
{
IWpfTextView? ActiveView { get; }
}
[Export(typeof(IActiveViewAccessor))]
internal sealed class ActiveViewAccessor : IActiveViewAccessor
{
private readonly SVsServiceProvider serviceProvider;
private readonly IVsEditorAdaptersFactoryService editorAdaptersFactoryService;
[ImportingConstructor]
public ActiveViewAccessor(
SVsServiceProvider vsServiceProvider,IVsEditorAdaptersFactoryService editorAdaptersFactoryService)
{
this.serviceProvider = vsServiceProvider;
this.editorAdaptersFactoryService = editorAdaptersFactoryService;
}
public IWpfTextView? ActiveView
{
get
{
IVsTextManager2 textManager =
serviceProvider.GetService<SVsTextManager,IVsTextManager2>();
if (textManager == null)
{
return null;
}
int hr = textManager.GetActiveView2(
fMustHaveFocus: 1,pBuffer: null,grfIncludeViewFrameType: (uint)_VIEWFRAMETYPE.vftCodeWindow,ppView: out IVsTextView vsTextView);
if (ErrorHandler.Failed(hr))
{
return null;
}
return editorAdaptersFactoryService.GetWpfTextView(vsTextView);
}
}
}