背后的故事

随着项目需求的逐步增加,后端开发框架在我手上也慢慢重构为组件开发模式,整体结构类似于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装上自己定制的功能来提高代码调试效率的更多相关文章

  1. Visual Studio 2013 上使用 Github

    教你如何在 Visual Studio 2013 上使用 Github 介绍 我承认越是能将事情变简单的工具我越会更多地使用它.尽管我已经知道了足够的命令来使用Github,但我宁愿它被集成到IDE中 ...

  2. 在Visual Studio 2017上配置Glut

    在Visual Studio 2017上配置Glut 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 在Visual Studio 2017上配置并使用 ...

  3. 在Visual Studio 2017上配置并使用OpenGL

    在Visual Studio 2017上配置并使用OpenGL 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 首先在Windows下安装Visual ...

  4. Visual Studio Developer Assistant 3月新功能展示

    Visual Studio Developer Assistant 3月添加了以下新功能: https://visualstudiogallery.msdn.microsoft.com/a116671 ...

  5. 如果不用 ReSharper,那么 Visual Studio 2019 能还原 ReSharper 多少功能呢?

    原文:https://blog.csdn.net/WPwalter/article/details/100158000 本文的内容分为三个部分: Visual Studio 能完全还原的 ReShar ...

  6. [vs2008]Visual Studio 2008 SP1添加或删除功能提示查找SQLSysClrTypes.msi文件

    前言 今天接到领导布置的一个任务,是之前同事负责的项目.离职了,现在客户有些地方需要修改,由于我之前参与过,就落在我的头上了. 然后我就把代码弄了过来,打开发现其中需要用到水晶报表.(我觉得不好用,不 ...

  7. Visual Studio 2015上安装Entity Framework Power Tools

    Entity Framework Power Tools是个非常好用的EF Code First插件.通过它能够非常简单地生成和数据库结构匹配的model和dbcontext代码.使用的方法,这里有介 ...

  8. 在Visual Studio 2013上安装SQLite designer components

    最近搞一个WinCE项目,要用到SQLite.看人家都能直接在Visual Studio上连接SQLite.我也按照他们的方法安装了最新的Setups for 32-bit Windows (.NET ...

  9. [转]一步步教你如何在 Visual Studio 2013 上使用 Github

    介绍 我承认越是能将事情变简单的工具我越会更多地使用它.尽管我已经知道了足够的命令来使用Github,但我宁愿它被集成到IDE中.在本教程中,我会告诉你使用Visual Studio 2013如何实现 ...

随机推荐

  1. ArcGIS制图表达Representation-制图表达介绍

    ArcGIS制图表达技术-制图表达介绍 by 李远祥 在基于GIS数据的制图中,大部分都是使用的数据+符号应用的这种模式.这种模式已经被应用很多年,而且也是非常成熟.对应在ArcGIS体系里面,就是数 ...

  2. 使用python制作ArcGIS插件(5)其他技巧

    使用python制作ArcGIS插件(5)其他技巧 by 李远祥 使用python做插件开发,除了了解ArcToolBox工具之外,还需要在了解ArcPy的相关函数和接口.只有掌握了这些,才可以顺利的 ...

  3. 利用canvas制作乱跑的小球

    canvas制作乱跑的小球 说明:将下面的代码放到html的body就可以,键盘控制上(W)下(S)左(A)右(D) <body> <canvas id="canvas&q ...

  4. C#编程基础->XML系列导航

    缘由 最近开发的小程序过程中需要涉及到XML相关操作,突然发现自己对于这知识点了解的太少,急需学习加强.刚好项目的时间也不是很紧急,自己就总结XML相关知识点.一个方面自己学习,一个方面也希望可以帮到 ...

  5. c#类,接口,结构,抽象类介绍 以及抽象和接口的比较

    c#中的类是最常见的实际上就是对某种类型的对象定义变量和方法的原型. 结构是值类型,而类是引用类型. 1.与类不同,结构的实例化可以不使用 new 运算符.结构可以声明构造函数,但它们必须带参数. 2 ...

  6. nodejs笔记2 --关于nodejs最新启动方式

    1,运行应用以前是node app.js或者 supervisor app.js 2, 现在的应用方式是npm start或者supervisor bin/www

  7. LINQ查询表达式和LAMBDA点标记方法基础

    在上一篇文章中,我们介绍了LINQ的一些基本用法,这一篇我们来看下另一种更简洁更优雅的表达式,Lambda表达式,也可以叫做点标记方法. 相信大家在实际工作中都用到过这两种方式,下面我们还是用实例来看 ...

  8. Github windows客户端简单上手教程

    作为一个前端,如果不知道GitHub,那你有可能就是一个假前端(O(∩_∩)O哈哈~)开个玩笑...进入正题,咳咳... 1.第一步要在GitHub官网下载最新的客户端,网址是https://desk ...

  9. cli/php.ini和fpm/php.ini的区别

    1. 当从命令行执行PHP binary时,cli/php.ini会被使用,你可以通过在命令行运行php --ini来查看. 2. 当PHP运行做为FPM时,会使用fpm/phh.ini,其中一种情况 ...

  10. java学习笔记——IO流部分

    IO流常用的有:字符流.字节流.缓冲流.序列化.RandomAccessFile类等,以上列出的都是开发中比较常用的. 1.字节流: 字节流包含:FileInputStream/FileOutputS ...