插件监控一个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#的垃圾回收机制造成的,读者可以自己试一下定义为局部变量的情况。
以下是该插件的部分代码
class="code_img_closed" src="/Upload/Images/2014111723/0015B68B3C38AA5B.gif" alt="" />logs_code_hide('5dd65a0c-6068-4c76-85ca-eecf5bda7d5d',event)" src="/Upload/Images/2014111723/2B1B950FA3DF188F.gif" alt="" />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); }