One of the issues that I experienced early on when building the various F# templates that are available on Visual Studio Gallery, was that of invalid references between some of the projects that made up the multi-project templates. This is because the act of creating a new project from an installed template always causes new project GUIDs to be created. Since the project references are tied to the GUIDs of the projects that existed when the multi-project template was developed, the references were being marked as suspect. This didn't really cause errors; however, it did cause an unsightly warning icon:
the best approach for accomplishing the association of projects within the multi-project template was to create a custom template wizard. This post will show how to create one of these custom template wizards using an example similar to the code which was developed for the F# and C# ASP.NET MVC 3 template.
Follow these simple steps:
1. Create a new F# project and add a class that implement IWizard and uses the standard DTE commands to add each project as a reference. An example is shown below:
namespace FSharpMVC3TemplateWizard open System open System.Collections.Generic open System.Collections open EnvDTE open Microsoft.VisualStudio.TemplateWizard open VSLangProj [<AutoOpen>] module TemplateWizardMod = let AddProjectReference (target:Option<Project>) (projToReference:Option<Project>) = if ((Option.isSome target) && (Option.isSome projToReference)) then let vsControllerProject = target.Value.Object :?> VSProject let existingProjectReference = vsControllerProject.References.Find(projToReference.Value.Name) if (existingProjectReference <> null) then existingProjectReference.Remove() vsControllerProject.References.AddProject(projToReference.Value) |> ignore let BuildProjectMap (projectEnumerator:IEnumerator) = let rec buildProjects (projectMap:Map<string,Project>) = match projectEnumerator.MoveNext() with | true -> let project = projectEnumerator.Current :?> Project projectMap |> Map.add project.Name project |> buildProjects | _ -> projectMap buildProjects Map.empty type TemplateWizard() = let projectRefs = [("Controllers", "Models"); ("Web", "Core") ("Web", "Models"); ("Web", "Controllers")] [<DefaultValue>] val mutable Dte : DTE interface IWizard with member x.RunStarted (automationObject:Object, replacementsDictionary:Dictionary<string,string>, runKind:WizardRunKind, customParams:Object) = x.Dte <- automationObject :?> DTE member x.ProjectFinishedGenerating (project:Project) = try let projects = BuildProjectMap (x.Dte.Solution.Projects.GetEnumerator()) projectRefs |> Seq.iter (fun (target,source) -> do AddProjectReference (projects.TryFind target) (projects.TryFind source)) with | _ -> "Do Nothing" |> ignore member x.ProjectItemFinishedGenerating projectItem = "Do Nothing" |> ignore member x.ShouldAddProjectItem filePath = true member x.BeforeOpeningFile projectItem = "Do Nothing" |> ignore member x.RunFinished() = "Do Nothing" |> ignore2. Add references to EnvDTE.dll, Microsoft.VisualStudio.TemplateWizardInterface.dll, and VSLangProj.dll.
3. This assembly needs to be signed with a strong name, so add the "keyfile" switch to the "Other flags" field on the build properties for your F# template project and specify the location of your strongly named key file as shown below:
4. In the source.extension.vsixmanifest file of the VSIX project, add a new Content reference to the TemplateWizard project with a content type of Template Wizard as show here:
4. Add a WizardExtension section to your project collection .vstemplate file (named FSMVC3.vstemplate in the F# and C# ASP.NET MVC 3 Template)
<WizardExtension> <Assembly>FSharpMVC3TemplateWizard, Version=0.0.0.0, Culture=neutral, PublicKeyToken=ba79043a32149735</Assembly> <FullClassName>FSharpMVC3TemplateWizard.TemplateWizard</FullClassName> </WizardExtension>
5. Repackage your multi-project template as described in this post.
6. Build it, test it, and call it a day.
Where to go from here:
This simple edition to our template has solved the little problem that was previously experienced; however, we have only seen the tip of the iceberg when it comes to the power that is available to us through these template wizards. In a future post, I plan to explore these possibilities in greater details.