Need advice on adding a text in let's say Text_Status after hitting a button.
How can I also automatically continue the command after a restart to let's say Button_clearTPM, then going to Button_enableTPM, then going to Button_initializeTPM?
Here is my XAML:
[xml]$xaml = #'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TPM Script" Height="482" Width="479" Background="White">
<Grid Height="375" Width="382">
<Button Content="Clear TPM" Height="55" HorizontalAlignment="Left" Margin="230,30,0,0" Name="Button_clearTPM" VerticalAlignment="Top" Width="140"/>
<Button Content="Enable TPM" Height="55" HorizontalAlignment="Left" Margin="230,0,0,220" Name="Button_enableTPM" VerticalAlignment="Bottom" Width="140"/>
<Button Content="Initialize TPM" Height="55" HorizontalAlignment="Left" Margin="230,169,0,0" Name="Button_initializeTPM" VerticalAlignment="Top" Width="140"/>
<Label Content="Enter Workstation ID: " Height="23" HorizontalAlignment="Left" Margin="31,45,0,0" Name="Label_1" VerticalAlignment="Top" Width="133"/>
<TextBox Height="38" HorizontalAlignment="Left" Margin="31,74,0,0" Name="Text_WSID" VerticalAlignment="Top" Width="171" />
<TextBox Height="114" HorizontalAlignment="Left" Margin="31,241,0,0" Name="Text_Status" VerticalAlignment="Top" Width="329" />
</Grid>
</Window>
'#
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
try
{
$Form=[Windows.Markup.XamlReader]::Load( $reader )
}
catch
{
Write-Host "Unable to load Windows.Markup.XamlReader. Some possible causes for this problem include: .NET Framework is missing PowerShell must be launched with PowerShell -sta, invalid XAML code was encountered."t
}
cls
#First Button
$clearTPM = $Form.FindName('Button_clearTPM')
$clearTPM.Add_Click({ Write-Host "Clear TPM clicked" -ForegroundColor Cyan})
#Second Button
$enableTPM = $Form.FindName('Button_enableTPM')
$enableTPM.Add_Click({ Write-Host "Enable TPM clicked" -ForegroundColor Cyan})
#Third Button
$initializeTPM = $Form.FindName('Button_initializeTPM')
$initializeTPM.Add_Click({ Write-Host "Initialize TPM clicked" -ForegroundColor Cyan})
$Form.ShowDialog() | out-null
##Possible Commands
#Clear-Tpm
#Enable-TpmAutoProvisioning (Export C:Notbackedup)
#Initialize-Tpm
The problem is your script doesn't know what your label is.
So you have to make it accessible by making it into a variable.
Then, you can assign whatever text you want via .Content.
So :
[xml]$xaml = #'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TPM Script" Height="482" Width="479" Background="White">
<Grid Height="375" Width="382">
<Button Content="Clear TPM" Height="55" HorizontalAlignment="Left" Margin="230,30,0,0" Name="Button_clearTPM" VerticalAlignment="Top" Width="140"/>
<Button Content="Enable TPM" Height="55" HorizontalAlignment="Left" Margin="230,0,0,220" Name="Button_enableTPM" VerticalAlignment="Bottom" Width="140"/>
<Button Content="Initialize TPM" Height="55" HorizontalAlignment="Left" Margin="230,169,0,0" Name="Button_initializeTPM" VerticalAlignment="Top" Width="140"/>
<Label Content="Enter Workstation ID: " Height="23" HorizontalAlignment="Left" Margin="31,45,0,0" Name="Label_1" VerticalAlignment="Top" Width="133"/>
<TextBox Height="38" HorizontalAlignment="Left" Margin="31,74,0,0" Name="Text_WSID" VerticalAlignment="Top" Width="171" />
<TextBox Height="114" HorizontalAlignment="Left" Margin="31,241,0,0" Name="Text_Status" VerticalAlignment="Top" Width="329" />
</Grid>
</Window>
'#
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
try
{
$Form=[Windows.Markup.XamlReader]::Load( $reader )
}
catch
{
Write-Host "Unable to load Windows.Markup.XamlReader. Some possible causes for this problem include: .NET Framework is missing PowerShell must be launched with PowerShell -sta, invalid XAML code was encountered."t
}
## THIS IS WHERE MAGIC HAPPENS
$xaml.SelectNodes("//*[#Name]") | %{Set-Variable -Name "$($_.Name)" -Value $Form.FindName($_.Name)} # find all names and make them accessible via a variable
#First Button
$clearTPM = $Form.FindName('Button_clearTPM')
$clearTPM.Add_Click({
Write-Host "Clear TPM clicked" -ForegroundColor Cyan
$label_1.content = "Clear TPM clicked"
})
#Second Button
$enableTPM = $Form.FindName('Button_enableTPM')
$enableTPM.Add_Click({ Write-Host "Enable TPM clicked" -ForegroundColor Cyan})
#Third Button
$initializeTPM = $Form.FindName('Button_initializeTPM')
$initializeTPM.Add_Click({ Write-Host "Initialize TPM clicked" -ForegroundColor Cyan})
$Form.ShowDialog() | out-null
##Possible Commands
#Clear-Tpm
#Enable-TpmAutoProvisioning (Export C:Notbackedup)
#Initialize-Tpm
Related
I'm trying to make a remote copy tool, which works fine in the console but I'm having trouble creating the GUI for it.
When I populate my Listbox with the list of files to copy they just don't display as intended...
My problem is in the ChooseFiles function
#xaml Code
$inputXML = #"
<Window x:Class="RemoteCopy.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:RemoteCopy"
mc:Ignorable="d"
Title="Remote Copy" Height="556.841" Width="800">
<Grid>
<Label Content="Remote Computer's IP:" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="RemoteIPTextbox" HorizontalAlignment="Left" Height="20" Margin="238,16,0,0" TextWrapping="Wrap" Text="IP address" VerticalAlignment="Top" Width="128"/>
<Label Content="Username: " HorizontalAlignment="Left" Height="26" Margin="10,40,0,0" VerticalAlignment="Top" Width="131"/>
<Label Content="Password: " HorizontalAlignment="Left" Height="26" Margin="10,70,0,0" VerticalAlignment="Top" Width="131"/>
<TextBox x:Name="UsernameTextbox" HorizontalAlignment="Left" Height="20" Margin="238,46,0,0" TextWrapping="Wrap" Text="Username" VerticalAlignment="Top" Width="128"/>
<Button x:Name="CheckConnectionButton" Content="Check Connection" HorizontalAlignment="Left" Margin="389,46,0,0" VerticalAlignment="Top" Width="107" Height="20"/>
<Button x:Name="ChooseFilesButton" Content="Choose Files" HorizontalAlignment="Left" Height="80" Margin="535,16,0,0" VerticalAlignment="Top" Width="122"/>
<ListView x:Name="FilesListView" HorizontalAlignment="Left" Height="234" Margin="33,128,0,0" VerticalAlignment="Top" Width="720">
<ListView.View>
<GridView>
<GridViewColumn Header="File"/>
<GridViewColumn Header="Size"/>
</GridView>
</ListView.View>
</ListView>
<Button x:Name="StartCopyButton" Content="Start Copy" HorizontalAlignment="Left" Height="24" Margin="33,374,0,0" VerticalAlignment="Top" Width="720"/>
<ProgressBar x:Name="ProgressBar" HorizontalAlignment="Left" Height="20" Margin="113,418,0,0" VerticalAlignment="Top" Width="572"/>
<Label Content="Progress:" HorizontalAlignment="Left" Height="28" Margin="33,418,0,0" VerticalAlignment="Top" Width="67"/>
<Label Content="100%" HorizontalAlignment="Left" Height="28" Margin="714,418,0,0" VerticalAlignment="Top" Width="39"/>
<PasswordBox x:Name="Passwordbox" HorizontalAlignment="Left" Margin="238,78,0,0" VerticalAlignment="Top" Width="128"/>
<Button x:Name="ExitButton" Content="Exit" HorizontalAlignment="Left" Height="21" Margin="283,455,0,0" VerticalAlignment="Top" Width="213"/>
</Grid>
</Window>
"#
$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = $inputXML
#Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
try{
$Form=[Windows.Markup.XamlReader]::Load( $reader )
}
catch{
Write-Warning "Unable to parse XML, with error: $($Error[0])`n Ensure that there are NO SelectionChanged or TextChanged properties in your textboxes (PowerShell cannot process them)"
throw
}
#===========================================================================
# Load XAML Objects In PowerShell
#===========================================================================
$xaml.SelectNodes("//*[#Name]") | %{"trying item $($_.Name)";
try {Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name) -ErrorAction Stop}
catch{throw}
}
Function Get-FormVariables{
if ($global:ReadmeDisplay -ne $true){Write-host "If you need to reference this display again, run Get-FormVariables" -ForegroundColor Yellow;$global:ReadmeDisplay=$true}
write-host "Found the following interactable elements from our form" -ForegroundColor Cyan
get-variable WPF*
}
Get-FormVariables
#===========================================================================
# Use this space to add code to the various form elements in your GUI
#===========================================================================
#my test folder
c:
Set-Location c:\temp
function DisplayInBytes($num)
#reference: https://stackoverflow.com/questions/24616806/powershell-display-files-size-as-kb-mb-or-gb
{
$suffix = "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
$index = 0
while ($num -gt 1kb)
{
$num = $num / 1kb
$index++
}
"{0:N1} {1}" -f $num, $suffix[$index]
}
function ChooseFiles
{
$WPFFilesListView.Clear()
$filesToCopy = Get-Files
$filesToCopy.FileNames | ForEach-Object {
$currentfilesize = (Get-Item $_).Length
$currentfilesize = DisplayInBytes -num $currentfilesize
$row = New-Object System.Windows.Forms.ListViewItem($_)
[void]$row.SubItems.Add("$currentfilesize")
[void]$WPFFilesListView.Items.Add($row)
}
}
Function Get-Files($initialDirectory="")
#Reference to https://stackoverflow.com/questions/15885132/file-folder-chooser-dialog-from-a-windows-batch-script
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")|Out-Null
$myFile = new-object Windows.Forms.OpenFileDialog
$myFile.InitialDirectory = Get-Location
$myFile.Filter = "Text Files (*.txt)|*.txt|Powershell Script Files (*.ps1)|*.ps1|Batch Files (*.bat)|*.bat|All Files (*.*)|*.*"
$myFile.ShowHelp = $true
$myFile.Multiselect = $true
[void]$myfile.ShowDialog()
if ($myFile.Multiselect) { $myFile.FileNames } else { $myFile.FileName }
return $myFile
}
$WPFExitButton.Add_Click({$form.close()})
$WPFChooseFilesButton.Add_Click({ChooseFiles})
$Form.ShowDialog() | out-null
The Function to Choose my Files and Populate my Listbox is this one:
function ChooseFiles
{
$WPFFilesListView.Clear()
$filesToCopy = Get-Files
$filesToCopy.FileNames | ForEach-Object {
$currentfilesize = (Get-Item $_).Length
$currentfilesize = DisplayInBytes -num $currentfilesize
$row = New-Object System.Windows.Forms.ListViewItem($_)
[void]$row.SubItems.Add("$currentfilesize")
[void]$WPFFilesListView.Items.Add($row)
}
}
But my Result looks like this:
I intended to have the Filename and Filesize displayed,
If I test the Variables they work
Write-host $_
write-host $currentfilesize
One in each column
You need to change two things to fix the display:
Add a DisplayMemberBinding to the ListView
<ListView x:Name="FilesListView" HorizontalAlignment="Left" Height="234" Margin="33,128,0,0" VerticalAlignment="Top" Width="720">
<ListView.View>
<GridView>
<GridViewColumn Header="File" DisplayMemberBinding="{Binding File}"/>
<GridViewColumn Header="Size" DisplayMemberBinding="{Binding Size}"/>
</GridView>
</ListView.View>
</ListView>
Pass a pscustomobject with the specified binding properties to the ListView
function ChooseFiles
{
$WPFFilesListView.Clear()
$filesToCopy = Get-Files
$filesToCopy.FileNames | ForEach-Object {
$currentfilesize = (Get-Item $_).Length
$currentfilesize = DisplayInBytes -num $currentfilesize
$WPFFilesListView.Items.Add([pscustomobject]#{File=$_;Size=$currentfilesize})
}
}
Im trying to make a powershellscript using XAML Objects, that can be used to configure AD-Users.
My goal right now is to add a live search for a specific textbox. The script uses the input from the textbox, compares it to AD-Users and displays the output in a listbox. What I need is an eventhandler, that activates when I click any Item in the listbox
The last part is the relevant piece.
I'm grateful for any help.
$inputXML = #"
<Window x:Class="WpfApplication3.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:WpfApplication3"
mc:Ignorable="d"
Title="HHI Helpdesk Helper" Height="350" Width="654.604" Icon="C:\Users\hoesl\Pictures\logo.png" WindowStartupLocation="CenterScreen">
<Grid Background="#FFA0A0A0">
<TabControl x:Name="tabControl" HorizontalAlignment="Left" Height="301" Margin="10,10,0,0" VerticalAlignment="Top" Width="628">
<TabItem Header="Enable Admin Account">
<Grid Background="#FFE5E5E5">
<Label x:Name="label" Content="User : " HorizontalAlignment="Left" Margin="47,10,0,0" VerticalAlignment="Top"/>
<Label x:Name="label1" Content="Service-Tag : " HorizontalAlignment="Left" Margin="10,53,0,0" VerticalAlignment="Top"/>
<Label x:Name="label2" Content="Set Password : " HorizontalAlignment="Left" Margin="2,95,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="textBox1" HorizontalAlignment="Left" Height="23" Margin="95,57,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="textBox2" HorizontalAlignment="Left" Height="23" Margin="95,99,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<Calendar HorizontalAlignment="Left" Margin="434,10,0,0" VerticalAlignment="Top"/>
<TextBlock x:Name="textBlock" HorizontalAlignment="Left" Margin="10,127,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Height="116" Width="410">
<TextBlock.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="#FFA3A3A3"/>
</LinearGradientBrush>
</TextBlock.Background>
</TextBlock>
<TextBlock x:Name="textBlock1" HorizontalAlignment="Left" Margin="434,182,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" RenderTransformOrigin="0.482,0.542" Height="27" Width="178" OpacityMask="White">
<TextBlock.Background>
<RadialGradientBrush>
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="#FFE0DFDF"/>
</RadialGradientBrush>
</TextBlock.Background>
</TextBlock>
<Label x:Name="label3" Content="Valid Until : " HorizontalAlignment="Left" Margin="346,11,0,0" VerticalAlignment="Top"/>
<CheckBox x:Name="checkBox" Content="Keep old password" HorizontalAlignment="Left" Margin="220,108,0,0" VerticalAlignment="Top"/>
<Button x:Name="button" Content="Apply" HorizontalAlignment="Left" Margin="435,215,0,0" VerticalAlignment="Top" Width="178" Height="29" BorderBrush="White" Background="#FFA6A5A5"/>
<ListBox x:Name="listBox" HorizontalAlignment="Left" Height="125" Margin="95,32,0,-0.2" VerticalAlignment="Top" Width="120" Visibility="Hidden" IsSynchronizedWithCurrentItem="True"/>
<TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="95,14,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" />
</Grid>
</TabItem>
<TabItem Header="Re-Activate Admin Account">
<Grid Background="#FFE5E5E5">
<Label x:Name="label4" Content="User : " HorizontalAlignment="Left" Margin="85,10,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="textBox3" HorizontalAlignment="Left" Height="23" Margin="133,13,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<Calendar HorizontalAlignment="Left" Margin="420,10,0,0" VerticalAlignment="Top" Height="168" Width="188"/>
<Button x:Name="button1" Content="Button" HorizontalAlignment="Left" Margin="420,214,0,0" VerticalAlignment="Top" Width="193" Height="29"/>
</Grid>
</TabItem>
<TabItem Content="New User" Header="New User"/>
<TabItem Header="Unlock User"/>
<TabItem Header="New Extern User">
<Grid Background="#FFE5E5E5">
<TextBox x:Name="textBox4" HorizontalAlignment="Left" Height="23" Margin="111,14,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="textBox5" HorizontalAlignment="Left" Height="23" Margin="111,49,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="textBox6" HorizontalAlignment="Left" Height="23" Margin="111,88,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="textBox7" HorizontalAlignment="Left" Height="23" Margin="111,127,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="textBox8" HorizontalAlignment="Left" Height="23" Margin="111,167,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<Label x:Name="label5" Content="Firstname :" HorizontalAlignment="Left" Margin="36,10,0,0" VerticalAlignment="Top"/>
<Label x:Name="label6" Content="Lastname :" HorizontalAlignment="Left" Margin="36,45,0,0" VerticalAlignment="Top"/>
<Label x:Name="label7" Content="E-Mail-Address :" HorizontalAlignment="Left" Margin="6,85,0,0" VerticalAlignment="Top"/>
<Label x:Name="label8" Content="Manager :" HorizontalAlignment="Left" Margin="39,124,0,0" VerticalAlignment="Top"/>
<Label x:Name="label9" Content="Ticketnumber :" HorizontalAlignment="Left" Margin="15,163,0,0" VerticalAlignment="Top"/>
<TextBlock x:Name="textBlock2" HorizontalAlignment="Left" Margin="295,14,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="175" Width="318" Background="#FF323131" Foreground="White" Cursor="Wait"/>
<Button x:Name="button2" Content="Button" HorizontalAlignment="Left" Margin="503,212,0,0" VerticalAlignment="Top" Width="110" Height="31"/>
</Grid>
</TabItem>
</TabControl>
</Grid>
</Window>
"#
$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = $inputXML
#Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
try{
$Form=[Windows.Markup.XamlReader]::Load( $reader )
}
catch{
Write-Warning "Unable to parse XML, with error: $($Error[0])`n Ensure that there are NO SelectionChanged or TextChanged properties in your textboxes (PowerShell cannot process them)"
throw
}
#===========================================================================
# Load XAML Objects In PowerShell
#===========================================================================
$xaml.SelectNodes("//*[#Name]") | %{"trying item $($_.Name)";
try {Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name) -ErrorAction Stop}
catch{throw}
}
Function Get-FormVariables{
if ($global:ReadmeDisplay -ne $true){Write-host "If you need to reference this display again, run Get-FormVariables" -ForegroundColor Yellow;$global:ReadmeDisplay=$true}
write-host "Found the following interactable elements from our form" -ForegroundColor Cyan
get-variable WPF*
}
Get-FormVariables
#===========================================================================
# Use this space to add code to the various form elements in your GUI
#===========================================================================
#setzt die Variablen auf bestimmte Objekte, wodurch das Verändern der Objekte sehr flexibel ist
$OutputBox = $Form.FindName('textBlock')
$InputBox = $Form.FindName('textBox')
$ListBox = $Form.FindName('listBox')
#Eventhandler
$InputBox.Add_TextChanged({
$input = $InputBox.Text
if($input.length -gt 3){
$ListBox.Visibility ="Visible"
$input = "*" + $input + "*"
$user = #(Get-ADUser -Filter ' Name -like $input ' | Select -ExpandProperty Name)
#debug $OutputBox.Text = ($user | ForEach {"{0}`r" -f $_})
$ListBox.itemsSource = $user
---> Here should be the eventhandler for clicking an item in the listbox <---
# $listuser = Get-ADUser -Filter ' Name -like $ListBox.SelectedItem '
# $OutputBox.Text = $ListBox.SelectedItem
}
})
$Form.ShowDialog()
The answer:
$ListBox.add_SelectionChanged({$OutputBox.Text = $ListBox.SelectedItem})
Personally I prefer creating special function with events and call it before $Form.ShowDialog(). Add as much events as you need. Example:
function event_handler
{
$textbox_name.add_TextChanged({$textbox_Login.Text = Construct-Login $textbox_name.Text $textbox_LastName.Text})
}
In this example when you type something to the textbox textbox_name, function Construct-Login is invoked and result is written to another textbox.
By the way, you don't need extra
$OutputBox = $Form.FindName('textBlock')
$ListBox = $Form.FindName('listBox')
since $xaml.SelectNodes does the thing for you. Just make sure you have unique names for all controls at your form.
Im a beginner and is needing help running an XAML into powershell.
Im trying to follow some format from the net but cant seem to convert my xaml GUI to powershell.
Any help will do. Heres my code:
<Window x:Class="WpfApp3.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:WpfApp3"
mc:Ignorable="d"
Title="Strike Email" Height="137" Width="274" Background="White">
<Grid Background="#FF005D80">
<Button Content="1st Strike" HorizontalAlignment="Left" Height="50" Margin="10,30,0,0" VerticalAlignment="Top" Width="70" Background="White" Click="Button_Click"/>
<Button Content="3rd Strike" HorizontalAlignment="Left" Height="50" Margin="180,30,0,0" VerticalAlignment="Top" Width="70" Background="White"/>
<Button Content="2nd Strike" HorizontalAlignment="Left" Height="50" Margin="95,30,0,0" VerticalAlignment="Top" Width="70" Background="White"/>
</Grid>
For that, you will need to:
Cast your xaml string as a xml
Load presentationframework assembly
Load the XAML into a xmlNodeReader
Load the form
Powershell must be running in STA (Single threaded) mode for this to work
(Powershell ISE launch in STA mode by default.)
[xml]$xaml = 'ValidXAMLStringHere'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Form=[Windows.Markup.XamlReader]::Load( $reader )
$Form.ShowDialog() | out-null
Something like the snippet would be the end result.
I even added a button click event for fun.
[xml]$xaml = #'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Strike Email" Height="137" Width="274" Background="White">
<Grid Background="#FF005D80">
<Button Content="1st Strike" HorizontalAlignment="Left" Height="50" Margin="10,30,0,0" VerticalAlignment="Top" Width="70" Background="White" x:Name="FirstStrike" />
<Button Content="3rd Strike" HorizontalAlignment="Left" Height="50" Margin="180,30,0,0" VerticalAlignment="Top" Width="70" Background="White"/>
<Button Content="2nd Strike" HorizontalAlignment="Left" Height="50" Margin="95,30,0,0" VerticalAlignment="Top" Width="70" Background="White"/>
</Grid>
</Window>
'#
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
try
{
$Form=[Windows.Markup.XamlReader]::Load( $reader )
}
catch
{
Write-Host "Unable to load Windows.Markup.XamlReader. Some possible causes for this problem include: .NET Framework is missing PowerShell must be launched with PowerShell -sta, invalid XAML code was encountered."t
}
cls
$Button = $Form.FindName('FirstStrike')
$Button.Add_Click({ Write-Host "First Strike Button Clicked" -ForegroundColor Cyan})
$Form.ShowDialog() | out-null
References:
Technet - Integrating XAML into PowerShell
Learn Powershell - PowerShell and WPF: Buttons
I am trying to implement background commands that would initiate after the user clicks a button, and so far I have always ended up with my UI locking up while the job is on-going. I am currently using a somewhat time consuming for loop to check if my UI locks up. What can I do to let the job work in the background as the UI is freed up. I am not sure I want to complicate the code by adding runspaces. What am I doing incorrectly?
$inputXML = #"
<Window x:Class="WpfApplication2.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:WpfApplication2"
mc:Ignorable="d"
Title="Create User" Height="448.05" Width="656.017" ResizeMode="NoResize">
<Grid Margin="0,0,-6.8,-0.8" Background="#FFD7D7D7">
<Grid.RowDefinitions>
<RowDefinition Height="403*"/>
<RowDefinition Height="18*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="Create_User" Content="Create User" HorizontalAlignment="Left" Margin="67,224,0,0" VerticalAlignment="Top" Width="300" Height="26" RenderTransformOrigin="0.551,-0.671" IsEnabled="True">
<Button.Background>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFF3F3F3" Offset="0"/>
<GradientStop Color="#FFEBEBEB" Offset="0.5"/>
<GradientStop Color="#FFDDDDDD" Offset="0.5"/>
<GradientStop Color="#FFCDCDCD" Offset="1"/>
</LinearGradientBrush>
</Button.Background>
</Button>
<Label x:Name="fname" Content="" HorizontalAlignment="Left" Margin="43,11,0,0" VerticalAlignment="Top" Height="26" Width="10"/>
<Label x:Name="fname1" Content="First Name" HorizontalAlignment="Left" Margin="88,54,0,0" VerticalAlignment="Top" Width="72" RenderTransformOrigin="0.513,1.469" Height="26"/>
<Label x:Name="lname" Content="Last Name" HorizontalAlignment="Left" Margin="88,83,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.167,-0.458" Width="72" Height="25"/>
<TextBox x:Name="fnametxt" Height="23" Margin="167,54,0,0" TextWrapping="Wrap" VerticalAlignment="Top" RenderTransformOrigin="0.501,0.452" HorizontalAlignment="Left" Width="136"/>
<Button x:Name="exitbtn" Content="Exit" HorizontalAlignment="Left" VerticalAlignment="Top" Width="135" Margin="447,365,0,0" Height="38" RenderTransformOrigin="0.489,0.462"/>
<Label x:Name="label" Content="Password" HorizontalAlignment="Left" Margin="92,113,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.064,0.601" Height="26" Width="68"/>
<PasswordBox x:Name="passwordBox" Margin="167,113,0,0" VerticalAlignment="Top" Height="24" HorizontalAlignment="Left" Width="136"/>
<TextBox x:Name="stsbox" HorizontalAlignment="Left" Height="62" Margin="67,267,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="300" Background="#FFDADADA" Foreground="Black" Opacity="0.45" SelectionBrush="#FF1D6EBF" RenderTransformOrigin="0.503,-0.59" IsReadOnly="True"/>
<TextBox x:Name="lnametxt" Height="23" Margin="167,85,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Left" Width="136"/>
</Grid>
</Window>
"#
$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[xml]$xaml = $inputXML
#Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
try{$Form=[Windows.Markup.XamlReader]::Load( $reader )}
catch{Write-Host "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed."}
$xaml.SelectNodes("//*[#Name]") | %{Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name) -Scope Global}
Function global:Get-FormVariables{
if ($global:ReadmeDisplay -ne $true) {$global:ReadmeDisplay=$true}
#write-host "Found the following interactable elements from our form" -ForegroundColor Cyan
get-variable WPF*
}
Get-FormVariables
$WPFCreate_User.Add_Click({
$test = { for($i=1; $i -le 100000; $i++){
$z = $i + $z
$z
}}
$job = Start-Job $test
Wait-Job $job
Receive-Job $job -OutVariable results
Remove-Job $job
$WPFstsbox.text = "$results`n"
})
$WPFexitbtn.add_click({
$form.Close() | out-null
exit})
$form.ShowDialog() | Out-null
Yes, PSJobs are truly "background" jobs.
When you call Start-Job, a separate process is started, executing your job/command.
In your script, the job itself doesn't block the calling thread, but your subsequent command (Wait-Job $job) does.
If you simply fire off Start-Job and return from the Click handler, your UI wouldn't lock up:
$button.add_Click({
$jobCode = { Start-Sleep -Seconds 10 }
$job = Start-Job $jobCode
})
you'll see that the UI becomes responsive again in way lees than 10 seconds.
The problem is that you no longer have access to the $job and no longer control over when it is to be displayed.
To compensate for this, you need background thread or a timed event that can periodically check up on the job(s) for you and output the result.
You could use a Timer and PowerShell's builtin eventing infrastructure for this:
# Create timer
$backgroundTimer = New-Object System.Timers.Timer
# Set interval (ms)
$backgroundTimer.Interval = 500
# Make sure timer stops raising events after each interval
$backgroundTimer.AutoReset = $false
# Have powershell "listen" for the event in the background
Register-ObjectEvent $backgroundTimer -EventName 'Elapsed' -SourceIdentifier 'timerElapsed' -Action {
# Loop through any completed jobs with data to return, from "oldest" to "newest"
while(($finishedJob = Get-Job |Where-Object {$_.State -eq 'Completed' -and $_.HasMoreData}|Select-Object -First 1))
{
# Update the text box on your WPF form with the results
# NOTE: the event is executed in a separate scope, thus the "global:" scope prefix
$global:WPFstsbox.Text = "$(Receive-Job $finishedJob)`n"
# Clean up the job
Remove-Job $finishedJob
}
# Restart timer
$backgroundTimer.Start()
}
# Enable the timer
$backgroundTimer.Enabled = $true
I'm trying to convert my PowerShell project to an executable program (.exe). After some research I found PowerGUI. After converting my .ps1 file into an exe I ran into some problems:
Firstly it takes ages to start the program (about 15 seconds), is this normal or is there something I can do to improve this?
Secondly, if i exit the program I get a windows error message saying the program stopped working unexpectedly. Is there a way to hide this message?
Here is my ps1 code, I got a part of it from a blog and it is my first PowerShell code so don't be to harsh on me ;)
$inputXML = #"
<Window x:Class="BlogPostIII.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:BlogPostIII"
mc:Ignorable="d"
Title="Organizer" Height="540" Width="540" FontSize="18.667">
<Grid x:Name="background">
<Button x:Name="OK" Content="OK" HorizontalAlignment="Left" Height="41" Margin="420,458,0,0" VerticalAlignment="Top" Width="100" FontSize="18.667"/>
<Button x:Name="Cancel" Content="Cancel" HorizontalAlignment="Left" Height="41" Margin="315,458,0,0" VerticalAlignment="Top" Width="100" FontSize="18.667"/>
<TextBox x:Name="TextBox1" HorizontalAlignment="Left" Height="30" Margin="108,216,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="300" FontSize="18.667"/>
<TextBlock x:Name="TextBlock1" HorizontalAlignment="Left" Height="30" Margin="108,36,0,0" TextWrapping="Wrap" Text="Soort bewerking:" VerticalAlignment="Top" Width="300" FontSize="18.667"/>
<TextBox x:Name="TextBox2" HorizontalAlignment="Left" Height="30" Margin="108,291,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="300" FontSize="18.667"/>
<TextBlock x:Name="TextBlock2" HorizontalAlignment="Left" Height="30" Margin="108,111,0,0" TextWrapping="Wrap" Text="Naam Machine:" VerticalAlignment="Top" Width="300" FontSize="18.667"/>
<TextBox x:Name="TextBox3" HorizontalAlignment="Left" Height="30" Margin="108,366,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="300" FontSize="18.667"/>
<TextBlock x:Name="TextBlock3" HorizontalAlignment="Left" Height="30" Margin="108,185,0,0" TextWrapping="Wrap" Text="Naam van opdrachtgevend bedrijf:" VerticalAlignment="Top" Width="300" FontSize="18.667"/>
<TextBlock x:Name="TextBlock4" HorizontalAlignment="Left" Height="30" Margin="108,261,0,0" TextWrapping="Wrap" Text="Naam product:" VerticalAlignment="Top" Width="300" FontSize="18.667"/>
<TextBlock x:Name="TextBlock5" HorizontalAlignment="Left" Height="30" Margin="108,336,0,0" TextWrapping="Wrap" Text="Product ID:" VerticalAlignment="Top" Width="300" FontSize="18.667"/>
<ComboBox x:Name="combobox1" HorizontalAlignment="Left" Margin="108,66,0,0" VerticalAlignment="Top" Width="300"/>
<ComboBox x:Name="combobox2" HorizontalAlignment="Left" Margin="108,140,0,0" VerticalAlignment="Top" Width="300"/>
</Grid>
</Window>
"#
$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = $inputXML
#Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
try{$Form=[Windows.Markup.XamlReader]::Load( $reader )}
catch{Write-Host "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed."}
#===========================================================================
# Store Form Objects In PowerShell
#===========================================================================
$xaml.SelectNodes("//*[#Name]") | %{Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name)}
Function Get-FormVariables{
if ($global:ReadmeDisplay -ne $true){Write-host "If you need to reference this display again, run Get-FormVariables" -ForegroundColor Yellow;$global:ReadmeDisplay=$true}
write-host "Found the following interactable elements from our form" -ForegroundColor Cyan
get-variable WPF*
}
Get-FormVariables
#===========================================================================
# List the Comboboxes
#===========================================================================
$WPFcombobox1.AddText('Draaien')
$WPFcombobox1.AddText('Frezen')
$WPFcombobox1.AddText('Slijpen')
$WPFcombobox2.AddText('Doosan 3100LM')
$WPFcombobox2.AddText('Doosan 123')
$WPFcombobox2.AddText('machine 3')
$WPFcombobox2.AddText('machine 4')
$WPFcombobox2.AddText('machine 5')
#===========================================================================
# Actually make the objects work
#===========================================================================
#$WPFMakeUserbutton.Add_Click({(Get-FormFields)})
$WPFOK.Add_Click({
$1 = $WPFcomboBox1.Text
$2 = $WPFcomboBox2.Text
$3 = $WPFtextBox1.Text
$4 = $WPFtextBox2.Text
$5 = $WPFtextBox3.Text + " Werkblad"
New-Item C:\Users\Bjorn\Documents\Powershell\$1\$2\$3\$4\ -Force -type directory
Copy-Item C:\Users\Bjorn\Documents\Powershell\Test_werkblad.docx C:\Users\Bjorn\Documents\Powershell\$1\$2\$3\$4\
Rename-Item C:\Users\Bjorn\Documents\Powershell\$1\$2\$3\$4\Test_werkblad.docx C:\Users\Bjorn\Documents\Powershell\$1\$2\$3\$4\$5.docx
Invoke-Item C:\Users\Bjorn\Documents\Powershell\$1\$2\$3\$4
Invoke-Item C:\Users\Bjorn\Documents\Powershell\$1\$2\$3\$4\$5.docx
$Form.Close()})
$WPFCancel.Add_Click({
$Form.Close()})
#===========================================================================
# Shows the form
#===========================================================================
write-host "To show the form, run the following" -ForegroundColor Cyan
function Show-Form{
$Form.ShowDialog() | out-null
}
Show-Form
About the 15 seconds I am not sure why. Could you give more details? You should add logging and put time stamp in every message... This way you should be able to locate slow part.
About avoiding the error message, you should surround your code with try/catch. For example:
[...]
write-host "To show the form, run the following" -ForegroundColor Cyan
function Show-Form
{
$Form.ShowDialog() | out-null
}
try
{
Show-Form
}
catch
{
$ErrorMessage = $_.Exception.Message
# Show friendly message with error and/or log the error)
}
After reading your code here is some extra advice that could be useful:
Do not hardcode paths. (Like "C:\Users\Bjorn\Documents\Powershell\")
Do you really need PowerShell? Sounds like C# WinForms would be better for this task.
Store XML in a file and read it rather having it in code in $inputXML variable.
The 15 second pause is PowerShell initializing in the background. This is especially noticeable on Windows 7
CAVEAT: "compiling" PowerShell to EXE in most cases (PowerGUI included) is stuffing your original PS1 file inside a Self-extracting EXE. You could do the same with 7-Zip or WinZip.
A PowerGUI "compiled" EXE locks your script execution to whatever version or PowerShell + .Net you had on your DEV box. IOW: If you compile on a PC with PowerShell v4 but only use Write-host in your script (for example), the target PCs will need PowerShell v4+ for it to run!