背后的故事

随着项目需求的逐步增加,后端开发框架在我手上也慢慢重构为组件开发模式,整体结构类似于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. esri-leaflet入门教程(2)-地图的HelloWorld

    esri-leaflet入门教程(2)-地图的HelloWorld by 李远祥 常言道"君子性非异也,善假于物也".这句话在IT界同样也适用,只不过IT界有更为时髦的说法:&qu ...

  2. Git合并分支命令:git merge --ff

    今天研究了一下git merge命令常用参数,并分别用简单的例子实验了一下,整理如下: 输入git merge -h可以查看相关参数: --ff  快速合并,这个是默认的参数.如果合并过程出现冲突,G ...

  3. 下一个项目为什么要用 SLF4J

    阿里巴巴 Java 开发手册 前几天阿里巴巴在云栖社区首次公开阿里官方Java代码规范标准,就是一个PDF手册,有命名规范,让你知道自己原来取的每一个类名.变量名都是烂名字,真替你家未来孩子担心:有集 ...

  4. 自己动手系列——实现一个简单的LinkedList

    LinkedList与ArrayList都是List接口的具体实现类.LinkedList与ArrayList在功能上也是大体一致,但是因为两者具体的实现方式不一致,所以在进行一些相同操作的时候,其效 ...

  5. [Android] "Failed to find Java version for 'C:\Windows\system32\java.exe"

    Impossible to install SDK r17 on win 7 x64 "Failed to find Java version for 'C:\Windows\system3 ...

  6. WP8.1开发中找程序下的Assets文件夹

    这俩天在开发另一个程序时,遇到一个小问题:如何调用程序下的Assets文件夹及其下的文件和文件夹: 在网上找了两天,基本上是关于如何调用手机中库的方法,没找到有关介绍如何调用查找 编译前添加图片或其它 ...

  7. DDD中的分层架构

    DDD中的分层架构很好的应用了关注点分离原则Separation of Concerns(SOC),每一层做好自己的事情,减少交叉 表现层 表现层提供用来完成任务的用户界面,如webform wpf ...

  8. Java多线程编程(四)—浅谈synchronized与lock

    一.共享资源竞争问题 在Java语言的并发编程中,由于我们不知道线程实际上在何时运行,所以在实际多线程编程中,如果两个线程访问相同的资源,那么由于线程运行的不确定性便会在这种多线程中产生访问错误.所以 ...

  9. C#一定比C++性能差?当然不!破除迷信,从我做起!

    几乎所有的程序员在初学编程之时,都被灌输过"托管语言(Java.C#)性能比非托管语言(C.C++)差好多" 的迷信教条.如果你问他们为什么,他们一定会说:托管语言需要通过虚拟机或 ...

  10. Android HelloChart Demo

    这几天,要做一个图标的统计,自己去网上查了下,现在用的比较多的有三种,AChartEngine 是Google的一个开源图表库 这种我最开始就去导demo去了解他,不过里面是是英文,不好研究.我就放弃 ...