The Boy Scout Rule It’s not enough to write the code well. The code has to be kept clean over time. We’ve all seen code rot and degrade as time passes. So we must take an active role in preventing this degradation. The Boy Scouts of America have a simple rule that we can apply to our profession. 'Leave the campground cleaner than you found it'
Last active
March 18, 2021 07:59
-
-
Save mouadcherkaoui/7b0f32d9dbefa71102acdbb07299c9bb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using namespace System.Windows | |
using namespace System.Collections.ObjectModel | |
Add-Type -AssemblyName presentationframework, presentationcore | |
Add-Type -AssemblyName System.Windows | |
Add-Type -AssemblyName System.Collections | |
function Get-WPFObject { | |
Param( | |
[string] $inputXML, | |
[string] $XamlPath) | |
# Load a WPF GUI from a XAML file build with Visual Studio | |
$wpf = @{ } | |
# NOTE: Either load from a XAML file or paste the XAML file content in a "Here String" | |
if([string]::IsNullOrEmpty($inputXML)){ | |
$inputXML = Get-Content -Path $XamlPath | |
#".\WPFGUIinTenLines\MainWindow.xaml" | |
} | |
[xml]$xaml = Get-CleanXML -RawInput $inputXML | |
$NamedNodes = Get-XamlNamedNodes -Xml $xaml | |
$reader = ([System.Xml.XmlNodeReader]::new($xaml)) | |
$form = [Windows.Markup.XamlReader]::Load($reader) | |
$NamedNodes | ForEach-Object {$wpf.Add($_.Name, $form.FindName($_.Name))} | |
#Get the form name to be used as parameter in functions external to form... | |
$rootName = $NamedNodes[0].Name | |
return $wpf | |
} | |
function Get-CleanXML { | |
Param([string]$RawInput) | |
return $($RawInput -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace 'x:Class=".*?"','' -replace 'd:DesignHeight="\d*?"','' -replace 'd:DesignWidth="\d*?"','') | |
} | |
function Get-XamlNamedNodes { | |
Param([xml]$Xml) | |
$namedNodes = $Xml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | |
return $namedNodes | |
} | |
class Relay: System.Windows.Input.ICommand { | |
hidden [Action[object]]$command; | |
Relay($Command){ | |
$this.command = $Command; | |
#$Command.Invoke("") | |
} | |
[void]add_CanExecuteChanged ([System.EventHandler]$handler){} | |
[void]remove_CanExecuteChanged ([System.EventHandler]$handler){} | |
[bool]CanExecute([object]$arg) {return $true; } | |
[void]Execute ([object]$arg){ $this.command?.Invoke($arg); } | |
} | |
class ShowMessageCommand : System.Windows.Input.ICommand { | |
[string]$Message | |
ShowMessageCommand([string]$message) { | |
$this.Message = $message | |
} | |
[void]add_CanExecuteChanged ([System.EventHandler]$handler){} | |
[void]remove_CanExecuteChanged ([System.EventHandler]$handler){} | |
[bool]CanExecute([object]$arg) {return $true; } | |
[void]Execute ([object]$arg){ Write-Host $this.Message; } | |
} | |
class MainWindowVM { | |
[string]$Title = "test"; | |
[string]$Content = "content"; | |
[string]$Uri; | |
[string]$buttonContent = "click!" | |
[object]$Item = @{Title=$Title; Content=$Content} | |
[ObservableCollection[object]]$Items | |
hidden [Relay]$relay = [Relay]::new({ param($p) Show-MessageBox "fired"}) | |
MainWindowVM(){ | |
$this.Items = [ObservableCollection[object]]::new() | |
$this.relay.Command = { param($p) Show-MessageBox "fired"} | |
$this.ShowCommand = $this.relay | |
} | |
[System.Windows.Input.ICommand]$ShowCommand; | |
#[ShowMessageCommand]::new("test command") | |
} | |
function Show-MessageBox{ | |
Param([string]$message) | |
[System.Windows.MessageBox]::Show("$message") | |
} | |
$inputXml = '<Window x:Class="WpfApp1.MainWindow" | |
x:Name="MainWindow" | |
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:WpfApp1" | |
mc:Ignorable="d" | |
Title="MainWindow" Height="450" Width="800"> | |
<Window.Resources> | |
<Style TargetType="Button"> | |
<Setter Property="Background" Value="Gray" /> | |
<Setter Property="Foreground" Value="DarkGray"/> | |
</Style> | |
</Window.Resources> | |
<Grid> | |
<Grid.RowDefinitions> | |
<RowDefinition Height="*"/> | |
<RowDefinition Height="5*"/> | |
<RowDefinition Height="*"/> | |
</Grid.RowDefinitions> | |
<Grid.ColumnDefinitions> | |
<ColumnDefinition Width="*"/> | |
<ColumnDefinition Width="5*"/> | |
<ColumnDefinition Width="*"/> | |
</Grid.ColumnDefinitions> | |
<StackPanel Grid.Row="1" Grid.Column="1"> | |
<TextBox x:Name="_Title" Text="{Binding Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox> | |
<TextBlock x:Name="Title" Text="{Binding Title, Mode=TwoWay}"></TextBlock> | |
<Button x:Name="button1" | |
Content="{Binding buttonContent}" Command="{Binding ShowCommand}" CommandParameter="{Binding Item}"></Button> | |
</StackPanel> | |
</Grid> | |
</Window> | |
'; | |
#Define events functions | |
#region Load, Draw (render) and closing form events | |
#Things to load when the WPF form is loaded aka in memory | |
$wpf = $(Get-WPFObject -inputXML $inputXml) | |
$wpf.MainWindow.DataContext = [MainWindowVM]::new() | |
$wpf.button1.Add_Click({ | |
#$wpf.MainWindow.Close() | |
}); | |
$wpf.MainWindow.Add_Loaded({ | |
#Update-Cmd | |
}) | |
#Things to load when the WPF form is rendered aka drawn on screen | |
$wpf.MainWindow.Add_ContentRendered({ | |
#Update-Cmd | |
}) | |
$wpf.MainWindow.add_Closing({ | |
$msg = "bye bye !" | |
write-host $msg | |
}) | |
#endregion Load, Draw and closing form events | |
#End of load, draw and closing form events | |
#HINT: to update progress bar and/or label during WPF Form treatment, add the following: | |
# ... to re-draw the form and then show updated controls in realtime ... | |
$wpf.MainWindow.Dispatcher.Invoke("Render",[action][scriptblock]{}) | |
# Load the form: | |
# Older way >>>>> $wpf.MyFormName.ShowDialog() | Out-Null >>>>> generates crash if run multiple times | |
# Newer way >>>>> avoiding crashes after a couple of launches in PowerShell... | |
# USing method from https://gist.github.com/altrive/6227237 to avoid crashing Powershell after we re-run the script after some inactivity time or if we run it several times consecutively... | |
$async = $wpf.MainWindow.Dispatcher.InvokeAsync({ | |
$wpf.MainWindow.ShowDialog() | Out-Null | |
}) | |
$async.Wait() | Out-Null |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment