惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

腾讯CDC
IT之家
IT之家
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
Apple Machine Learning Research
Apple Machine Learning Research
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
人人都是产品经理
人人都是产品经理
The Cloudflare Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 【当耐特】
V
V2EX
Last Week in AI
Last Week in AI
H
Help Net Security
The GitHub Blog
The GitHub Blog
S
SegmentFault 最新的问题
F
Fortinet All Blogs
I
InfoQ
宝玉的分享
宝玉的分享
A
About on SuperTechFans
MongoDB | Blog
MongoDB | Blog
Microsoft Azure Blog
Microsoft Azure Blog
Blog — PlanetScale
Blog — PlanetScale
B
Blog

博客园 - winkingzhang

Build 2016概览 WinRT开发系列之编程语言:功能和效率 WinRT开发系列之基础概念:WinRT不是…… VS2010 Extension实践(3)——实现自定义配置 [VS2010 Extension]PowerExtension.GoToDefinition VS2010 Extension实践(2) 如何通过反射调用带有ref或者out的参数的方法[迁移] Win7硬盘安装和移动硬盘访问出错的修复办法[迁移] zt. Windows Mobile开发文章收藏 [WPF]在Style中设置ToolTip的问题分析 [WPF]RadioButton在Group的Header区部分不响应鼠标选择的bug分析 WPF模式思考 (zt) How to Get IIS Web Sites Information Programmatically Visual Studio 调试器 Application Request Routing and the IIS 7.0 Web Management Service Reference Resources for MOSS and WSS FileSystemWatcher事件多次触发的解决方法 ASP.NET Application Life Cycle CAS and Native Code
VS2010 Extension实践
winkingzhang · 2010-01-26 · via 博客园 - winkingzhang

最近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对象:

[TextViewRole("DOCUMENT")]
[Export(
typeof(IWpfTextViewCreationListener))]
[ContentType(
"text")]
internal sealed class PETextViewCreationListener : IWpfTextViewCreationListener
{
void IWpfTextViewCreationListener.TextViewCreated(IWpfTextView textView)
{
//...
}
}

这样VS就会在合适的时候调用IWpfTextViewCreationListener.TextViewCreated方法来通知文字编辑的状态改变。

为了实现浮动一个自己的工具栏,这里还需要导出一个AdornmentLayerDefinition,并通过Order Attribute来定制这个Adornment层的显示位置和次序:

[Name("QuickToolbarAdornmentLayer")]
[Order(After
= "Text")]
[Export(
typeof(AdornmentLayerDefinition))]
public AdornmentLayerDefinition QuickToolbarLayerDefinition
{
get;
set;
}

这里的Name Attribute很重要,以后的代码中要获取我们的AdornmentLayer就得靠它了:

this._adornmentLayer = this._textView.GetAdornmentLayer("QuickToolbarAdornmentLayer");

扯得远了,回到IWpfTextViewCreationListener.TextViewCreated,通过这里,可以得到一个IWpfTextView,

这是所有操作的目标和展现,另外,还需要挂他的Closed、LayoutChanged、MouseHovered、SelectionChanged等事件,以响应用户行为。

由于我们要通过工具栏操作代码,所以需要通过MEF导入IEditorOperationsFactoryService:

[Import]
internal IEditorOperationsFactoryService EditorOperationsFactoryService
{
get;
set;
}

这样就可以在IWpfTextViewCreationListener.TextViewCreated中通过IEditorOperationsFactoryService.GetEditorOperations(ITextView)来获得IEditorOperations,有了它,就可以方便快捷的编辑代码了。

接下来要实现工具栏的界面,这个就不多说了,建一个UserControl,里面放上ToolBar就搞定了。那么何时何地显示这个ToolBar呢?这就要依赖IWpfTextView的SelectionChanged事件了,上面提到会挂这个事件就是为这里用的。

代码

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 }
90  

然后要注意的是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)