VS2010 Extension实践(1)
最近VS2010 Extension在Visual Studio Blog(http://blogs.msdn.com/visualstudio/)上提得很频繁,于是也想翻来文档研究研究,结果居然找了半天,居然没有一丁点完整介绍这一块的,于是,只好自己找着VS IDE上的模板提供的内容和Visual Studio Blog上的讲解,一边Reflector参演,一边涂鸦一些代码,准备实弹演练一下,但是觉得这个模板建出来的Extension也太简单了,刚好看到AxTool(http://www.axtools.com/products-vs2010-extensions.php)有一个代码编辑器扩展,也是VS Extension的,于是就照着这个,自己一步一步做一下。
首先,要想建立VS Extension工程,你需要安装VS2010 SDK,目前是Beta2版本,你可以到这里可以下载:http://go.microsoft.com/fwlink/?LinkID=165597),这里我是通过Editor Text Adornment模板创建的工程,嗯,我就不详细写如何通过模板创建自己Extension工程了,如果你不熟悉这里,可以参考Quan To的这篇帖子——Building and publishing an extension for Visual Studio 2010。
建好工程以后,会自动生成TextViewCreationListener,这里实现了IWpfTextViewCreationListener接口,并通过MEF导出IWpfTextViewCreationListener对象:
1 2 3 4 5 6 7 8 9 10 | [TextViewRole( "DOCUMENT" )] [Export( typeof (IWpfTextViewCreationListener))] [ContentType( "text" )] internal sealed class PETextViewCreationListener : IWpfTextViewCreationListener { void IWpfTextViewCreationListener.TextViewCreated(IWpfTextView textView) { //... } } |
这样VS就会在合适的时候调用IWpfTextViewCreationListener.TextViewCreated方法来通知文字编辑的状态改变。
为了实现浮动一个自己的工具栏,这里还需要导出一个AdornmentLayerDefinition,并通过Order Attribute来定制这个Adornment层的显示位置和次序:
1 2 3 4 5 6 7 8 | [Name( "QuickToolbarAdornmentLayer" )] [Order(After = "Text" )] [Export( typeof (AdornmentLayerDefinition))] public AdornmentLayerDefinition QuickToolbarLayerDefinition { get ; set ; } |
这里的Name Attribute很重要,以后的代码中要获取我们的AdornmentLayer就得靠它了:
1 | this ._adornmentLayer = this ._textView.GetAdornmentLayer( "QuickToolbarAdornmentLayer" ); |
扯得远了,回到IWpfTextViewCreationListener.TextViewCreated,通过这里,可以得到一个IWpfTextView,
这是所有操作的目标和展现,另外,还需要挂他的Closed、LayoutChanged、MouseHovered、SelectionChanged等事件,以响应用户行为。
由于我们要通过工具栏操作代码,所以需要通过MEF导入IEditorOperationsFactoryService:
1 2 3 4 5 6 | [Import] internal IEditorOperationsFactoryService EditorOperationsFactoryService { get ; set ; } |
这样就可以在IWpfTextViewCreationListener.TextViewCreated中通过IEditorOperationsFactoryService.GetEditorOperations(ITextView)来获得IEditorOperations,有了它,就可以方便快捷的编辑代码了。
接下来要实现工具栏的界面,这个就不多说了,建一个UserControl,里面放上ToolBar就搞定了。那么何时何地显示这个ToolBar呢?这就要依赖IWpfTextView的SelectionChanged事件了,上面提到会挂这个事件就是为这里用的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | 1 private void MayBeAdornmentShowCondition() 2 { 3 if (! this ._textView.Selection.IsEmpty) 4 { 5 SnapshotPoint startPos = this ._textView.Selection.Start.Position; 6 SnapshotPoint endPos = this ._textView.Selection.End.Position; 7 IWpfTextViewLine textViewLineContainingBufferPosition = this ._textView.GetTextViewLineContainingBufferPosition(startPos); 8 TextBounds characterBounds = textViewLineContainingBufferPosition.GetCharacterBounds(startPos); 9 TextBounds bounds2 = this ._textView.GetTextViewLineContainingBufferPosition(endPos).GetCharacterBounds(endPos); 10 if ( this ._fromMouseHover) 11 { 12 this ._mustHaveAdornmentDisplayed = true ; 13 } 14 else 15 { 16 PELeftButtonMouseProcessor property = null ; 17 try 18 { 19 property = this ._textView.Properties.GetProperty<PELeftButtonMouseProcessor>( typeof (PELeftButtonMouseProcessor)); 20 } 21 catch 22 { 23 } 24 this ._mustHaveAdornmentDisplayed = (property != null ) 25 && (property.IsLeftButtonDown 26 || ((DateTime.Now - property.LastLeftButtonDownTime).TotalMilliseconds < 400.0)); 27 } 28 if ( this ._mustHaveAdornmentDisplayed) 29 { 30 TextBounds selectionBounds = ! this ._textView.Selection.IsReversed ? bounds2 : characterBounds; 31 int offset = 7; 32 double top = selectionBounds.Top + (! this ._textView.Selection.IsReversed ? (offset + textViewLineContainingBufferPosition.Height) : (-offset - this ._adornmentUI.ActualHeight)); 33 if (top < 0.0) 34 { 35 top = 0.0; 36 } 37 double left = characterBounds.Left + ((bounds2.Left - characterBounds.Left) / 2.0); 38 if ((left + this ._adornmentUI.ActualWidth) > this ._textView.ViewportWidth) 39 { 40 left = this ._textView.ViewportWidth - this ._adornmentUI.ActualWidth; 41 } 42 Canvas.SetTop( this ._adornmentUI, top); 43 Canvas.SetLeft( this ._adornmentUI, left); 44 long chars = 0L; 45 try 46 { 47 chars = this ._textView.Selection.SelectedSpans[0].Span.Length; 48 } 49 catch 50 { 51 } 52 this ._adornmentUI.SetStatus(chars); 53 this .RenderSelectionPopup(); 54 } 55 } 56 else 57 { 58 this ._mustHaveAdornmentDisplayed = false ; 59 this ._adornmentLayer.RemoveAdornmentsByTag( this ._adornmentTag); 60 } 61 } 62 63 private void RenderSelectionPopup() 64 { 65 if ( this ._mustHaveAdornmentDisplayed) 66 { 67 IAdornmentLayerElement element = null ; 68 try 69 { 70 element = this ._adornmentLayer.Elements.First<IAdornmentLayerElement>( 71 (IAdornmentLayerElement ile) => ile.Tag.ToString() == this ._adornmentTag); 72 } 73 catch (InvalidOperationException) 74 { 75 } 76 if (element == null ) 77 { 78 this ._adornmentLayer.AddAdornment( this ._textView.Selection.SelectedSpans[0], this ._adornmentTag, this ._adornmentUI); 79 } 80 this ._timer.Stop(); 81 this ._timer.Start(); 82 } 83 } 84 85 private void selection_SelectionChanged( object sender, EventArgs e) 86 { 87 this ._fromMouseHover = false ; 88 this .MayBeAdornmentShowCondition(); 89 } |
然后要注意的是IWpfTextView的Closed事件处理要记得取消所有挂这个事件等等收尾工作。
接下来编译工程,打包VSIX就完成了,目前实现的主要Feature:
1、当在代码编辑器中选择一段文字,并将鼠标移到文字区域时,QuickToolbar会以半透明的方式“浮”文字的旁边。
2、当鼠标移到QuickToolbar区域,QuickToolbar会变成不透明,其上的按钮会响应鼠标动作。
3、目前支持的操作有:
- 剪切(Cut)
- 复制(Copy)
- 粘贴(Paste)
- 删除(Delete)
- 减小缩进(Decrease Indent)
- 增加缩进(Increase Indent)
- 注释代码(Comment)
- 取消注释(Uncomment)
- 等等
VSIX和源代码下载以及安装方法在GCDN论坛: [VS2010扩展]浮动工具栏(http://gcdn.grapecity.com/showtopic-345.html)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了