Note: This post was written a while back but sat in draft. I’ve published this now, but I’m not sure it’s relevant to the latest versions etc. so please bear this in mind.
In this post we’re going to create a Prism Project Template using VSIX (as opposed to the File | Export Template option in Visual Studio). This is a relatively simple project template which will create a simple Prism based WPF application, so will demonstrate creating the project template itself along with adding nuget packages etc. and can be used as a starting point for other, more complex Prism based applications.
- Create New Project
- Extensibility | VSIX Project
- Delete index.html and stylesheet.css – no use for our project
- Add New Project | Extensibility | C# Project Template
- Double click source.extension.vsixmanifest
- Select Assets tab
- New, Type Microsoft.VisualStudio.ProjectTemplate
- “Source” set to A project in current solution
- “Project” set to the added C# project template
- Remove Class1.cs and remove the following line from the vstemplate in the project
<ProjectItem ReplaceParameters="true" OpenInEditor="true">Class1.cs</ProjectItem>
- From ProjectTemplate.csproj also remove the line
<Compile Include="Class1.cs" />
Now we have an empty class library template (in every sense).
Creating a very basic WPF Project template
- In ProjectTemplate.csproj change OutputType from Library to WinExe
- In ProjectTemplate.csproj in the ItemGroup with Reference elements add
<Reference Include="System.Xaml"> <RequiredTargetFramework>4.0</RequiredTargetFramework> </Reference> <Reference Include="WindowsBase" /> <Reference Include="PresentationCore" /> <Reference Include="PresentationFramework" />
- To the Project template project add new item WPF | User Control(WPF) name it App.xaml
- Replace the XAML with
<Application x:Class="$safeprojectname$.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Application.Resources> </Application.Resources> </Application>
- Change App.xaml.cs to
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace $safeprojectname$ { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); } } }
- For both files, show the properties (F4) window and set Build Action to None or the compiler will attempt to compile the code
- Inthe project’s vstemplate add the following within the project element
<ProjectItem ReplaceParameters="true" OpenInEditor="true">App.xaml</ProjectItem> <ProjectItem ReplaceParameters="true" OpenInEditor="true">App.xaml.cs</ProjectItem>
- In the csproj file add the following to the ItemGroup with the line
>Compile Include=”Properties\AssemblyInfo.cs” /<<Compile Include="App.xaml.cs"> <DependentUpon>App.xaml</DependentUpon> <SubType>Code</SubType> </Compile>
- Create a new ItemGroup in the csproject with the following
<ItemGroup> <ApplicationDefinition Include="App.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </ApplicationDefinition> </ItemGroup>
Adding Prism
At this point if you try to run this template it should create a valid project but it will not run as there’s no Main entry point. This was on purpose as we don’t need the StartupUri set in the App.xaml.
We’re going to need to load up nuget packages for prism
- Add the following after the </TemplateContent>
<WizardExtension> <Assembly>NuGet.VisualStudio.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly> <FullClassName>NuGet.VisualStudio.TemplateWizard</FullClassName> </WizardExtension> <WizardData> <packages repository="extension" repositoryId="productId"> <package id="Prism.Core" version="6.3.0" /> <package id="Prism.Wpf" version="6.3.0" /> <package id="Prism.Unity" version="6.3.0" /> <package id="Unity" version="4.0.1" /> <package id="CommonServiceLocator" version="1.3.0" /> </packages> </WizardData>
Replacing the productId with the Product ID from your vsixmanifest
- In your vsix add a folder named Packages
- Goto https://www.nuget.org/packages/Prism.Core/ and press download to grab Prism.Core
- As above for https://www.nuget.org/packages/Prism.Wpf/, https://www.nuget.org/packages/CommonServiceLocator/ and https://www.nuget.org/packages/Unity/
- Copy/save the nupkg to your new Packages folder
- Back in Visual Studio select show all files then right mouse click on the nupkg files and select include in project
- Set the build action to Content in the Properties (F4) view on each file and Copy to output to Copy Always, also set Include in VSIX to True
- Add the following to the Assets section in the vsixmanifest
<Asset Type="prism.core.6.3.0.nupkg" d:Source="File" Path="Packages\prism.core.6.3.0.nupkg" d:VsixSubPath="Packages" /> <Asset Type="prism.unity.6.3.0.nupkg" d:Source="File" Path="Packages\prism.unity.6.3.0.nupkg" d:VsixSubPath="Packages" /> <Asset Type="prism.wpf.6.3.0.nupkg" d:Source="File" Path="Packages\prism.wpf.6.3.0.nupkg" d:VsixSubPath="Packages" /> <Asset Type="unity.4.0.1.nupkg" d:Source="File" Path="Packages\unity.4.0.1.nupkg" d:VsixSubPath="Packages" /> <Asset Type="commonservicelocator.1.3.0.nupkg" d:Source="File" Path="Packages\commonservicelocator.1.3.0.nupkg" d:VsixSubPath="Packages" />
Add the Boostrapper
For Prism to work we need to add the boostrapper so in your project template add a new CS file named ShellBootstrapper.cs and as we’re using Unity here, it should look like this
using Microsoft.Practices.Unity; using Prism.Unity; using System.Windows; namespace $safeprojectname$ { class ShellBootstrapper : UnityBootstrapper { protected override DependencyObject CreateShell() { return Container.Resolve<MainWindow>(); } protected override void InitializeShell() { Application.Current.MainWindow.Show(); } } }
set the Build Action in the properties (F4) window to None. In the csproj add under the other Compile entry
<Compile Include="ShellBootstrapper.cs" />
We now need a MainWindow, so add a UsecControl.xaml named MainWindow.xaml, set Build Action to None and changed the XAML from UserControl to Window and the same in the cs file, here’s the code
<Window x:Class="$safeprojectname$.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:cal="http://www.codeplex.com/prism" xmlns:local="clr-namespace:$safeprojectname$" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <ItemsControl cal:RegionManager.RegionName="MainRegion" /> </Window>
and the code is
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace $safeprojectname$.App { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } }
add the following in after the last ProjectItem in the vstemplate
<ProjectItem ReplaceParameters="true" OpenInEditor="true">MainWindow.xaml</ProjectItem> <ProjectItem ReplaceParameters="true" OpenInEditor="true">MainWindow.xaml.cs</ProjectItem> <ProjectItem ReplaceParameters="true" OpenInEditor="true">ShellBootstrapper.cs</ProjectItem>
Also add to csproj
<Compile Include="MainWindow.xaml.cs"> <DependentUpon>MainWindow.xaml</DependentUpon> </Compile>
within the ItemGroup in the template’s csproj that has the None Include App.xaml place
<Page Include="MainWindow.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </Page>
In the OnStartup of App.xaml.cs add
var bootstrapper = new ShellBootstrapper(); bootstrapper.Run();
If you now run the VSIX from Visual Studio, it should load up the Visual Studio Experimental instance and you should be able to create a project from the template which will build and run successfully.