【小试插件开发】给Visual Studio装上自己定制的功能来提高代码调试效率
背后的故事
随着项目需求的逐步增加,后端开发框架在我手上也慢慢重构为组件开发模式,整体结构类似于NopCommence。在这种结构中,每个组件所在的类库项目其实是生成到网站项目里指定的一个目录的,然后随之而来的就有一个不痛不痒的问题一直挥之不去。那就是每次在组件内修改代码后都要清理解决方案,然后重新生成一下才能开始调试。如果不重新生成的话,修改后的代码根本看不到效果,但是重新生成会替换上一次生成的程序集,这时候程序集有可能正在被iis express的进程占用就会生成失败,这时候就要先清理解决方案。
对于那种只在视图里改了一个文字的情况还要重新生成简直是不能忍,所以特别怀念之前web开发中保存文件后刷新浏览器就能看到效果的日子。虽然说操作上也不是很复杂,可是由于项目众多,每次先清理再编译一次特别浪费时间,最重要的是修改前端代码完全不需要去编译啊,于是就有了下面的想法。
因为生成项目的时候本质上对静态文件是一个复制过程,就想着有没有办法通过一个操作把组件内的视图文件复制到指定目录下去?
既然有了这个想法,那也不能塞回去吧,就只有一个字了:干!
把想法付诸实践
既然想给VS添加自己想要的功能,那就得给VS开发一个插件了。记得以前看过VS插件开发的帖子,估计用的上,照猫画狗加上百度一番,终于把想要的东西实现了。
先创建一个插件项目:

然后在项目中添加一个自定义命令MyCommand:

可以看到项目中出现了很多以“MyCommand”开头的文件,不用猜也知道都是和这个命令有关的一些文件。其中“MyCommand.cs”需要特别关注,因为你的命令创建、回调事件都是在这个类中定义的,这里面必须要了解的就是MenuItemCallback方法,看名称大致可以猜到它是你命令执行的回调函数。说白了,你的命令想干些什么事就是在这个方法里面code出来的,看一下自动生成的代码:
/// <summary>
/// This function is the callback used to execute the command when the menu item is clicked.
/// See the constructor to see how the menu item is associated with this function using
/// OleMenuCommandService service and MenuCommand class.
/// </summary>
/// <param name="sender">Event sender.</param>
/// <param name="e">Event args.</param>
private void MenuItemCallback(object sender, EventArgs e)
{
string message = string.Format(CultureInfo.CurrentCulture, "Inside {0}.MenuItemCallback()", this.GetType().FullName);
string title = "MyCommand"; // Show a message box to prove we were here
VsShellUtilities.ShowMessageBox(
this.ServiceProvider,
message,
title,
OLEMSGICON.OLEMSGICON_INFO,
OLEMSGBUTTON.OLEMSGBUTTON_OK,
OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
}
通过VS的方法提示和代码注释可以看到方法体内主要做了一个弹框操作,类似Winform的MessageBox.Show()的玩意儿,那我们就在这里根据实际需求来写代码。
我的需求是:通过执行这个命令把当前编辑的文件保存到本地指定的一个目录中,如果有同名文件则直接替换。非常简单的需求,那就开始像平常开发那样啪啪啪地coding了。中间只有一个需要注意的点,就是要根据当前文件所在的组件名称去拼接目标目录,好在我的项目命名都是有规律的,所以也就比较轻松了。主要代码为:
private void MenuItemCallback(object sender, EventArgs e)
{
var dte = this.ServiceProvider.GetService(typeof(DTE)) as DTE;
var doc = dte?.ActiveDocument;//当前文档
if (doc == null)
{
ShowErrorMessage();
return;
}
if (!doc.Name.EndsWith(".cshtml")&&!doc.Name.EndsWith(".js")&&!doc.Name.EndsWith(".css"))
{
ShowErrorMessage();
return;
}
doc.Save();
dte.StatusBar.Text = "suibao:当前修改已保存";
DirectoryInfo directory = new DirectoryInfo(doc.Path);
var projectPath = directory.Parent.Parent;
var moduleName = projectPath.Name.Split('.');
if (moduleName.Length > )
{
string path = doc.Path.Replace(projectPath.Name, "SuiBao.WebAdmin\\Plugins\\" + moduleName[]);
//doc.Save(path);
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
File.Copy(doc.FullName, path + doc.Name, true);//复制且替换
dte.StatusBar.Text = "suibao:保存到组件目录完成";
}
else
{
ShowErrorMessage();
}
} private void ShowErrorMessage()
{
VsShellUtilities.ShowMessageBox(ServiceProvider, "无效操作!", "系统提示", OLEMSGICON.OLEMSGICON_WARNING, OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
}
目前只做了Razor视图、js、css的处理,同时也做了异常操作处理,并且在VS状态栏中给出操作结果提示。然后编译、运行,这时会在VS的主菜单“工具”下面第一行多了自定义的命令:

觉得“Invoke MyCommand”这个名字不喜欢想自己定义?没问题~打开项目中的“MyCommandPackage.vsct”文件,找到Buttons这个节点,里面定义了我们命令的各种属性,改名称改图标自己看着办:
<Button guid="guidMyCommandPackageCmdSet" id="MyCommandId" priority="0x0100" type="Button">
<Parent guid="guidMyCommandPackageCmdSet" id="MyMenuGroup" />
<Icon guid="guidImages" id="bmpPic1" />
<Strings>
<ButtonText>MyCommandDemo</ButtonText>
</Strings>
</Button>
什么?觉得每次都要点2次菜单太麻烦想搞个快捷键?小意思~有两种方式,如下。
方式一,在配置文件中设置快捷键,参考这里:
<KeyBindings>
<KeyBinding guid="guidMyCommandPackageCmdSet" id="MyCommandId"
editor="guidVSStd97" key1="Q" mod1="CONTROL"/>
</KeyBindings>
方式二,在VS中给命令设置快捷键:
依次打开菜单“工具”-“选项”-“环境”-“键盘”,按名称搜索到命令,然后输入快捷键,点击“分配”,再保存一下,搞定。

持续地探索
折腾到现在总算是解决了其中一个问题,内心多少有点小兴奋。回到项目中,依然有个痛点亟需解决,那就是关于编译的问题。稍微分析一下不难发现,这个问题的核心其实就是DLL文件生成与存放路径。于是就打算继续按上面的套路,在本项目生成程序集然后copy到web项目中,然后就开干了。在写代码过程中,发现EnvDTE.DTE这个接口提供了很多操作VS资源的方法,然后顺着一路找下来看到了SolutionBuild这个接口对解决方案有各种Build相关的方法(参考这里和这里),于是果然放弃之前的套路,打算把“清理”和“重新编译”两个命令结合到一起。因为按原来的思路,也是要先编译完才能复制DLL,中间还要解决DLL被进程占用的问题,还不如直接Clean+Build一条龙来的快。代码非常简单:
private void MenuItemCallback(object sender, EventArgs e)
{
var dte = this.ServiceProvider.GetService(typeof(DTE)) as DTE;
dte.Solution.SolutionBuild.Clean(true);
dte.Solution.SolutionBuild.Build();
}
有了上面提到的那些接口,发现能够干的事太多了,几乎可以随心所欲来扩展自己想要的功能。
总结
本文的目的并不是展示Visual Studio插件开发的流程,只是借这个例子来阐述遇到问题时要积极寻找合适的工具或方法去解决问题,对于过程中碰到未知领域,要乐于探索,对于工作中那种重复性特别高的事,尽可能想办法来提高效率。我是第一次接触VS插件开发,本文的例子也是最最基础的尝试。网上有很多强大和酷炫的插件开发示例,VS的插件库也有很多实用的扩展包可以下载使用。总之,能解决你实际问题的任何过程和产出都是有价值、有意义的~
【小试插件开发】给Visual Studio装上自己定制的功能来提高代码调试效率的更多相关文章
- Visual Studio 2013 上使用 Github
教你如何在 Visual Studio 2013 上使用 Github 介绍 我承认越是能将事情变简单的工具我越会更多地使用它.尽管我已经知道了足够的命令来使用Github,但我宁愿它被集成到IDE中 ...
- 在Visual Studio 2017上配置Glut
在Visual Studio 2017上配置Glut 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 在Visual Studio 2017上配置并使用 ...
- 在Visual Studio 2017上配置并使用OpenGL
在Visual Studio 2017上配置并使用OpenGL 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 首先在Windows下安装Visual ...
- Visual Studio Developer Assistant 3月新功能展示
Visual Studio Developer Assistant 3月添加了以下新功能: https://visualstudiogallery.msdn.microsoft.com/a116671 ...
- 如果不用 ReSharper,那么 Visual Studio 2019 能还原 ReSharper 多少功能呢?
原文:https://blog.csdn.net/WPwalter/article/details/100158000 本文的内容分为三个部分: Visual Studio 能完全还原的 ReShar ...
- [vs2008]Visual Studio 2008 SP1添加或删除功能提示查找SQLSysClrTypes.msi文件
前言 今天接到领导布置的一个任务,是之前同事负责的项目.离职了,现在客户有些地方需要修改,由于我之前参与过,就落在我的头上了. 然后我就把代码弄了过来,打开发现其中需要用到水晶报表.(我觉得不好用,不 ...
- Visual Studio 2015上安装Entity Framework Power Tools
Entity Framework Power Tools是个非常好用的EF Code First插件.通过它能够非常简单地生成和数据库结构匹配的model和dbcontext代码.使用的方法,这里有介 ...
- 在Visual Studio 2013上安装SQLite designer components
最近搞一个WinCE项目,要用到SQLite.看人家都能直接在Visual Studio上连接SQLite.我也按照他们的方法安装了最新的Setups for 32-bit Windows (.NET ...
- [转]一步步教你如何在 Visual Studio 2013 上使用 Github
介绍 我承认越是能将事情变简单的工具我越会更多地使用它.尽管我已经知道了足够的命令来使用Github,但我宁愿它被集成到IDE中.在本教程中,我会告诉你使用Visual Studio 2013如何实现 ...
随机推荐
- CSS3知识点整理(五)----响应式设计及其他属性
介绍Media Queries与Responsive设计以及外轮廓属性.resize属性.CSS3生成内容等 学会如何使用CSS3中的Media Queries模块来让一个页面适应不同的终端(或屏幕尺 ...
- php安装详解
获取资源: cd /usr/local/src/ wget http://cn2.php.net/distributions/php-5.4.45.tar.bz2 tar jxvf php-5.4.4 ...
- Odoo安装
打开终端机0. sudo passwd root #设定超级使用者密码1. sudo apt-get update #更新软件源2. sudo apt-get dist-upgrade #更新软件包, ...
- C# XmlSerializer将对象序列化以及反序列化(Sqlite数据库)
获取不同数据库表信息将筛选出来的信息序列化以及反序列化 相应类结构: Class Tables: [Serializable] [XmlRoot("Table")] public ...
- 什么是DOCTYPE?
一.DOCTYPE是什么? DOCTYPE是文档类型的速记(文档.网页中指定页面的XHTML或HTML版本类型).使符合标准的页面,一个关键组成部分是DOCTYPE声明.只有确定了正确的XHTML D ...
- HTTP状态码理解
100-199 用于指定客户端应相应的某些动作. 200-299 用于表示请求成功. 300-399 用于已经移动的文件并且常被包含在定位头信息中指定新的地址信息. 400-499 用于指出客户端的错 ...
- 各种ORM框架对比(理论篇,欢迎来观摩)
各种ORM框架对比 目前框架有以下 PetaPoco Dapper.NET Massive Simple.Data Chain PetaPoco 轻量级,以前单文件,目前有维护形成项目级别,适合多个数 ...
- 浅谈PHP的CI框架(一)
作为前端开发人员,掌握一门后端语言是必不可少的,PHP的CI框架是一个快速开发框架,基于MVC,比较接近原生PHP,在原有的PHP代码上封装了许多类,易上手,容易扩展,适用于小项目,并且CI的文档及案 ...
- JavaScript 毒瘤和糟粕(需要注意的地方)
简介 我想这是在我总结JavaScript系列中最为需要注意的,最为重要的内容.你必须要去了解这些问题特性,才能准备好应对措施,这真的很重要. 毒瘤 全局变量 全局变量的存在的确是带来了方便,但是我觉 ...
- Java Web(九) 用户管理系统
前面学习了一大堆,什么JSP,Servlet.jstl.el等等等,大多是一些死的东西,只要会其语法,知道怎么用就行了,所以做了一个小小的只有增删改查的小demo,为的就是熟悉这些知识.灵活运用起来. ...