VS文件同步插件開發,vs同步插件
一、插件功能描述
插件監控一個xml文件,當該文檔有添加新元素在保存的時候將新增的元素同步到指定的目錄下。
二、模板的選擇
由於該功能是跟代碼編輯有關的,要監控文檔的保存事件,所以要在文檔打開的時候就注冊保存事件的響應方法。VS提供了一個接口,監聽文檔的打開事件,這個接口就是:IWpfTextViewCreationListener,接口代碼如下,該接口只有一個方法,在文檔打開的時候會調用TextViewCreated方法執行。
// 摘要:
// Listens to text view created events.
public interface IWpfTextViewCreationListener
{
// 摘要:
// Called when a text view having matching roles is created over a text data
// model having a matching content type.
//
// 參數:
// textView:
// The newly created text view.
void TextViewCreated(IWpfTextView textView);
}
所以隨便選擇一個模板都是可以的,只要在項目裡面將模板創建的插件入口類改為繼承IWpfTextViewCreationListener接口,並實現其接口即可。由於該功能需要用到選項配置功能,所以我建議讀者使用Visual Stuido Package模板,因為使用該模板添加選項頁會比較容易。這是msdn上在使用Visual Studio Package模板的情況下如何添加選項頁的教程:http://msdn.microsoft.com/en-us/library/bb166195.aspx。我選擇的是Editor Text Adornment模板,因為該模板創建的入口類就是繼承IWpfTextViewCreationListener接口,但是使用該模板的話,添加選項頁會比較麻煩一點。
三、添加選項頁
按照msdn上的教程,我們知道需要添加兩個類:繼承Package的類和繼承DialogPage的類,過程我就不再贅述。如果緊緊按照msdn上的教程,你會發現在工具-選項下根本沒有我們添加的選項頁(非Visual Studio Package模板的情況),這是為什麼呢?原來在我們的csproj文件和source.extension.vsixmanifest裡缺少了一些元素,按照下面的步驟就可以實現添加選項頁的功能:
1、打開source.extension.vsixmanifest,選擇Assets選項,點擊New按鈕,彈出圖-1窗口
圖-1
Type選擇Microsoft.VisualStudio.Assembly,Source選擇A project in current solution,Project選擇當前的插件項目,點擊OK添加完成,再次點擊New按鈕,這次的Type選擇Microsoft.VisualStudio.VsPackage,Source和Project跟第一次的一樣即可。
2、將csproj文件裡的GeneratePkgDefFile元素改為true。
3、GeneratePkgDefFile元素後添加<CopyBuildOutputToOutputDirectory>true</CopyBuildOutputToOutputDirectory>
4、在GeneratePkgDefFile元素前添加<IncludeAssemblyInVSIXContainer>true</IncludeAssemblyInVSIXContainer>,注意IncludeAssemblyInVSIXContainer這個元素一定要添加在GeneratePkgDefFile和CopyBuildOutputToOutputDirectory元素之前。
經過上面的四個步驟,我們添加的選項頁就會顯示在工具-選項裡了,如果缺少了第一個步驟的話會出現”加載此屬性頁時出錯“的錯誤。
四、監聽保存事件
通過查看TextViewCreated函數的參數類型textView,可以知道IWpfTextView 接口並沒有包含文檔保存的事件。那麼,我們該如何才能訂閱保存事件呢?通過查找相關的資料,發現可以通過以下方式獲取文檔的保存事件:
//EnvDTE.DTE _dte
this._dte = ServiceProvider.GlobalProvider.GetService(typeof(DTE)) as DTE;
//EnvDTE.Events _events
this._events = this._dte.Events;
//EnvDTE.DocumentEvents _docEvents
this._docEvents = this._events.DocumentEvents;
一定要記住一定要將上面的三個對象定義為全局變量,否則就沒辦法響應保存事件,這是因為C#的垃圾回收機制造成的,讀者可以自己試一下定義為局部變量的情況。
以下是該插件的部分代碼
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017012018384397.gif)
![]()
1 namespace AppSettingsSync
2 {
3 public class TextViewListener
4 {
5 /// <summary>
6 /// 文檔信息
7 /// </summary>
8 private ITextView _view;
9
10 private DTE _dte;
11 private Events _events;
12 private DocumentEvents _docEvents;
13 /// <summary>
14 /// 文檔是否修改過
15 /// </summary>
16 private bool _isChanged;
17 /// <summary>
18 /// 保存的時候是否自動同步到其他AppSettings.xml
19 /// </summary>
20 private bool _isAutoReplace = true;
21 /// <summary>
22 /// 觸發同步操作的AppSetting.xml
23 /// </summary>
24 private string _sourceFile;
25 /// <summary>
26 /// 要被同步的AppSettings.xml所在的文件目錄
27 /// </summary>
28 private string _targetFolder;
29
30 /// <summary>
31 /// 打開文檔時觸發
32 /// </summary>
33 /// <param name="textView"></param>
34 public TextViewListener(IWpfTextView textView)
35 {
36 this._view = textView;
37 this._dte = ServiceProvider.GlobalProvider.GetService(typeof(DTE)) as DTE;
38 Properties props = this._dte.get_Properties("IStrong", "AppSettingsSync");
39 if (props == null)
40 return;
41 this._sourceFile = (string)props.Item("SourceXmlFilePath").Value;
42 //File.AppendAllText(@"D:\log.txt", "源文件" + this._sourceFile + "當前文件" + this._dte.ActiveDocument.FullName);
43 if (!this._dte.ActiveDocument.FullName.Equals(this._sourceFile, StringComparison.OrdinalIgnoreCase))
44 return;
45 //獲取DTE對象
46 this._events = this._dte.Events;
47 this._docEvents = this._events.DocumentEvents;
48 //訂閱文檔保存和修改事件
49 this._docEvents.DocumentSaved += _docEvents_DocumentSaved;
50 this._view.TextBuffer.Changed += TextBuffer_Changed;
51 }
52
53 /// <summary>
54 /// 文檔修改事件
55 /// </summary>
56 /// <param name="sender"></param>
57 /// <param name="e"></param>
58 void TextBuffer_Changed(object sender, Microsoft.VisualStudio.Text.TextContentChangedEventArgs e)
59 {
60 if (e.Changes.Count() > 0)
61 this._isChanged = true;
62 }
63
64 /// <summary>
65 /// 文檔保存事件
66 /// </summary>
67 /// <param name="Document"></param>
68 async void _docEvents_DocumentSaved(Document Document)
69 {
70 try
71 {
72 //File.AppendAllText(@"D:\log.txt", "觸發保存事件");
73 //獲取Tool->Opetions->IStrong->AppSettingsSync配置項內容
74 Properties props = this._dte.get_Properties("IStrong", "AppSettingsSync");
75 if (props == null)
76 return;
77 this._sourceFile = (string)props.Item("SourceXmlFilePath").Value;
78 //保存時要同時滿足是源AppSettings.xml文件和該文件有被修改過
79 if (Document.FullName.Equals(this._sourceFile, StringComparison.OrdinalIgnoreCase) && this._isChanged)
80 {
81 this._isAutoReplace = (bool)props.Item("IsAutoSync").Value;
82 this._targetFolder = (string)props.Item("TargetFolder").Value;
83 //手動選擇要同步的文件
84 if (!this._isAutoReplace)
85 {
86 SelectFiles sf = new SelectFiles(this._sourceFile, this._targetFolder);
87 sf.ShowDialog();
88 this._isChanged = false;
89 }
90 else
91 {
92 //自動同步文件
93 string fileName = System.IO.Path.GetFileName(this._sourceFile);
94 string[] files = Directory.GetFiles(this._targetFolder, fileName, SearchOption.AllDirectories);
95 this._isChanged = false;
96 await SyncHelper.SyncAppSettings(this._sourceFile, files);
97 //同步完成後修改Visual Studio狀態欄信息
98 IVsStatusbar bar = ServiceProvider.GlobalProvider.GetService(typeof(SVsStatusbar)) as IVsStatusbar;
99 bar.SetText("AppSettings配置文件同步完成。");
100 }
101 }
102 }
103 catch (Exception ex)
104 {
105 MessageBox.Show(ex.Message, "提示", MessageBoxButton.OK, MessageBoxImage.Error);
106 }
107 }
108 }
109 }
View Code
獲取VS的主題顏色
/// <summary>
/// 將模態窗口的顏色設置為Visual Studio的背景色
/// </summary>
/// <param name="themeColor"></param>
/// <returns></returns>
private Color converVsThemeColor(vsThemeColors themeColor)
{
DTE2 dte2 = (EnvDTE80.DTE2)System.Runtime.InteropServices.Marshal.
GetActiveObject("VisualStudio.DTE.12.0");
uint color = dte2.GetThemeColor(themeColor);
int a = (int)color / 0x1000000;
int b = (int)(color - a * 0x1000000) / 0x10000;
int g = (int)(color - a * 0x1000000 - b * 0x10000) / 0x100;
int r = (int)(color - a * 0x1000000 - b * 0x10000 - g * 0x100);
return Color.FromArgb(0xFF, r, g, b);
}