Moving Code Blocks Among Code Regions using VS 2010 Extensions

(翻译)使用VS 2010 扩展性将代码块移至Region区域中

[译注:以上两个链接需登录CodeProject]

背景

为了通过将代码块放置在有逻辑的Region区域中,规范已经写好的代码,依照Visual Studio 目前现有的机制,你需要完成以下工作:

1、 生成 Regions

2、 对于每一个代码块,都要进行以下操作

  A、 选中代码块

  B、 执行剪切操作

  C、 导航到需要的Region,或者通过Region名查找它

  D、粘贴代码块到这个Region中

而自从我实现了 MoveToRegionVXS 这一扩展包之后,只需采用以下步骤:

1、 生成Regions

2、 右击代码编辑器选择 Move to Region 命令,将会弹出一个工具窗,展示一个包含所哟Regions的列表

3、 对于每一个代码块,执行以下步骤:

  A、 选中代码块

  B、 双击工具窗中的目标Region

项目前准备

扩展Visual Studio 既有趣又有挑战性,第一步是要选择正确的项目模板,为了开发这个工具,我决定使用Visual Studio Package机制,使用C#语言开发。接下了,我将一步步的解释如何开发这个工具。

1、 打开Visual Studio,选择新建项目…

2、 选择C#作为开发语言

3、 在“其他项目类型”中选择“扩展性”

4、 选择“Visual Studio Package”

5、 输入Package所在项目名称后点确定,前两个向导界面都点“Next”

6、 在company文本框中,我建议你输入你的名字,这个将作为你的Package的命名空间,并且你需要给你的Package起一个不易混淆的名字,在这里我用MoveToRegionVSX命名

7、 这一步将相当重要,在下图所示的界面中,勾选Menu Command 和 Tool Window。

Menu Command被用来在上下文菜单(即右键菜单)中调用菜单命令,Tool Window能够展示一个包含所有Region 名称列表的面板。

1、 输入命令的名称和命令的ID为“Move To Region”

2、 输入窗口的名称和窗口的ID为“Select Region”

3、 向导的最后一步中,不要勾选“test projects”

信不信由你,你已经完成了Visual Studio 2010的扩展,按下F5将会打开Visual Studio的新实例,点击 工具->Move To Region 将会打开一个信息框(Message Box),这个就是你所做的Visual Studio 扩展。

现在,我们已经准备好实现它的功能了,但是,首先,请让我将这个大任务分解:

1、 将Move To Region命令由工具菜单(Tools Meun)移动到上下文菜单(Context Meun)中去

2、 建立工具窗口

3、 添加必要的程序集和命名空间

4、 加载 Regions

5、 移动选中的代码到选中的Region中

6、 处理菜单项点击事件

将命令(按钮)从工具菜单移到上下文菜单

正因为规范代码的过程和代码编辑器息息相关,我认为放置命令按钮较为理想的地方是上下文菜单而不是工具菜单。但是正如你所看到的,默认的命令放置处是工具菜单,在解决方案资源管理器中,打开文件MoveToRegionVSX.vsct ,找到以下代码块:

  1. <!-- In this section you can define new menu groups. A menu group is a
  2. container for other menus or buttons (commands); from a visual point of view
  3. you can see the group as the part of a menu contained between two lines. The
  4. parent of a group must be a menu. -->
  5. <Groups>
  6. <Group guid="guidMoveToRegionVSXCmdSet" id="MyMenuGroup" priority="0x0600">
  7. <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
  8. </Group>
  9. </Groups>

从这个位置可以看出,我们的命令的父菜单是通过Parent id确定的。

IDM_VS_MEUN_TOOLS指工具菜单

IDM_VS_CTXT_CODEWIN指上下文菜单

因此只要简单地用IDM_VS_CTXT_CODEWIN取代IDM_VS_MEUN_TOOLS就可以了,事实证明也确实如此。

创建工具窗

在解决方案资源管理器(Solution explorer)中打开文件MyControl.xaml,你将会看到下图:

删除按钮(button)和标签(label),添加一个列表容器控件(list box),命名为lstbxRegions,这个控件将会容纳Regions列表。(注意这是一个WPF控件,因此,这个控件没有ID,只有name属性)。

调整列表容器控件的大小,使其和用户控件一样大,即宽=200,高=300(width=200、height=300)

现在当我们点击“Move to Region”命令,让我们展示一下工具窗(Tool Window)。

为了实现这个目的,我们需要完成以下步骤:

1、 从解决方资源管理器中打开文件"MoveToRegionVSXPackage.cs"

2、 找到MenuItemCallback 事件句柄(event handler)[译注:事件处理程序,下面有函数代码]正如他名字所陈述的,当你点击”Move to Region”命令时正是这个事件句柄被调用

3、 将函数主体用简单地对于另一个函数ShowToolWindow 的调用替代

  1. private void MenuItemCallback(object sender, EventArgs e)
  2. {
  3. //Show the tool window
  4. ShowToolWindow(sender, e);
  5. }

运行它,你就会发现工具窗出现了。

注意第一次运行的时候,你可能会发现工具窗比列表容器控件稍大,请你调整它的大小,只需调整这一次,它将会保存它的大小,下一次运行时就不会出现这一问题。

添加必要的程序集和命名空间

添加对下列命名空间的引用:

Microsoft.VisualStudio.CoreUtility

Microsoft.VisualStudio.Editor

Microsoft.VisualStudio.Text.Data

Microsoft.VisualStudio.Text.Logic

Microsoft.VisualStudio.Text.UI

Microsoft.VisualStudio.Text.UI.Wpf

在文件"MoveToRegionVSXPackage.cs"中,使用下列using语句代替已存在的using语句:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.Design;
  4. using System.Diagnostics;
  5. using System.Globalization;
  6. using System.Runtime.InteropServices;
  7. using Microsoft.VisualStudio.Editor;
  8. using Microsoft.VisualStudio.Shell;
  9. using Microsoft.VisualStudio.Shell.Interop;
  10. using Microsoft.VisualStudio.Text;
  11. using Microsoft.VisualStudio.Text.Editor;
  12. using Microsoft.VisualStudio.TextManager.Interop;

加载Regions

我们的想法很简单,首先需要获取目前活动的代码视口(code viewer),然后遍历该视口内所有的代码,查找所有关键字#region,对于每一个region,读取它的name[译注:关键字后面的标示],添加name到一个string list中,然后用string list填充list box。

因此,首先,我们将在MoveToRegionVSXPackage类中申明以下成员:

  1. public sealed class MoveToRegionVSXPackage : Package
  2. {
  3. //<gouda>
  4. //The current active editor's view info
  5. private IVsTextView currentTextView;
  6. private IVsUserData userData;
  7. private IWpfTextViewHost viewHost;
  8. private string allText;
  9. private const string keyword = "#region ";
  10. //</gouda>

现在,我们就来实现方法GetRegions():

  1. /// <summary>
  2. /// Parse(解析) all the text in the active editor's view and get all regions
  3. /// </summary>
  4. /// <returns> list of strings containing the names of the existing regions </returns>
  5. internal List<string> GetRegions()
  6. {
  7. List<string> regionsList = new List<string>();
  8. userData = currentTextView as IVsUserData;
  9. if (userData == null)// no text view
  10. {
  11. Console.WriteLine("No text view is currently open");
  12. return regionsList;
  13. }
  14. // In the next 4 statements, I am trying to get access to the editor's view
  15. object holder;
  16. Guid guidViewHost = DefGuidList.guidIWpfTextViewHost;
  17. userData.GetData(ref guidViewHost, out holder);
  18. viewHost = (IWpfTextViewHost)holder;
  19. //Now, I will load all the text of the editor to detect the key word "#region"
  20. allText = viewHost.TextView.TextSnapshot.GetText();
  21. string[] regionDelimitedCode = System.Text.RegularExpressions.Regex.Split(allText,
  22. "\\s#region\\s", //'\s' means any white space character e.g. \t, space, \n, \r, etc
  23. System.Text.RegularExpressions.RegexOptions.IgnoreCase);
  24. for (int index = /*skip first block*/; index < regionDelimitedCode.Length; index++)
  25. {
  26. regionsList.Add(regionDelimitedCode[index].Split('\r')[]);
  27. }
  28. return regionsList;
  29. }

现在假设我们已经取得了regions列表,所以我们能组装list box了,为了这个目的,我在MyControl类中添加了一个名叫PopulateList 的简单方法。

  1. /// <summary>
  2. /// Populates the list box with the list of regions found in the current active view
  3. /// </summary>
  4. /// <param name="regionsList"> list of strings holding the names of regions </param>
  5. /// <param name="myPkg"> reference to my package (MoveToRegion Package)</param>
  6. public void PopulateList(List<string> regionsList, MoveToRegionVSXPackage myPkg)
  7. {
  8. //Here is the best place to initialise the packageRef
  9. //I need that reference to enable calling the method MoveToRegion later on double click
  10. //Unless it is logically to do this initialization in the constructor,
  11. //this cannot be done
  12. //because we do not create instance of that class directly
  13. if(packageRef == null)
  14. packageRef = myPkg;
  15. lstbxRegions.Items.Clear();
  16. foreach (string s in regionsList)
  17. lstbxRegions.Items.Add(s);
  18. }

可以看到,我们使用MoveToReionVSXPackage类中的方法GetRegions能重新得到所有的regions,然后我们使用MyControl类中的PopulateList方法将这些regions组装到list box中,那么,如何传递由前一个方法所返回的regions list到后一个方法呢?

解决方案是如下所示的ShowToolWindow方法。为了更利于解释,我们来看看这个方法是如何实现的。

这个由向导生成的方法主体如下:

  1. /// <summary>
  2. /// This function is called when the user clicks the menu item that shows the
  3. /// tool window. See the Initialize method to see how the menu item is associated to
  4. /// this function using the OleMenuCommandService service and the MenuCommand class.
  5. /// </summary>
  6. private void ShowToolWindow(object sender, EventArgs e)
  7. {
  8. // Get the instance number 0 of this tool window. This window is single
  9. // instance so this instance is actually the only one.
  10. // The last flag is set to true so that if the tool window does not exists
  11. // it will be created.
  12. ToolWindowPane window = this.FindToolWindow(typeof(MyToolWindow), , true);
  13. if ((null == window) || (null == window.Frame))
  14. {
  15. throw new NotSupportedException(Resources.CanNotCreateWindow);
  16. }
  17. IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame;
  18. Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(windowFrame.Show());
  19. }

方法FindToolWindow MyToolWindow 类型作为第一个形参,通过调用这一方法,我们得到了window 对象,这一方法的调用同时也调用了MyToolWindow类的无参构造函数,如果你看一看这个类,你会发现它仅有这么一个无参构造函数,这个无参构造函数反过来也生成了MyControl类的一个新实例,这个实例包含一个Content属性.这就是链接list box和菜单命令的关键。[译注:这一段话我不太懂,可以参见文末的原文链接,希望博友能提供更好的翻译]

因此,在获取window之后,我将会把Content属性转换成MyControl类型并且调用PopulateList方法:

 

  1. ToolWindowPane window = this.FindToolWindow(typeof(MyToolWindow), , true);
  2. //<gouda>
  3. List<string> regionsList = this.GetRegions();
  4. //call the method PopulateList which is member in MyControl class
  5. //So, cast the Content property of window to MyControl
  6. ((MyControl)window.Content).PopulateList(regionsList, this);
  7. //</gouda>

移动选中的代码块到选中的Region中

这儿我的想法是检测被选中的代码块,一旦我们初始化viewHost,这个任务就会相当的简单(待会我将会说明什么时候在哪儿初始化viewHost)

初始化viewHost之后,我们能访问它的TextView属性,这个属性将会提供许多有用的的属性和方法。

请看下面代码:

  1. /// <summary>
  2. /// Moves the selected text to the given regionName
  3. /// If no selection, does nothing
  4. /// </summary>
  5. /// <param name="regionName"> The name of the destination region </param>
  6. internal void MoveToRegion(string regionName)
  7. {
  8. if (viewHost.TextView.Selection.IsEmpty)
  9. return;
  10. //Get the selected text
  11. string selectedText = viewHost.TextView.Selection.StreamSelectionSpan.GetText();
  12. //get the selected span to delete its contents
  13. Span deleteSpan = viewHost.TextView.Selection.SelectedSpans[];
  14. //now, delete the span as its text is saved in the selectedText
  15. viewHost.TextView.TextBuffer.Delete(deleteSpan);
  16. //Now, I will load all the text of the editor again, because it is subject to change
  17. allText = viewHost.TextView.TextSnapshot.GetText();
  18. //get the position at which region exists
  19. string fullRegionName = keyword + regionName;
  20. int regPos = allText.IndexOf(fullRegionName) + fullRegionName.Length;
  21. //insert the selected text at the specified position
  22. viewHost.TextView.TextBuffer.Insert(regPos, "\r" + selectedText);
  23. }

处理菜单项的点击

现在,我们初始化currentTextView,这样,我们才能初始化viewHost[译注:有关viewHost为什么被初始化请看上文中GetRegions()方法],然后显示工具窗:

  1. /// This function is the callback used to execute a command when the a menu item
  2. /// is clicked.
  3. /// See the Initialize method to see how the menu item is associated to this
  4. /// function using
  5. /// the OleMenuCommandService service and the MenuCommand class.
  6. /// </summary>
  7. private void MenuItemCallback(object sender, EventArgs e)
  8. {
  9. IVsTextManager txtMgr = (IVsTextManager)GetService(typeof(SVsTextManager));
  10. int mustHaveFocus = ;//means true
  11. //initialize the currentTextView
  12. txtMgr.GetActiveView(mustHaveFocus, null, out currentTextView);
  13. //Show the tool window
  14. ShowToolWindow(sender, e);
  15. }

引用

译注:本人近期做VS扩展的工作,无奈资料甚少,于是从分析代码入手,这是第一次翻译别人的代码讲解,才疏学浅,一定有不少疏漏和错误,下面给出原文链接

http://www.codeproject.com/Articles/69125/Moving-Code-Blocks-Among-Code-Regions-using-VS-201

欢迎大家留言指正

VSX(翻译)Moving Code Blocks Among Code Regions using VS 2010 Extensions的更多相关文章

  1. 从Script到Code Blocks、Code Behind到MVC、MVP、MVVM

    刚过去的周五(3-14)例行地主持了技术会议,主题正好是<UI层的设计模式——从Script.Code Behind到MVC.MVP.MVVM>,是前一天晚上才定的,中午花了半小时准备了下 ...

  2. 从Script到Code Blocks、Code Behind到MVC、MVP、MVVM(转载)

    http://www.cnblogs.com/indream/p/3602348.html 刚过去的周五(3-14)例行地主持了技术会议,主题正好是<UI层的设计模式——从Script.Code ...

  3. 【Code::Blocks】windows 环境下编译 Code::Blocks(已修正)

    Code::Blocks 在2012-11-25发布了最新的12.11版本,相比上一个版本(10.05),Code::Blocks 进行了许多改进和更新(Change log). 引用 Wikiped ...

  4. Code::Blocks

    Code::Blocks 是一个开放源码的全功能的跨平台C/C++集成开发环境. Code::Blocks是开放源码软件.Code::Blocks由纯粹的C++语言开发完成,它使用了著名的图形界面库w ...

  5. 使用Code::blocks在windows下写网络程序

    使用Code::blocks在windows下写网络程序 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创 ...

  6. Code::Blocks配置GTK+2和GTK+3

    Code::Blocks配置GTK+2和GTK+3 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根 ...

  7. code blocks 如何实现一键代码格式化

    问题:code blocks 如何实现一键代码格式化 解答:直接右键,选择format use ASstyle

  8. Code::Blocks的魅力

    Code::Blocks是C/C++集成开发环境,就像Dev C++.Visual Studio. 一.码代码时的技巧 按住Ctrl滚动鼠标滚轮,改变字体大小. Ctrl+D可复制当前行或选中块. C ...

  9. 错误:The Controls collection cannot be modified because the control contains code blocks (i.e. ). .

    用 <%# %>这种写法是写在数据绑定控件中的,之所以用 <%= %>会出现The Controls collection cannot be modified because ...

随机推荐

  1. js插件---JS表格组件BootstrapTable行内编辑解决方案x-editable

    js插件---JS表格组件BootstrapTable行内编辑解决方案x-editable 一.总结 一句话总结:bootstrap能够做为最火的框架,绝对不仅仅只有我看到的位置,它应该还有很多位置可 ...

  2. private SortedDictionary<string, object> Dic_values = new SortedDictionary<string, object>();

    private SortedDictionary<string, object> Dic_values = new SortedDictionary<string, object&g ...

  3. windows下gopath设置

    下载了go语言的安装包, 然后安装, 装完了需要设置三个地方: 1. 在windows的PATH变量中添加go的可执行文件所在的目录: PATH=C:\Go\bin;其他设置; 2. 设置 GOROO ...

  4. HDU 2564 饭卡

    饭卡 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submissi ...

  5. DG的数据保护模式

    DG的数据保护模式 数据保护膜有三种: – Maximum protection – Maximum availability – Maximum performance Maximum protec ...

  6. PHP 获取完整URL地址

    /** * 获取当前完整URL * @return string */ function get_url() { $sys_protocal = isset($_SERVER['SERVER_PORT ...

  7. Log4Net 用法记录

    https://www.cnblogs.com/lzrabbit/archive/2012/03/23/2413180.html https://blog.csdn.net/guyswj/articl ...

  8. pidof---查找指定名称的进程的进程号id号。

    pidof命令用于查找指定名称的进程的进程号id号. 语法 pidof(选项)(参数) 选项 -s:仅返回一个进程号: -c:仅显示具有相同“root”目录的进程: -x:显示由脚本开启的进程: -o ...

  9. 让JavaScript在Visual Studio 2015中编辑得更easy

    微软公布的Visual Studio 2015展示了该公司对于让该开发工具更好的支持主流的开发语言的工作.微软项目经理Jordan Matthiesen已经具体列出了一些具体处理JavaScript开 ...

  10. POJ 1426 Find The Multiple (DFS / BFS)

    题目链接:id=1426">Find The Multiple 解析:直接从前往后搜.设当前数为k用long long保存,则下一个数不是k*10就是k*10+1 AC代码: /* D ...