隨着項目需求的逐步增長,後端開發框架在我手上也慢慢重構爲組件開發模式,總體結構相似於NopCommence。在這種結構中,每一個組件所在的類庫項目實際上是生成到網站項目裏指定的一個目錄的,而後隨之而來的就有一個不痛不癢的問題一直揮之不去。那就是每次在組件內修改代碼後都要清理解決方案,而後從新生成一下才能開始調試。若是不從新生成的話,修改後的代碼根本看不到效果,可是從新生成會替換上一次生成的程序集,這時候程序集有可能正在被iis express的進程佔用就會生成失敗,這時候就要先清理解決方案。 css
對於那種只在視圖裏改了一個文字的狀況還要從新生成簡直是不能忍,因此特別懷念以前web開發中保存文件後刷新瀏覽器就能看到效果的日子。雖說操做上也不是很複雜,但是因爲項目衆多,每次先清理再編譯一次特別浪費時間,最重要的是修改前端代碼徹底不須要去編譯啊,因而就有了下面的想法。 html
由於生成項目的時候本質上對靜態文件是一個複製過程,就想着有沒有辦法經過一個操做把組件內的視圖文件複製到指定目錄下去? 前端
既然有了這個想法,那也不能塞回去吧,就只有一個字了:幹! git
既然想給VS添加本身想要的功能,那就得給VS開發一個插件了。記得之前看過VS插件開發的帖子,估計用的上,照貓畫狗加上百度一番,終於把想要的東西實現了。 github
先建立一個插件項目: web
而後在項目中添加一個自定義命令MyCommand: express
能夠看到項目中出現了不少以「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的插件庫也有不少實用的擴展包能夠下載使用。總之,能解決你實際問題的任何過程和產出都是有價值、有意義的~