项目模板参数化(下)
本篇主要来介绍一下怎么利用IWizard接口提供的代码向导动态进行参数的设值。
方式三:利用IWizard接口动态设值
1,首先我们先看一下IWizard这个接口,这个接口第一在Microsoft.VisualStudio.TemplateWizardInterface.dll这个程序集中,命名空间为Microsoft.VisualStudio.TemplateWizard,签名如下:
1: public interface IWizard
2: {
3: void BeforeOpeningFile(ProjectItem projectItem);
4: void ProjectFinishedGenerating(Project project);
5: void ProjectItemFinishedGenerating(ProjectItem projectItem);
6: void RunFinished();
7: void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams);
8: bool ShouldAddProjectItem(string filePath);
9: }
我们暂时先不深入展开这个接口,界于我们讨论的主题,这里我们只要知道两个结论,我先给出这两个结论:
1)当我们用“新建项目”对话框新建项目,按下确定时,Visual Studio会实例化这个接口的实现(Visual Studio怎么知道要实例哪个接口实现呢,下文会给出),并且执行Runstarted这个方法。
2)关于RunStarted方法,这个方法的第二个参数是一个Dictionary<string, string>,通过这个字典,我们可以添加新的Key-Value,Key就是我们要添加的参数,Value就是我们要添加的参数的值,实现IWizard接口动态设值。
2,我们来添加一个TemplateWizard类库,添加EnvDTE和Microsoft.VisualStudio.TemplateWizardInterface这两个程序集引用。我们这个类库是需要强签名的,因为最后要部署到GAC里面。
添加一个DynamicCustomParameterWizard类,让它实现IWizard接口,代码如下:
1: public class DynamicCustomParameterWizard : IWizard
2: {
3: #region Implementation of IWizard
4:
5: public void RunStarted(object automationObject, Dictionary<string, string> replaceme
6: {
7: if (replacementsDictionary != null)
8: {
9: //$CurrentTimeWithFormat$
10: replacementsDictionary["$CurrentTimeWithFormat$"] = DateTime.Now.ToString("y
11:
12: //$AllExistingKeyValues$
13: StringBuilder stringBuilder = new StringBuilder();
14: foreach (var item in replacementsDictionary)
15: {
16: stringBuilder.Append(item.Key.PadRight(40));
17: stringBuilder.Append(item.Value);
18: stringBuilder.Append("=".PadRight(10));
19: stringBuilder.Append("\r\n");
20: }
21: replacementsDictionary["$AllExistingKeyValues$"] = stringBuilder.ToString();
22: }
23: }
24:
25: public bool ShouldAddProjectItem(string filePath)
26: {
27: return true;
28: }
29:
30: public void RunFinished()
31: {
32: }
33:
34: public void BeforeOpeningFile(ProjectItem projectItem)
35: {
36: }
37:
38: public void ProjectItemFinishedGenerating(ProjectItem projectItem)
39: {
40: }
41:
42: public void ProjectFinishedGenerating(Project project)
43: {
44: }
45:
46: #endregion
47: }
当然,我们还要新建一个DynamicCustomParameterExperiment.txt,
CurrentTimeWithFormat = $CurrentTimeWithFormat$
AllExistingKeyValues:
$AllExistingKeyValues$
以及,添加元数据到项目元数据文件中:
<ProjectItem ReplaceParameters="true">DynamicCustomParameterExperiment.txt</ProjectItem>
这时候,该解决前面留下的一个问题了,怎么让Visual Studio知道WebClient这个子项目模板要依赖我们定义好的DynamicCustomParameterWizard实现,既然我们说过,模板元数据文件是纽带,所以这里有一个新的节点可以定义这种接口向导实现,直接定义在VSTemplate根节点下:
<WizardExtension>
<Assembly>Ethan.Woo.TemplateWizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e82b5e824e88ddd5</Assembly>
<FullClassName>Ethan.Woo.TemplateWizard.DynamicCustomParameterWizard</FullClassName>
</WizardExtension>
3,部署和使用:项目模板的ZIP包还是和之前一样做法,注意,这里接口向导所在的程序集由于要部署到GAC里,所以,不需要打到这个ZIP包里面。既然Visual Studio有这个GAC部署接口向导程序集的限制,且不是给我们的一站式部署带来了麻烦?是的,确实是个问题,但是可以解决,我们在以后的文章中再专门探讨这个问题。
部署好ZIP包和程序集以后,我们来看下新建项目后的效果:
Tips:如果我们在开发一个复杂的IWizard接口实现,经常需要调试新版本时,手工部署GAC是很麻烦的,这里我推荐使用Build Events机制(Post-build event command line):
"C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0 Tools\gacutil.exe" /u "Ethan.Woo.TemplateWizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e82b5e824e88ddd5"
"C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0 Tools\gacutil.exe" /i "$(TargetPath)"
这篇文章还没有到结束的时候,让我们分析一下这个替换的结果,从这个结果可以得出一些新的结论:
1)在RunStarted这个方法开始执行的时候,方式一和方式二指定的参数已经有值,并且被置入这个Dictionary里面,分别对应绿色和灰色。蓝色的部分很明显,是方式三动态加入的。那么红色的部分是哪里来的,我推断最大的可能是,微软MSDN中没有公布出来或者漏掉的系统参数。
2)由于RunStarted这个方法公布了这个Dictionary,并且有结论1,那么,我们完全可以覆盖掉已经存在的参数和值,这在某些特殊场合是有用的。
这篇文章,已经对我们的话题阐述到位了,但是,对于IWizard的讨论,还没有结束,下一篇,我会深入来看一下IWizard这个接口。
摘自:http://www.ethan-woo.com/post/2011/05/02/VisualStudio-Template-Parameterization-Second.aspx