【小试插件开发】给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如何实现 ...
随机推荐
- java_JDBC(3)
Batch和Fetch两个特性非常重要.Batch相当于JDBC的写缓冲,Fetch相当于读缓冲 如果把JDBC类比为JAVA IO的话,不使用Fetch和Batch相当于直接使用FileInputS ...
- 使用vlmcsd自建KMS服务~一句命令激活windows/office
服务作用:在线激活windows和office 适用对象:VOL版本的windows和office 适用版本:截止到win10和office2016的所有版本 服务时间:24H,偶尔更新维护 优点:在 ...
- 环信SDK 头像、昵称、表情自定义和群聊设置的实现 一(附源码)
前言: 环信的SDK在公司的项目中有用到,现在用到的是群聊的部分,这里我们分析总结一下自己对环信给的DEMO大概的拆解一下,说说我们怎么样充分的利用这个demo来写我们所需要的业务.这个也由于篇幅的原 ...
- IE兼容问题及处理
1.在IE6下,子元素能撑开父级设置好的宽高 2.IE6下的最小高度,高度小于19px的元素在IE6下会被当做19px来处理 解决办法:overflow:hidden; 3.IE6下 不支持1px的点 ...
- 从并发处理谈PHP进程间通信(二)System V IPC
.container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...
- shell-早间学习,每日一点-5
http://www.cnblogs.com/liuling/p/2013-8-4-01.htmlhttp://www.cnblogs.com/stephen-liu74/category/32665 ...
- wikioi 3132 高精度乘法(FFT)
第一次学FFT,先膜拜一下法法塔大神ORZ 关于FFT的话,有一篇博文特别赞http://z55250825.blog.163.com/blog/static/15023080920143127465 ...
- Brackets 前端编辑器试用
Brackets编辑器介绍 "一个现代的,开源的,了解网页设计的编辑器"这是官方的宣传语.也就是说它适用于网页开发,包含了许多亮点功能:实时预览(Live Preview).内联编 ...
- es6的解构赋值学习(1)
相对es5的简单的"="赋值来说,es6增加了一种新的赋值模式--解构赋值,按照它的规则,可以从数组和对象中提取值来对变量进行赋值,个人觉得方便了很多,但是这个模式有点恶心人,相比 ...
- IKAnalyzer 分词
IK Analyzer 3.0特性 采用了特有的"正向迭代最细粒度切分算法",具有80万字/秒的高速处理能力 采用了多子处理器分析模式,支持:英文字母(IP地址.Email.URL ...