【小試插件開發】給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 > 2)
            {
                string path = doc.Path.Replace(projectPath.Name, "SuiBao.WebAdmin\\Plugins\\" + moduleName[2]);
                //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的插件庫也有很多實用的擴展包可以下載使用。總之,能解決你實際問題的任何過程和產出都是有價值、有意義的~



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM