Use powershell, and powershell only, for Stylet.Start

NuGet's 'content' approach broke horribly when we tried to upgrade:
it removed App.xaml.cs.

Using powershell through and through is more powerful: we can have a
package which immediately uninstalls itself, which means it doesn't
remain part of the user's package set. We can also be smarter about
upgrading the project: we don't nuke anything that's in App.xaml, we
don't need the user to manually delete MainWindow.xaml, and we can keep
anything that's currently in MainWindow.xaml
This commit is contained in:
Antony Male 2016-04-03 19:52:21 +01:00
parent 16a61db659
commit a03803658c
2 changed files with 245 additions and 0 deletions

1
.gitattributes vendored
View File

@ -1,2 +1,3 @@
*.cs eol=crlf
*.xaml eol=crlf
*.ps1 eol=crlf

View File

@ -0,0 +1,244 @@
param($installPath, $toolsPath, $package, $project)
# Testing: call with Invoke-Expression "path\to\install.ps1"
# $project = Get-Project
$rootNamespace = $project.Properties.Item("RootNamespace").Value
$rootPath = $project.Properties.Item("LocalPath").Value
# Modify App.xaml
$appXamlPath = [System.IO.Path]::Combine($rootPath, "App.xaml")
if (![System.IO.File]::Exists($appXamlPath))
{
Write-Host "WARNING: Not modifying App.xaml as it doesn't exist"
}
else
{
$doc = [System.Xml.Linq.XDocument]::Load($appXamlPath)
$styletNs = [System.Xml.Linq.XNamespace]::Get("https://github.com/canton7/Stylet")
$ns = $doc.Root.GetDefaultNamespace()
$localNs = $doc.Root.GetNamespaceOfPrefix("local")
$startupUri = $doc.Root.Attribute("StartupUri")
if ($startupUri -ne $null)
{
$startupUri.Remove()
}
$existingApplicationLoader = $doc.Root.Descendants($styletNs.GetName("ApplicationLoader")) | Select -First 1
if ($existingApplicationLoader -ne $null)
{
Write-Host "Not modifying App.xaml as it already has an <s:ApplicationLoader> element"
}
else
{
if ($doc.Root.Attribute([System.Xml.Linq.XNamespace]::Xmlns.GetName("s")) -eq $null)
{
$doc.Root.Add((New-Object System.Xml.Linq.XAttribute([System.Xml.Linq.XNamespace]::Xmlns.GetName("s"), $styletNs)))
}
$resources = $doc.Root.Element($ns.GetName("Application.Resources"))
$existingResources = @($resources.Nodes())
$bootstrapper = New-Object System.Xml.Linq.XElement($localNs.GetName("Bootstrapper"))
$bootstrapperProperty = New-Object System.Xml.Linq.XElement($styletNs.GetName("ApplicationLoader.Bootstrapper"), $bootstrapper)
$applicationLoader = New-Object System.Xml.Linq.XElement($styletNs.GetName("ApplicationLoader"), $bootstrapperProperty)
if ($existingResources.Count -gt 0)
{
$mergedDictionaries = New-Object System.Xml.Linq.XElement($ns.GetName("ResourceDictionary.MergedDictionaries"), $applicationLoader)
$resourceDictionary = New-Object System.Xml.Linq.XElement($ns.GetName("ResourceDictionary"), $mergedDictionaries, $existingResources)
$resources.ReplaceNodes($resourceDictionary)
}
else
{
$resources.Add($applicationLoader)
}
$settings = New-Object System.Xml.XmlWriterSettings
$settings.Indent = $True
$settings.NewLineOnAttributes = $True
$settings.OmitXmlDeclaration = $True
$settings.IndentChars = " "
$stringWriter = New-Object System.IO.StringWriter
$xmlWriter = [System.Xml.XmlWriter]::Create($stringWriter, $settings)
$doc.WriteTo($xmlWriter)
$xmlWriter.Close()
$appXaml = $stringWriter.ToString();
$stringWriter.Close()
# Manually fudge the newlines after xmlns declarations, since XmlWriter won't do them itself
$appXaml = [System.Text.RegularExpressions.Regex]::Replace($appXaml, "Application\s+x:Class=", "Application x:Class=")
$appXaml = $appXaml.Replace(" xmlns=", "`r`n xmlns=")
$appXaml = $appXaml.Replace(" xmlns:", "`r`n xmlns:")
[System.IO.File]::WriteAllText($appXamlPath, $appXaml)
}
}
# Add bootstrapper
# Only do this if Bootstrapper.cs doesn't already exist...
$existingBootstrapper = $project.ProjectItems | Where { $_.Name -eq "Bootstrapper.cs" } | Select -First 1
if ($existingBootstrapper -ne $null)
{
Write-Host "Not creating Bootstrapper.cs as it already exists"
}
else
{
$bootstrapperContent = "using System;
using Stylet;
using StyletIoC;
using ${rootNamespace}.Pages;
namespace ${rootNamespace}
{
public class Bootstrapper : Bootstrapper<ShellViewModel>
{
protected override void ConfigureIoC(IStyletIoCBuilder builder)
{
// Configure the IoC container in here
}
protected override void Configure()
{
// Perform any other configuration before the application starts
}
}
}
"
$bootstrapperPath = [System.IO.Path]::Combine($rootPath, "Bootstrapper.cs")
[System.IO.File]::WriteAllText($bootstrapperPath, $bootstrapperContent)
$null = $project.ProjectItems.AddFromFile($bootstrapperPath)
}
# Add Pages/ folder
$pages = $project.ProjectItems | Where { $_.Name -Eq "Pages" } | Select -First 1
if ($pages -eq $null)
{
# This also creates the folder on disk
$pages = $project.ProjectItems.AddFolder("Pages")
}
# Add Pages/ShellView.xaml
# Only do this if ShellView doesn't already exist...
$existingShellView = $pages.ProjectItems | Where { $_.Name -eq "ShellView.xaml" } | Select -First 1
if ($existingShellView -ne $null)
{
Write-Host "Not renaming MainWindow.xaml to Pages/ShellView.xaml as Pages/ShellView.xaml already exists. "
}
else
{
$mainWindow = ($project.ProjectItems | Where { $_.Name -Eq "MainWindow.xaml" } | Select -First 1)
if ($mainWindow -eq $null)
{
Write-Host "Creating Pages/ShellView.xaml from scratch, as MainWindow.xaml doesn't exist"
$shellViewContent = "<Window x:Class=""${rootNamespace}.Pages.ShellView""
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:d=""http://schemas.microsoft.com/expression/blend/2008""
xmlns:mc=""http://schemas.openxmlformats.org/markup-compatibility/2006""
xmlns:local=""clr-namespace:${rootNamespace}.Pages""
xmlns:s=""https://github.com/canton7/Stylet""
mc:Ignorable=""d""
d:DataContext=""{d:DesignInstance local:ShellViewModel}""
Title=""Stylet Start Project""
Width=""350"" Height=""200"">
<TextBlock FontSize=""30"" HorizontalAlignment=""Center"" VerticalAlignment=""Center"">
Hello Stylet!
</TextBlock>
</Window>
"
$shellViewCsContent = "using System;
using System.Windows;
namespace ${rootNamespace}.Pages
{
/// <summary>
/// Interaction logic for ShellView.xaml
/// </summary>
public partial class ShellView : Window
{
public ShellView()
{
InitializeComponent();
}
}
}
"
}
else
{
$mainWindowPath = $mainWindow.Properties.Item("FullPath").Value
$mainWindowCsPath = $mainWindowPath + ".cs"
$mainWindow.Remove()
$shellViewContent = [System.IO.File]::ReadAllText($mainWindowPath)
$shellViewContent = $shellViewContent.Replace($rootNamespace + ".MainWindow", $rootNamespace + ".Pages.ShellView")
$shellViewContent = $shellViewContent.Replace('xmlns:local="clr-namespace:' + $rootNamespace + '"', 'xmlns:local="clr-namespace:' + $rootNamespace + '.Pages"')
$shellViewContent = $shellViewContent.Replace('Title="MainWindow"', 'Title="Stylet Start Project"')
$shellViewContent = $shellViewContent.Replace('mc:Ignorable="d"', "mc:Ignorable=""d""
d:DataContext=""{d:DesignInstance local:ShellViewModel}""")
$shellViewContent = [System.Text.RegularExpressions.Regex]::Replace($shellViewContent, "<Grid>\s*</Grid>", "<TextBlock FontSize=""30"" HorizontalAlignment=""Center"" VerticalAlignment=""Center"">
Hello Stylet!
</TextBlock>")
$shellViewCsContent = [System.IO.File]::ReadAllText($mainWindowCsPath)
$shellViewCsContent = $shellViewCsContent.Replace("namespace " + $rootNamespace, "namespace " + $rootNamespace + ".Pages")
$shellViewCsContent = $shellViewCsContent.Replace("class MainWindow", "class ShellView")
$shellViewCsContent = $shellViewCsContent.Replace("public MainWindow()", "public ShellView()")
$shellViewCsContent = $shellViewCsContent.Replace("/// Interaction logic for MainWindow.xaml", "/// Interaction logic for ShellView.xaml")
[System.IO.File]::Delete($mainWindowPath)
[System.IO.File]::Delete($mainWindowCsPath)
}
$shellViewPath = [System.IO.Path]::Combine($rootPath, "Pages", "ShellView.xaml")
$shellViewCsPath = $shellViewPath + ".cs"
[System.IO.File]::WriteAllText($shellViewPath, $shellViewContent)
[System.IO.File]::WriteAllText($shellViewCsPath, $shellViewCsContent)
$shellView = $pages.ProjectItems.AddFromFile($shellViewPath)
# This should have been added automagically, but just in case...
$null = $shellView.ProjectItems.AddFromFile($shellViewCsPath)
}
# Add Pages/ShellViewModel.cs
# Only do this if ShellViewModel doesn't already exist
$existingShellViewModel = $pages.ProjectItems | Where { $_.Name -eq "ShellViewModel.cs" } | Select -First 1
if ($existingShellViewModel -ne $null)
{
Write-Host "Not creating Pages/ShellViewModel.cs as it already exists"
}
else
{
$shellViewModelContent = "using System;
using Stylet;
namespace ${rootNamespace}.Pages
{
public class ShellViewModel : Screen
{
}
}
"
$shellViewModelPath = [System.IO.Path]::Combine($rootPath, "Pages", "ShellViewModel.cs")
[System.IO.File]::WriteAllText($shellViewModelPath, $shellViewModelContent)
$null = $pages.ProjectItems.AddFromFile($shellViewModelPath)
}
Uninstall-Package Stylet.Start