• 实现Visual Studio 2010一个很简单的很酷的扩展


    基本介绍篇

        在实现这个扩展之前,让我们先弄清楚这个扩展实现什么功能。这个扩展实际上是在你的VS窗口的右上角创建了一个信息框代码。该信息框显示您的源代码的统计信息。这些信息包括:

        1、文件的代码行数
        2、文件的字符数
        3、命名空间的个数
        4、字段个数
        5、接口个数
        6、类的个数
        7、函数个数
        8、属性个数
        9、注释的数量
       10、统计文件的大小(Bytes, KB, MB等)。

        当您键入您的代码,你会看到信息窗口中的数据会即时更新。

        这个信息窗口,是利用可扩展面板。你可以把它看成两个部分,每一个部分都可以根据您的需要展开和折叠。这样,当你不需要它时,你将它可以折叠起来,需要的时候,将它展开。下面演示如何展开/折叠这个控件。

        这个控件有三个特殊的状态。第一个状态是一个很小的扩展按钮。如上图的第一部分。只需点击它,就会打开控件右侧的面板,这个面板显示文件的基本数据,如上图的第二部分。这个控件还有一个可扩展面板,如果点击扩展,就会看到下面的面板,其中显示其他的统计数据,如上图的第三部分。

    实现篇:

        需要软件:

        1、 Microsoft Visual Studio 2010
        2、 Visual Studio 2010 SDK

        你安装 Visual Studio SDK之后,你的Visual Studio 2010中会多出下面这些模板:

        这篇文章中,我们使用模板Editor ViewPort Adornment实现这个扩展,此模板将为你的代码编辑器的带来一个装饰品。 

        其实这个扩展包一个WPF用户控件,我把它放进VS的视窗中就成了信息框。它还含有两个类,一个类用来解析代码,获取代码的相关信息;另一个类用来处理自定义编辑器的事件和当页以及加载的时候将WPF控件添加到页面中。

    第一步:创建一个Viewport Adornment项目

        我们从Extensibility中选择Viewport Adornment模板创建一个项目。这将生成一个SourceManifest文件和两个类文件。一个是Adornment类本身,另外一个是AdornmentFactory类。

    第二步:添加一个WPF用户控件

        右键单击项目,选择添加一个新的WPF用户控件。为了简单起见,我使用了一个用户控件。这个用户控件实际上包含一个Expander控件,设置它的ExpandDirection = Left,它里面又包含了一些TextBlock控件和另外一个Expander ,设置里面的这个Expander的ExpandDirection = Down。看下面的代码(我删除不必要的元素,使其更简单):

     1   <Expander ExpandDirection="Left" Style="{DynamicResource ExpanderStyle1}" 
     2            x:Name="expMain" >
     3   <StackPanel>
     4                 <TextBlock x:Name="txtNoLines" 
     5                            Text="No of Lines : {0}" 
     6                            Margin="25 25 25 0" 
     7                            FontSize="12" 
     8                            FontFamily="Verdana" 
     9                            FontWeight="Bold" 
    10                            Foreground="Yellow"></TextBlock>
    11                 <TextBlock x:Name="txtNoCharacters" 
    12                            Text="No of Characters : {0}" 
    13                            Margin="25 5 25 15" 
    14                            FontSize="12" 
    15                            FontFamily="Verdana" 
    16                            FontWeight="Bold" 
    17                            Foreground="Yellow"></TextBlock>
    18                 <Expander x:Name="expCodeInfo" ExpandDirection="Down" 
    19                                      Header="Code Information">
    20                     <StackPanel>
    21                         <TextBlock x:Name="txtClassInfo" 
    22                                    Margin="25 25 25 0" 
    23                                    FontSize="12" 
    24                                    FontFamily="Verdana" 
    25                                    FontWeight="Bold" 
    26                                    Foreground="LightYellow"/>
    27                         <Line
    28                               Margin="0,4"
    29                               SnapsToDevicePixels="True"
    30                               Stroke="Gold"
    31                               Stretch="Fill"
    32                               X1="0" X2="1" 
    33                               />
    34                         <TextBlock x:Name="txtFileSize"
    35                                    Margin="25 5 25 15" 
    36                                    FontSize="12" 
    37                                    FontFamily="Verdana" 
    38                                    FontWeight="Bold" 
    39                                    Foreground="AliceBlue"/>
    40                     </StackPanel>
    41                 </Expander>
    42             </StackPanel>
    43          </Expander>

        你可以看到,代码很简单,两个Expanders,一个用来显示基本的统计信息和另外一个显示扩展的统计信息。我还使用StackPanel来固定TextBlocks布局。

        现在,如果你看一下后台代码,发现它也一样简单。其实我已经创建了一个CodeInfoTracker类,用它来为我们分析源代码文件。我只是为我们的用户控件添加了一个构造函数,使用户控件更具扩展性而已。

     1 private CodeInfoTracker _cinfo;
     2 private CodeInfoTracker.Calculators _calculator;
     3 public ucInfoBox(CodeInfoTracker cinfo)
     4             : this()
     5 {
     6         this._cinfo = cinfo;
     7 }
     8  public void UpdateInfo(CodeInfoTracker info)
     9  {
    10             _calculator = info.PerFormCalculate();
    11             this.txtNoLines.Text = string.Format("No of Lines : {0}"
    12                                     _calculator.no_of_lines);
    13             this.txtNoCharacters.Text = string.Format("No of Characters : {0}"
    14                                                        _calculator.no_of_characters);
    15             this.txtFileSize.Text = string.Format("Total File Size : {0}"
    16                                                        _calculator.totalfilesize);
    17 
    18             StringBuilder builder = new StringBuilder();
    19             if (this._calculator.interfaces != 0)
    20                 builder.AppendFormat("Interfaces : {0}\n\r"
    21                                           this._calculator.interfaces);
    22             if (this._calculator.namespaces != 0)
    23                 builder.AppendFormat("NameSpaces : {0}\n\r"
    24                                             this._calculator.namespaces);
    25             if (this._calculator.classes != 0)
    26                 builder.AppendFormat("Classes : {0}\n\r"
    27                                             this._calculator.classes);
    28             if (this._calculator.methods != 0)
    29                 builder.AppendFormat("Methods : {0}\n\r"this._calculator.methods);
    30             if (this._calculator.properties != 0)
    31                 builder.AppendFormat("Properties : {0}\n\r"
    32                                                this._calculator.properties);
    33             if (this._calculator.fields != 0)
    34                 builder.AppendFormat("Fields : {0}\n\r"this._calculator.fields);
    35             if (this._calculator.comments != 0)
    36                 builder.AppendFormat("Comments : {0}\n\r"this._calculator.comments);
    37 
    38             if (builder.Length > 0)
    39             {
    40                 this.txtClassInfo.Visibility = System.Windows.Visibility.Visible;
    41                 this.txtClassInfo.Text = builder.ToString();
    42             }
    43             else
    44             {
    45                 this.txtClassInfo.Text = "";
    46                 this.txtClassInfo.Visibility = System.Windows.Visibility.Hidden;
    47        }
    48   }
       使用了一个结构体Calculators ,这个结构体放置在我们的自定义类中,它有几个int属性用来保存分析源文件获取的所有信息。 info.PerFormCalculate(); 给出分析的结果。这里使用的所有获取的信息来更新了UIElements。

    第三步:创建获取源文件信息的类

        虽然代码存在一些复杂性,但是这个类其实很简单。我很感谢CS Parser [^],它帮助我自动地解析源代码。   

        这个类需要一个IWpfTextView对象,它代表着Visual Studio文本编辑器。实际上WpfTextView实现了IWpfTextView。在执行期间这个类接受这个对象。我可以从WPFTextView.TextSnapshot.GetText()获得到了源代码。
        在我调用的这个分析的时候,我只需要检测的代码是什么语言写的。开始我想自己来实现,但是感谢上帝,我在WPFTextView中发现已经存在这个对象了。

     1 public enum Language
     2 {
     3         CSharp, VisualBasic, Indeterminate
     4 }
     5 internal Language DetectLanguage
     6 {
     7             get
     8             {
     9                 string langtype = 
    10         this._view.FormattedLineSource.TextAndAdornmentSequencer.
    11         SourceBuffer.ContentType.DisplayName;
    12                 if(langtype.Equals("CSHARP"
    13             StringComparison.InvariantCultureIgnoreCase))
    14                     return Language.CSharp;
    15                 else if(langtype.Equals("BASIC"
    16                               StringComparison.InvariantCultureIgnoreCase))
    17                     return Language.VisualBasic;
    18                 else
    19                     return Language.Indeterminate;
    20             }
    21 }

         DetectLanguage妥善地利用WPFTextView对象的FormattedLineSource.TextAndAdornmentSequencer.
    SourceBuffer.ContentType.DisplayName,这个属性
    告诉我是使用了哪种语言。之后我创建了一个新的方法PerFormCalculate,用它来解析源代码,它返回一个Calculation结构对象。

    第四步:创建 Adornment Factory 类

        回到这个扩展,我创建一个Adornment(InfoBoxAdornmentFactory)的Factory类。这个类继承IWpfTextViewCreationListener,用来监听WPF的编辑和创建事件。

     1 [Export(typeof(IWpfTextViewCreationListener))]
     2 [ContentType("text")]
     3 [TextViewRole(PredefinedTextViewRoles.Document)] 
     4 internal sealed class InfoBoxAdornmentFactory : IWpfTextViewCreationListener
     5 {
     6         [Export(typeof(AdornmentLayerDefinition))]
     7         [Name("AlwaysVisibleInfoBox")]
     8         [Order(After = PredefinedAdornmentLayers.Selection)]
     9         [TextViewRole(PredefinedTextViewRoles.Interactive)]
    10         public AdornmentLayerDefinition editorAdornmentLayer = null;
    11         public void TextViewCreated(IWpfTextView textView)
    12         {
    13             new AlwaysVisibleInfoBox(textView);
    14         }
    15  } 

        这里,你可以看到我在这个类上使用了很多Attributes,像ContentType,它定义了我们只处理文本格式的编辑器;还有TextViewRole,它定义了将被这个类处理的textview的类型

        在这个类中,我创建了一个AdornmentLayerDefination对象。可能你想知道我们没有使用它,无什么还需要定义它呢,它只是用来配置属性的。Order属性指定,当,InfoBox在层被选之后监听,Name是编辑扩展的名字。

    第五步:创建Adornment 类

        Adornment类实际创建了一个WPF用户控件对象,并设置它的视图画布。在内部构造函数中,我处理IWpfTextView.LayoutChanged事件,当代码修改或者布局改变的时候,就触发这个事件。因此,通过这一事件,当我们编辑的文档时,我们可以很容易地得到回调。当浏览器编辑器的大小改变时,我还通过处理WPFTextView.ViewportHeightChanged,WPFTextView.ViewportWidthChanged得到回调,使我们可以重新定位相应的UserControl。

     1 public AlwaysVisibleInfoBox(IWpfTextView view)
     2 {
     3           _view.LayoutChanged += this.OnLayoutChanged;
     4           this.GetLayer();
     5 }
     6 private void GetLayer()
     7  {
     8             _adornmentLayer = this._view.GetAdornmentLayer("AlwaysVisibleInfoBox");
     9             _view.ViewportHeightChanged += delegate { this.onSizeChange(); };
    10             _view.ViewportWidthChanged += delegate { this.onSizeChange(); };
    11 }
    12  private void OnLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
    13 {
    14             this._info = new CodeInfoTracker(_view);
    15             this.infobox.UpdateInfo(this._info);
    16  }
    17  public void onSizeChange()
    18  {
    19      
    20             _adornmentLayer.RemoveAllAdornments();
    21             Canvas.SetLeft(infobox, _view.ViewportRight - 255);
    22             Canvas.SetTop(infobox, _view.ViewportTop + 10);
    23             
    24           _adornmentLayer.AddAdornment(AdornmentPositioningBehavior.ViewportRelative, 
    25           nullnull
    26           infobox, null);
    27 }

       因此,构造函数只是调用GetLayer来获取的Layer对象,发生在ViewportHeightChanged和ViewportWidthChanged ViewPortSizeChage事件。当一个布局改变时,我就能更新这个用户的控件。

        至此,我们成功地建立我们的扩展。你可以使用F5运行它,它会打开一个Visual Studio的Experimental实例。

    安装和卸载这个扩展:
        安装和卸载这个扩展是非常容易的。当您编译项目后,它会产生一个VSIX文件。您可以只需双击这个文件,它会自动安装到Visual Studio。
       

        要卸载的文件,您打开Visual Studio,转到 Tools - > Extension Manager,然后选择卸载该扩展。

    发布您的扩展:

        发布你的扩展到Visual Studio库的方式也非常的酷。只要你需要上传VSIX文件到http://www.visualstudiogallery.com/。我已经上载我的这个扩展。

    总结:这篇文章,从头到尾一步一步教你实现一个很简单很酷的VS2010的扩展

    原文:Code InfoBox Visual Studio Extension (VSX) 2010

    参考:

    Building and publishing an extension for Visual Studio 2010 [^]

    Official Extension Site [^]

    代码:Zip

    作者:朱祁林
    出处:http://zhuqil.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    python zip文件读取转存excel
    R语言--初步学习
    Python 设置进度条
    python简单爬虫(股票信息)
    如何将windbg设置为默认截崩溃软件
    python简单爬虫(中国大学排名)
    win10远程桌面连接提示身份验证错误怎么办?
    redis 学习
    keepalived入门
    sqlite内存数据库和文件数据库的同步[转]
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/1803227.html
Copyright © 2020-2023  润新知