So I have a button that pulls this function to search for all the Install.Log files on a machine.
After loading the results, I want to have a double click event on the a row, where it will open the log file.
I am having a hard time adding a click event, and anytime I try to find something related to Datatables I find stuff about java. Any guidance or links would be appreciated.
Thanks in advace
TEST THE CODE FOR YOUR SELF BY RUNNING THIS IN PS ISE
$ComputerName = "your computer name here"
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = #'
<Window Name="Form"
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"
Title="Install Logs" Height="488.773" Width="797.65" Icon = "\\bconac01\ds-support\GS_IT\Tools\Test Tools (Alx)\Tool\icon.ico" ShowInTaskbar="False">
<Grid Margin="0,0,-8,-21">
<DataGrid Name="DataGrid1" HorizontalAlignment="Left" Height="368" VerticalAlignment="Top" Width="772" Margin="10,41,0,0"/>
<Label Content="Filter" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
<TextBox Name="FilterTextBox" HorizontalAlignment="Left" Height="26" Margin="78,10,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="172"/>
</Grid>
</Window>
'#
#Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
try{$Software=[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."; exit}
# Store Form Objects In PowerShell
$xaml.SelectNodes("//*[#Name]") | ForEach-Object{
Set-Variable -Name ($_.Name) -Value $Software.FindName($_.Name)
Write-host $_.Name
}
$Fields = #(
'Name'
'LastWriteTime'
)
#$Services = Get-WmiObject -Computer ($prebox.text + $device.text) -Class Win32reg_AddRemovePrograms | Select-object -Property *
$Services = Get-ChildItem \\$ComputerName\c$\build\logs -Include *install* -recurse -ErrorAction Stop | Sort-Object LastWriteTime -Descending
# Add Services to a datatable
$Datatable = New-Object System.Data.DataTable
[void]$Datatable.Columns.AddRange($Fields)
foreach ($Service in $Services)
{
$Array = #()
Foreach ($Field in $Fields)
{
$array += $Service.$Field
}
[void]$Datatable.Rows.Add($array)
}
#$filter = "DisplayName LIKE 'B%'"
#$Datatable.DefaultView.RowFilter = $filter
# Create a datagrid object and populate with datatable
$DataGrid1.ItemsSource = $Datatable.DefaultView
$DataGrid1.CanUserAddRows = $False
$DataGrid1.IsReadOnly = $True
$DataGrid1.GridLinesVisibility = "None"
$DataGrid1.Add_CellMouseClick({gridClick})
function gridClick(){
$rowIndex = $DataGrid1.CurrentRow.Index
$columnIndex = $DataGrid1.CurrentCell.ColumnIndex
Write-Host $rowIndex
Write-Host $columnIndex
Write-Host $DataGrid1.Rows[$rowIndex].Cells[0].value
Write-Host $DataGrid1.Rows[$rowIndex].Cells[$columnIndex].value}
$FilterTextBox.Add_TextChanged({
$InputText = $FilterTextBox.Text
$filter = "Name LIKE '$InputText%'"
$Datatable.DefaultView.RowFilter = $filter
$DataGrid1.ItemsSource = $Datatable.DefaultView
$form.Controls.Add($DataGrid1)
$Software.Controls.Add($DataGrid1)
})
# Shows the form
$statusBar1.Text = "Done."
$Software.Add_Shown({$Software.Activate()})
$Software.ShowDialog() | out-null
--Things Ive tried that are suggested.
[
Ok. Here's a sample with gridview cell click event. You can add cell double click event with $DataGrid1.Add_CellMouseClick({gridClick})
hope this should help
$form = New-Object System.Windows.Forms.Form
$form.Size = New-Object System.Drawing.Size(900,600)
$DataGrid1 = New-Object System.Windows.Forms.DataGridView
$DataGrid1.Size=New-Object System.Drawing.Size(800,400)
$DataGrid1.Add_CellMouseClick({gridClick})
$form.Controls.Add($DataGrid1)
#Create an unbound DataGridView by declaring a column count.
$DataGrid1.ColumnCount = 4
$DataGrid1.ColumnHeadersVisible = $true
#Set the column header names.
$DataGrid1.Columns[0].Name = "Recipe"
$DataGrid1.Columns[1].Name = "Category"
$DataGrid1.Columns[2].Name = "Third COlumn"
$DataGrid1.Columns[3].Name = "Rating"
#Populate the rows.
$row1 = #("Meatloaf","Main Dish", "boringMeatloaf", "boringMeatloafRanking")
$row2 = #("Key Lime Pie","Dessert", "lime juice evaporated milk", "****")
$row3 = #("Orange-Salsa Pork Chops","Main Dish", "pork chops, salsa, orange juice", "****")
$row4 = #("Black Bean and Rice Salad","Salad", "black beans, brown rice", "****")
$row5 = #("Chocolate Cheesecake","Dessert", "cream cheese", "***")
$row6 = #("Black Bean Dip", "Appetizer","black beans, sour cream", "***")
$rows = #( $row1, $row2, $row3, $row4, $row5, $row6 )
foreach ($row in $rows){
$DataGrid1.Rows.Add($row)
}
function gridClick(){
$rowIndex = $DataGrid1.CurrentRow.Index
$columnIndex = $DataGrid1.CurrentCell.ColumnIndex
Write-Host $rowIndex
Write-Host $columnIndex
Write-Host $DataGrid1.Rows[$rowIndex].Cells[0].value
Write-Host $DataGrid1.Rows[$rowIndex].Cells[$columnIndex].value}
$form.ShowDialog()
As per your requirement here's one another sample I have created with WPF with PowerShell. You can bind the event using $WPFListView.Add_MouseDoubleClick({gridClick}) and to access the selected cell value using column like $WPFListView.SelectedValue.OriginalFileName
Try this as it is in PowerShell ISE. This is how it looks
##Sample DataTable
$tabName = "SampleTable"
#Create Table object
$table = New-Object system.Data.DataTable “$tabName”
#Define Columns
$col1 = New-Object system.Data.DataColumn OriginalFileName,([string])
$col2 = New-Object system.Data.DataColumn FileDescription,([string])
$col3 = New-Object system.Data.DataColumn FileVersionRaw,([string])
#Add the Columns
$table.columns.add($col1)
$table.columns.add($col2)
$table.columns.add($col3)
#Create a row
$row = $table.NewRow()
$row.OriginalFileName = "Test Log"
$row.FileDescription = "Test log data"
$row.FileVersionRaw = "v1.0"
$row1 = $table.NewRow()
$row1.OriginalFileName = "IIS Log"
$row1.FileDescription = "IIS Sys log"
$row1.FileVersionRaw = "v2.0"
$row2 = $table.NewRow()
$row2.OriginalFileName = "User Data"
$row2.FileDescription = "User data details"
$row2.FileVersionRaw = "v1.0"
$row3 = $table.NewRow()
$row3.OriginalFileName = "Sys Info"
$row3.FileDescription = "System Info Details"
$row3.FileVersionRaw = "v2.0"
#Add the row to the table
$table.Rows.Add($row)
$table.Rows.Add($row1)
$table.Rows.Add($row2)
$table.Rows.Add($row3)
##Sample DataTable
$inputXML = #"
<Window x:Class="FileVersionChecker.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:FileVersionChecker"
mc:Ignorable="d"
Title="FileVersionChecker" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="115*"/>
<ColumnDefinition Width="373*"/>
<ColumnDefinition Width="29*"/>
</Grid.ColumnDefinitions>
<ListView Name="ListView" Grid.Column="1" HorizontalAlignment="Left" Height="150" Margin="10,10,0,0" VerticalAlignment="Top" Width="350">
<ListView.View>
<GridView>
<GridViewColumn Header="OriginalFileName" DisplayMemberBinding ="{Binding 'OriginalFileName'}" Width="100"/>
<GridViewColumn Header="FileDescription" DisplayMemberBinding ="{Binding 'FileDescription'}" Width="100"/>
<GridViewColumn Header="FileVersionRaw" DisplayMemberBinding ="{Binding 'FileVersionRaw'}" Width="100"/>
</GridView>
</ListView.View>
</ListView>
</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-Output "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed."
}
$xaml.SelectNodes("//*[#Name]") | ForEach-Object {Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name)}
$WPFListView.ItemsSource = $table.DefaultView
$WPFListView.Add_MouseDoubleClick({gridClick})
function gridClick()
{
Write-Host ""
Write-Host "$($WPFListView.SelectedValue.OriginalFileName) , $($WPFListView.SelectedValue.FileDescription), $($WPFListView.SelectedValue.FileVersionRaw)"
}
$Form.ShowDialog() | out-null;
Related
I have PowerShell script and need to use message box to alert but I found this Powershell message box
my question is to make the powershell message box above in the link to support multi-lines and to support right-to-left Languages like Arabic.
Here is an example to show you how you can use this function in Powershell :
# PowerShell function to display a customizable WPF message box / window
# https://gist.github.com/SMSAgentSoftware/0c0eee98a673b6ac34f5215ea6841beb
# https://smsagent.wordpress.com/2017/08/24/a-customisable-wpf-messagebox-for-powershell/
cls
Function New-WPFMessageBox {
# For examples for use, see my blog:
# https://smsagent.wordpress.com/2017/08/24/a-customisable-wpf-messagebox-for-powershell/
# CHANGES
# 2017-09-11 - Added some required assemblies in the dynamic parameters to avoid errors when run from the PS console host.
# Define Parameters
[CmdletBinding()]
Param
(
# The popup Content
[Parameter(Mandatory=$True,Position=0)]
[Object]$Content,
# The window title
[Parameter(Mandatory=$false,Position=1)]
[string]$Title,
# The buttons to add
[Parameter(Mandatory=$false,Position=2)]
[ValidateSet('OK','OK-Cancel','Abort-Retry-Ignore','Yes-No-Cancel','Yes-No','Retry-Cancel','Cancel-TryAgain-Continue','None')]
[array]$ButtonType = 'OK',
# The buttons to add
[Parameter(Mandatory=$false,Position=3)]
[array]$CustomButtons,
# Content font size
[Parameter(Mandatory=$false,Position=4)]
[int]$ContentFontSize = 14,
# Title font size
[Parameter(Mandatory=$false,Position=5)]
[int]$TitleFontSize = 14,
# BorderThickness
[Parameter(Mandatory=$false,Position=6)]
[int]$BorderThickness = 0,
# CornerRadius
[Parameter(Mandatory=$false,Position=7)]
[int]$CornerRadius = 8,
# ShadowDepth
[Parameter(Mandatory=$false,Position=8)]
[int]$ShadowDepth = 3,
# BlurRadius
[Parameter(Mandatory=$false,Position=9)]
[int]$BlurRadius = 20,
# WindowHost
[Parameter(Mandatory=$false,Position=10)]
[object]$WindowHost,
# Timeout in seconds,
[Parameter(Mandatory=$false,Position=11)]
[int]$Timeout,
# Code for Window Loaded event,
[Parameter(Mandatory=$false,Position=12)]
[scriptblock]$OnLoaded,
# Code for Window Closed event,
[Parameter(Mandatory=$false,Position=13)]
[scriptblock]$OnClosed
)
# Dynamically Populated parameters
DynamicParam {
# Add assemblies for use in PS Console
Add-Type -AssemblyName System.Drawing, PresentationCore
# ContentBackground
$ContentBackground = 'ContentBackground'
$AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.Mandatory = $False
$AttributeCollection.Add($ParameterAttribute)
$RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$arrSet = [System.Drawing.Brushes] | Get-Member -Static -MemberType Property | Select -ExpandProperty Name
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
$AttributeCollection.Add($ValidateSetAttribute)
$PSBoundParameters.ContentBackground = "White"
$RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ContentBackground, [string], $AttributeCollection)
$RuntimeParameterDictionary.Add($ContentBackground, $RuntimeParameter)
# FontFamily
$FontFamily = 'FontFamily'
$AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.Mandatory = $False
$AttributeCollection.Add($ParameterAttribute)
$arrSet = [System.Drawing.FontFamily]::Families.Name | Select -Skip 1
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
$AttributeCollection.Add($ValidateSetAttribute)
$RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($FontFamily, [string], $AttributeCollection)
$RuntimeParameterDictionary.Add($FontFamily, $RuntimeParameter)
$PSBoundParameters.FontFamily = "Segoe UI"
# TitleFontWeight
$TitleFontWeight = 'TitleFontWeight'
$AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.Mandatory = $False
$AttributeCollection.Add($ParameterAttribute)
$arrSet = [System.Windows.FontWeights] | Get-Member -Static -MemberType Property | Select -ExpandProperty Name
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
$AttributeCollection.Add($ValidateSetAttribute)
$PSBoundParameters.TitleFontWeight = "Normal"
$RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($TitleFontWeight, [string], $AttributeCollection)
$RuntimeParameterDictionary.Add($TitleFontWeight, $RuntimeParameter)
# ContentFontWeight
$ContentFontWeight = 'ContentFontWeight'
$AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.Mandatory = $False
$AttributeCollection.Add($ParameterAttribute)
$arrSet = [System.Windows.FontWeights] | Get-Member -Static -MemberType Property | Select -ExpandProperty Name
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
$AttributeCollection.Add($ValidateSetAttribute)
$PSBoundParameters.ContentFontWeight = "Normal"
$RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ContentFontWeight, [string], $AttributeCollection)
$RuntimeParameterDictionary.Add($ContentFontWeight, $RuntimeParameter)
# ContentTextForeground
$ContentTextForeground = 'ContentTextForeground'
$AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.Mandatory = $False
$AttributeCollection.Add($ParameterAttribute)
$arrSet = [System.Drawing.Brushes] | Get-Member -Static -MemberType Property | Select -ExpandProperty Name
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
$AttributeCollection.Add($ValidateSetAttribute)
$PSBoundParameters.ContentTextForeground = "Black"
$RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ContentTextForeground, [string], $AttributeCollection)
$RuntimeParameterDictionary.Add($ContentTextForeground, $RuntimeParameter)
# TitleTextForeground
$TitleTextForeground = 'TitleTextForeground'
$AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.Mandatory = $False
$AttributeCollection.Add($ParameterAttribute)
$arrSet = [System.Drawing.Brushes] | Get-Member -Static -MemberType Property | Select -ExpandProperty Name
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
$AttributeCollection.Add($ValidateSetAttribute)
$PSBoundParameters.TitleTextForeground = "Black"
$RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($TitleTextForeground, [string], $AttributeCollection)
$RuntimeParameterDictionary.Add($TitleTextForeground, $RuntimeParameter)
# BorderBrush
$BorderBrush = 'BorderBrush'
$AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.Mandatory = $False
$AttributeCollection.Add($ParameterAttribute)
$arrSet = [System.Drawing.Brushes] | Get-Member -Static -MemberType Property | Select -ExpandProperty Name
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
$AttributeCollection.Add($ValidateSetAttribute)
$PSBoundParameters.BorderBrush = "Black"
$RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($BorderBrush, [string], $AttributeCollection)
$RuntimeParameterDictionary.Add($BorderBrush, $RuntimeParameter)
# TitleBackground
$TitleBackground = 'TitleBackground'
$AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.Mandatory = $False
$AttributeCollection.Add($ParameterAttribute)
$arrSet = [System.Drawing.Brushes] | Get-Member -Static -MemberType Property | Select -ExpandProperty Name
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
$AttributeCollection.Add($ValidateSetAttribute)
$PSBoundParameters.TitleBackground = "White"
$RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($TitleBackground, [string], $AttributeCollection)
$RuntimeParameterDictionary.Add($TitleBackground, $RuntimeParameter)
# ButtonTextForeground
$ButtonTextForeground = 'ButtonTextForeground'
$AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.Mandatory = $False
$AttributeCollection.Add($ParameterAttribute)
$arrSet = [System.Drawing.Brushes] | Get-Member -Static -MemberType Property | Select -ExpandProperty Name
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
$AttributeCollection.Add($ValidateSetAttribute)
$PSBoundParameters.ButtonTextForeground = "Black"
$RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ButtonTextForeground, [string], $AttributeCollection)
$RuntimeParameterDictionary.Add($ButtonTextForeground, $RuntimeParameter)
# Sound
$Sound = 'Sound'
$AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.Mandatory = $False
#$ParameterAttribute.Position = 14
$AttributeCollection.Add($ParameterAttribute)
$arrSet = (Get-ChildItem "$env:SystemDrive\Windows\Media" -Filter Windows* | Select -ExpandProperty Name).Replace('.wav','')
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
$AttributeCollection.Add($ValidateSetAttribute)
$RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($Sound, [string], $AttributeCollection)
$RuntimeParameterDictionary.Add($Sound, $RuntimeParameter)
return $RuntimeParameterDictionary
}
Begin {
Add-Type -AssemblyName PresentationFramework
}
Process {
# Define the XAML markup
[XML]$Xaml = #"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Window" Title="" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterScreen" WindowStyle="None" ResizeMode="NoResize" AllowsTransparency="True" Background="Transparent" Opacity="1">
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border>
<Grid Background="{TemplateBinding Background}">
<ContentPresenter />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Border x:Name="MainBorder" Margin="10" CornerRadius="$CornerRadius" BorderThickness="$BorderThickness" BorderBrush="$($PSBoundParameters.BorderBrush)" Padding="0" >
<Border.Effect>
<DropShadowEffect x:Name="DSE" Color="Black" Direction="270" BlurRadius="$BlurRadius" ShadowDepth="$ShadowDepth" Opacity="0.6" />
</Border.Effect>
<Border.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="DSE" Storyboard.TargetProperty="ShadowDepth" From="0" To="$ShadowDepth" Duration="0:0:1" AutoReverse="False" />
<DoubleAnimation Storyboard.TargetName="DSE" Storyboard.TargetProperty="BlurRadius" From="0" To="$BlurRadius" Duration="0:0:1" AutoReverse="False" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
<Grid >
<Border Name="Mask" CornerRadius="$CornerRadius" Background="$($PSBoundParameters.ContentBackground)" />
<Grid x:Name="Grid" Background="$($PSBoundParameters.ContentBackground)">
<Grid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=Mask}"/>
</Grid.OpacityMask>
<StackPanel Name="StackPanel" >
<TextBox Name="TitleBar" IsReadOnly="True" IsHitTestVisible="False" Text="$Title" Padding="10" FontFamily="$($PSBoundParameters.FontFamily)" FontSize="$TitleFontSize" Foreground="$($PSBoundParameters.TitleTextForeground)" FontWeight="$($PSBoundParameters.TitleFontWeight)" Background="$($PSBoundParameters.TitleBackground)" HorizontalAlignment="Stretch" VerticalAlignment="Center" Width="Auto" HorizontalContentAlignment="Center" BorderThickness="0"/>
<DockPanel Name="ContentHost" Margin="0,10,0,10" >
</DockPanel>
<DockPanel Name="ButtonHost" LastChildFill="False" HorizontalAlignment="Center" >
</DockPanel>
</StackPanel>
</Grid>
</Grid>
</Border>
</Window>
"#
[XML]$ButtonXaml = #"
<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="Auto" Height="30" FontFamily="Segui" FontSize="16" Background="Transparent" Foreground="White" BorderThickness="1" Margin="10" Padding="20,0,20,0" HorizontalAlignment="Right" Cursor="Hand"/>
"#
[XML]$ButtonTextXaml = #"
<TextBlock xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" FontFamily="$($PSBoundParameters.FontFamily)" FontSize="16" Background="Transparent" Foreground="$($PSBoundParameters.ButtonTextForeground)" Padding="20,5,20,5" HorizontalAlignment="Center" VerticalAlignment="Center"/>
"#
[XML]$ContentTextXaml = #"
<TextBlock xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Text="$Content" Foreground="$($PSBoundParameters.ContentTextForeground)" DockPanel.Dock="Right" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="$($PSBoundParameters.FontFamily)" FontSize="$ContentFontSize" FontWeight="$($PSBoundParameters.ContentFontWeight)" TextWrapping="Wrap" Height="Auto" MaxWidth="500" MinWidth="50" Padding="10"/>
"#
# Load the window from XAML
$Window = [Windows.Markup.XamlReader]::Load((New-Object -TypeName System.Xml.XmlNodeReader -ArgumentList $xaml))
# Custom function to add a button
Function Add-Button {
Param($Content)
$Button = [Windows.Markup.XamlReader]::Load((New-Object -TypeName System.Xml.XmlNodeReader -ArgumentList $ButtonXaml))
$ButtonText = [Windows.Markup.XamlReader]::Load((New-Object -TypeName System.Xml.XmlNodeReader -ArgumentList $ButtonTextXaml))
$ButtonText.Text = "$Content"
$Button.Content = $ButtonText
$Button.Add_MouseEnter({
$This.Content.FontSize = "17"
})
$Button.Add_MouseLeave({
$This.Content.FontSize = "16"
})
$Button.Add_Click({
New-Variable -Name WPFMessageBoxOutput -Value $($This.Content.Text) -Option ReadOnly -Scope Script -Force
$Window.Close()
})
$Window.FindName('ButtonHost').AddChild($Button)
}
# Add buttons
If ($ButtonType -eq "OK")
{
Add-Button -Content "OK"
}
If ($ButtonType -eq "OK-Cancel")
{
Add-Button -Content "OK"
Add-Button -Content "Cancel"
}
If ($ButtonType -eq "Abort-Retry-Ignore")
{
Add-Button -Content "Abort"
Add-Button -Content "Retry"
Add-Button -Content "Ignore"
}
If ($ButtonType -eq "Yes-No-Cancel")
{
Add-Button -Content "Yes"
Add-Button -Content "No"
Add-Button -Content "Cancel"
}
If ($ButtonType -eq "Yes-No")
{
Add-Button -Content "Yes"
Add-Button -Content "No"
}
If ($ButtonType -eq "Retry-Cancel")
{
Add-Button -Content "Retry"
Add-Button -Content "Cancel"
}
If ($ButtonType -eq "Cancel-TryAgain-Continue")
{
Add-Button -Content "Cancel"
Add-Button -Content "TryAgain"
Add-Button -Content "Continue"
}
If ($ButtonType -eq "None" -and $CustomButtons)
{
Foreach ($CustomButton in $CustomButtons)
{
Add-Button -Content "$CustomButton"
}
}
# Remove the title bar if no title is provided
If ($Title -eq "")
{
$TitleBar = $Window.FindName('TitleBar')
$Window.FindName('StackPanel').Children.Remove($TitleBar)
}
# Add the Content
If ($Content -is [String])
{
# Replace double quotes with single to avoid quote issues in strings
If ($Content -match '"')
{
$Content = $Content.Replace('"',"'")
}
# Use a text box for a string value...
$ContentTextBox = [Windows.Markup.XamlReader]::Load((New-Object -TypeName System.Xml.XmlNodeReader -ArgumentList $ContentTextXaml))
$Window.FindName('ContentHost').AddChild($ContentTextBox)
}
Else
{
# ...or add a WPF element as a child
Try
{
$Window.FindName('ContentHost').AddChild($Content)
}
Catch
{
$_
}
}
# Enable window to move when dragged
$Window.FindName('Grid').Add_MouseLeftButtonDown({
$Window.DragMove()
})
# Activate the window on loading
If ($OnLoaded)
{
$Window.Add_Loaded({
$This.Activate()
Invoke-Command $OnLoaded
})
}
Else
{
$Window.Add_Loaded({
$This.Activate()
})
}
# Stop the dispatcher timer if exists
If ($OnClosed)
{
$Window.Add_Closed({
If ($DispatcherTimer)
{
$DispatcherTimer.Stop()
}
Invoke-Command $OnClosed
})
}
Else
{
$Window.Add_Closed({
If ($DispatcherTimer)
{
$DispatcherTimer.Stop()
}
})
}
# If a window host is provided assign it as the owner
If ($WindowHost)
{
$Window.Owner = $WindowHost
$Window.WindowStartupLocation = "CenterOwner"
}
# If a timeout value is provided, use a dispatcher timer to close the window when timeout is reached
If ($Timeout)
{
$Stopwatch = New-object System.Diagnostics.Stopwatch
$TimerCode = {
If ($Stopwatch.Elapsed.TotalSeconds -ge $Timeout)
{
$Stopwatch.Stop()
$Window.Close()
}
}
$DispatcherTimer = New-Object -TypeName System.Windows.Threading.DispatcherTimer
$DispatcherTimer.Interval = [TimeSpan]::FromSeconds(1)
$DispatcherTimer.Add_Tick($TimerCode)
$Stopwatch.Start()
$DispatcherTimer.Start()
}
# Play a sound
If ($($PSBoundParameters.Sound))
{
$SoundFile = "$env:SystemDrive\Windows\Media\$($PSBoundParameters.Sound).wav"
$SoundPlayer = New-Object System.Media.SoundPlayer -ArgumentList $SoundFile
$SoundPlayer.Add_LoadCompleted({
$This.Play()
$This.Dispose()
})
$SoundPlayer.LoadAsync()
}
# Display the window
$null = $window.Dispatcher.InvokeAsync{$window.ShowDialog()}.Wait()
}
}
$Params = #{
Content = "The file 'Important Document.docx' could not be deleted."
Title = "File Deletion Error"
TitleBackground = "LightGray"
TitleFontSize = 28
TitleFontWeight = "Bold"
TitleTextForeground = "Red"
ContentFontSize = 18
ContentFontWeight = "Medium"
}
$Content = "ذكرت تقارير صحفية لوكالة الأنباء رويترز نقلا عن ثلاثة مسؤولين إن وزراة التجارة الأمريكية تعتزم إصدار أمر اليوم الجمعة بمنع الأشخاص داخل الولايات المتحدة من تنزيل تطبيق وي تشات WeChat وتطبيق تيك توك TikTok ابتداء من يوم الأحد المقبل 20 سبتمبر.
وأضاف المسؤولون لوكالة رويترز أن الحظر المفروض على تطبيق تيك توك قد يتم إلغاؤه من قبل الرئيس الأمريكي، دونالد ترامب قبل أن يدخل القرار حيز التنفيذ يوم الأحد المقبل. يأتي هذا في الوقت الذي تسعى فيه شركة بايت دانس ByteDance، الشركة الأم لتطبيق تيك توك، للتوصل إلى اتفاق بشأن عملياتها داخل الولايات المتحدة.
وتجدر الإشارة إلى أن هذا الحظر جاء استجابة لأوامر تنفيذية أصدرها الرئيس الأمريكي دونالد ترامب في 6 أغسطس الماضي، أعطت وزارة التجارة 45 يوما لتحديد المعاملات التي يجب حظرها من التطبيقات التي اعتبرها تشكل تهديدا للأمن القومي، وبذلك يصبح الموعد النهائي يوم الأحد المقبل.
وكان مسؤولون في وزارة التجارة الأمريكية قد صرحوا بأنهم يتخذون إجراءات غير عادية بسبب المخاطر التي تشكلها تلك التطبيقات من خلال جمع بيانات المستخدمين، بينما نفت الشركة الصينية استخدام أي من التطبيقات في عمليات التجسس.
investing
"
$Params = #{
Content = $Content
Title = "MISSION:POSSIBLE"
TitleFontSize = 30
TitleTextForeground = 'Red'
TitleFontWeight = 'Bold'
TitleBackground = 'Silver'
FontFamily = 'Lucida Console'
ButtonType = 'None'
Timeout = 30
}
New-WPFMessageBox #Params
EDIT : Or you can download the function from the internet into New-MsgBox.ps1 and inculde it with your main script like this example :
$MyUrlFile = "https://pastebin.com/raw/cVsJ5x2Y"
$OutPutFile = "$env:TEMP\New-MsgBox.ps1"
Invoke-WebRequest $MyUrlFile -Outfile $OutPutFile
.$OutPutFile # The dot operator is used for script include.
$Content = "ذكرت تقارير صحفية لوكالة الأنباء رويترز نقلا عن ثلاثة مسؤولين إن وزراة التجارة الأمريكية تعتزم إصدار أمر اليوم الجمعة بمنع الأشخاص داخل الولايات المتحدة من تنزيل تطبيق وي تشات WeChat وتطبيق تيك توك TikTok ابتداء من يوم الأحد المقبل 20 سبتمبر.
وأضاف المسؤولون لوكالة رويترز أن الحظر المفروض على تطبيق تيك توك قد يتم إلغاؤه من قبل الرئيس الأمريكي، دونالد ترامب قبل أن يدخل القرار حيز التنفيذ يوم الأحد المقبل. يأتي هذا في الوقت الذي تسعى فيه شركة بايت دانس ByteDance، الشركة الأم لتطبيق تيك توك، للتوصل إلى اتفاق بشأن عملياتها داخل الولايات المتحدة.
وتجدر الإشارة إلى أن هذا الحظر جاء استجابة لأوامر تنفيذية أصدرها الرئيس الأمريكي دونالد ترامب في 6 أغسطس الماضي، أعطت وزارة التجارة 45 يوما لتحديد المعاملات التي يجب حظرها من التطبيقات التي اعتبرها تشكل تهديدا للأمن القومي، وبذلك يصبح الموعد النهائي يوم الأحد المقبل.
وكان مسؤولون في وزارة التجارة الأمريكية قد صرحوا بأنهم يتخذون إجراءات غير عادية بسبب المخاطر التي تشكلها تلك التطبيقات من خلال جمع بيانات المستخدمين، بينما نفت الشركة الصينية استخدام أي من التطبيقات في عمليات التجسس.
investing
"
$Params = #{
Content = $Content
Title = "MISSION:POSSIBLE"
TitleFontSize = 30
TitleTextForeground = 'Red'
TitleFontWeight = 'Bold'
TitleBackground = 'Silver'
FontFamily = 'Lucida Console'
ButtonType = 'None'
Timeout = 30
}
New-WPFMessageBox #Params
Help me please figure out how asynchronous loop execution works.
In many forums, examples are very cumbersome and not understandable
During execution, the program window freezes, and at the end of the cycle it shows the final number
for example
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = #"
<Window
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"
Title="Рассылка интернет-трафика" Height="200" Width="300" ResizeMode="NoResize">
<Grid Width="300">
<TextBox Name="text_result" TextWrapping="Wrap" Margin="0,0,0,0" VerticalAlignment="Top" Height="31" Width="200" />
<Button Name="button_start" Content="Start" Height="31" Margin="0,50,0,0" VerticalAlignment="Top" Width="200" />
<Button Name="button_close" Content="Close" Height="31" Margin="0,100,0,0" VerticalAlignment="Top" Width="200"/>
</Grid>
</Window>
"#
#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"; exit}
# Store Form Objects In PowerShell
$xaml.SelectNodes("//*[#Name]") | ForEach-Object {Set-Variable -Name ($_.Name) -Value $Form.FindName($_.Name)}
$button_start.Add_Click({
for ($row = 2; $row -le 1000; $row++) {
$text_result.Text = $row
}
})
#Загружаем главную форму
$Form.ShowDialog() | out-null
It took me quite a while to figure this one out. Let me preface by saying that Adam the Automator has a fantastic breakdown:
https://adamtheautomator.com/building-asynchronous-powershell-functions/
The easiest way to think about executing an asynchronous loop is to think about tricking powershell. In short, you just want it to do something in the background while your script is running. So, you just need to turn the loop you would like into a script block and use start-job, a.k.a. &.
Let's use a common task, such as looking through a bunch of files. Here, we'll use gci.
$scriptblock = {
param ($folder)
Get-ChildItem -Path $folder}
Start-job -ScriptBlock $scriptblock -ArgumentList "C:\Windows" -name "stackexample"
The name is important so that you can reference it later. Now you have a job in the background, and you're good to continue with your script. But what happens if you want to see if the job is done? There's a command for checking on the status of your job (code below).
Get-Job -Name stackexample
One thing to note, make sure to CLOSE your jobs once they're done.
$status = (Get-Job -Name stackexample).state
if ($status -eq "completed")
{
Remove-Job -Name stackexample
}
Wait, I need to know what those folders were! No problem. We just need to tell the scriptblock to output data (write-host, for example). Then, we receive the job before we close it. So a full circuit might look like this:
$scriptblock = {
param ($folder)
$folders = Get-ChildItem -Path $folder
write-host $folders
}
Start-job -ScriptBlock $scriptblock -ArgumentList "C:\Windows" -name "stackexample"
Start-Sleep -Seconds 5
$status = (Get-Job -Name stackexample).state
if ($status -eq "completed")
{
Receive-Job -Name StackExample -Wait
Remove-Job -Name stackexample
}
You can get so much fancier from here, but that is the core of what 'asynchronous code execution' is - telling powershell to do something time consuming while you're doing other stuff.
As applied to your code, you can use the button click to set off a job and run in the background, then receive the final results.
$xaml.SelectNodes("//*[#Name]") | ForEach-Object {Set-Variable -Name ($_.Name) -Value $Form.FindName($_.Name)}
$scriptblock = {
for ($row = 2; $row -le 1000; $row++)
{
$result = $row
}
return $result
}
$button_start.Add_Click({
Start-job -ScriptBlock $scriptblock -Name scriptblock
While ((Get-Job -Name scriptblock).State -ne "Completed")
{
start-sleep 1
}
$results = receive-job -Name scriptblock -keep
remove-job -Name scriptblock
$text_result.Text = $results
})
Above the button click we've defined the script block. When the button is clicked, it kicks off the job and then watches it with the while loop. Once the job is finished, we can extract the result (done here with return $result in the scriptblock). Receive the job and you have your final variable which you can now display for the user.
With no cycle.
The form is just a name in this case. You can remove all GUI elements from it if you want.
$Script:SyncHashTable = [Hashtable]::Synchronized(#{})
$RunSpace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
$RunSpace.ApartmentState = "STA"
$RunSpace.ThreadOptions = "ReuseThread"
$RunSpace.Open()
$RunSpace.SessionStateProxy.SetVariable("SyncHashTable", $Script:SyncHashTable)
$PowerShellCmd = [Management.Automation.PowerShell]::Create().AddScript({
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$Script:SyncHashTable.objForm = New-Object System.Windows.Forms.Form
$Script:SyncHashTable.InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
$Script:SyncHashTable.objForm.Size = New-Object System.Drawing.Size(340, 200)
$Script:SyncHashTable.objForm.Name = "objForm"
$Script:SyncHashTable.objForm.Text = "Test form"
$Script:SyncHashTable.objLabel = New-Object System.Windows.Forms.Label
$Script:SyncHashTable.objLabel.Location = New-Object System.Drawing.Size(10,20)
$Script:SyncHashTable.objLabel.Size = New-Object System.Drawing.Size(320,20)
$Script:SyncHashTable.objLabel.Text = "Enter the data and press the OK button or key Enter"
$Script:SyncHashTable.objForm.Controls.Add($Script:SyncHashTable.objLabel)
$Script:SyncHashTable.objTextBox = New-Object System.Windows.Forms.TextBox
$Script:SyncHashTable.objTextBox.Location = New-Object System.Drawing.Size(10,40)
$Script:SyncHashTable.objTextBox.Size = New-Object System.Drawing.Size(300,20)
$Script:SyncHashTable.objForm.Controls.Add($Script:SyncHashTable.objTextBox)
$Script:SyncHashTable.objForm.KeyPreview = $True
$Script:SyncHashTable.objForm.Add_KeyDown( { if ($_.KeyCode -eq "Enter") { $Script:SyncHashTable.X = $Script:SyncHashTable.objTextBox.Text;$Script:SyncHashTable.objForm.Close() } })
$Script:SyncHashTable.objForm.Add_KeyDown( { if ($_.KeyCode -eq "Escape") { $objForm.Close() } })
$Script:SyncHashTable.OKButton = New-Object System.Windows.Forms.Button
$Script:SyncHashTable.OKButton.Location = New-Object System.Drawing.Size(75,120)
$Script:SyncHashTable.OKButton.Size = New-Object System.Drawing.Size(75,23)
$Script:SyncHashTable.OKButton.Text = "OK"
$Script:SyncHashTable.OKButton.Add_Click( { $Script:SyncHashTable.X = $Script:SyncHashTable.objTextBox.Text;$Script:SyncHashTable.objForm.Close() } )
$Script:SyncHashTable.objForm.Controls.Add($Script:SyncHashTable.OKButton)
$Script:SyncHashTable.InitialFormWindowState = $Script:SyncHashTable.objForm.WindowState
$Script:SyncHashTable.objForm.add_Load($Script:SyncHashTable.OnLoadForm_StateCorrection)
[void] $Script:SyncHashTable.objForm.ShowDialog()
})
$PowerShellCmd.Runspace = $RunSpace
$obj=$PowerShellCmd.BeginInvoke()
#Time to retrieve our missing object $AsyncObject
$BindingFlags = [Reflection.BindingFlags]'nonpublic','instance'
$Field = $PowerShellCmd.GetType().GetField('invokeAsyncResult',$BindingFlags)
$AsyncObject = $Field.GetValue($PowerShellCmd)
Write-Host 'Here is your code that will be executed in parallel with the form code'
Write-Host '(any length and execution time)'
Write-Host "`$PowerShellCmd.EndInvoke(`$AsyncObject) will wait for results from the form,"
Write-Host "or pick them up if they are ready."
Write-Host "=================================="
#Closing the execution space when getting the result
$PowerShellCmd.EndInvoke($AsyncObject)
'This is the result of executing the form code: {0}' -f $Script:SyncHashTable.X
is it possible in PowerShell and WPF to trigger an event out of a keyboard click and a mouse click?
I have for example following button in my WPF:
<Button Name="button_test" Content="Test" Grid.Column="0" Grid.Row="4" />
I would like to trigger to two events with it. One event with a standard mouse click:
$button_test.Add_Click({ #do something })
And something when I hold CTRL and do a mouse click on the same button.
A keyboard click I do like this
$window.add_KeyDown{
param
(
[Parameter(Mandatory)][Object]$sender,
[Parameter(Mandatory)][Windows.Input.KeyEventArgs]$e
)
if($e.Key -eq 'CTRL') { #do something else}
}
But I don’t know how to combine these two events. Is it even possible?
Thanks
Stephan
sorry for the late respond. I "solved" it with an extra variable, but I am not sure if this is the correct way, so any suggestion would be appreciated.
$title = "test"
Add-Type -AssemblyName PresentationFramework
# GUI
[xml]$xaml = #"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Window_GuiManagement" Title="$title" WindowStartupLocation = "CenterScreen"
Width = "Auto" Height = "350.000" Visibility="Visible" WindowStyle="ToolWindow" ResizeMode="NoResize" SizeToContent="WidthAndHeight" >
<Grid>
<DockPanel Margin="5">
<StackPanel DockPanel.Dock="Bottom" >
<Button Name="button_1" Content="Button 1" />
<Button Name="button_2" Content="Button 2" />
</StackPanel>
</DockPanel>
</Grid>
</Window>
"#
$reader = (New-Object System.Xml.XmlNodeReader $xaml)
$window = [Windows.Markup.XamlReader]::Load($reader)
# Declare objects
$button_1 = $window.FindName("button_1")
$button_2 = $window.FindName("button_2")
$global:ctrl = $false
$button_1.Add_Click({ Write-Host "Button 1 clicked" })
$button_2.Add_Click({
if($global:ctrl){
Write-Host "do more" $ctrl
} else {
Write-Host "do standard" $ctrl
}
})
$window.add_KeyDown{
param
(
[Parameter(Mandatory)][Object]$sender,
[Parameter(Mandatory)][Windows.Input.KeyEventArgs]$e
)
#Write-Host $e.Key
if($e.Key -eq "LeftCtrl")
{
return ($global:ctrl = $true)
}
}
$window.add_KeyUp{
param
(
[Parameter(Mandatory)][Object]$sender,
[Parameter(Mandatory)][Windows.Input.KeyEventArgs]$e
)
#Write-Host $e.Key
if($e.Key -eq "LeftCtrl")
{
return ($global:ctrl = $false)
}
}
$Window.ShowDialog() | Out-Null
I'm an active reader of StackOverflow as it usually helps me resolve all my issues.
But for my first question ever I'll ask your help about runspaces in Powershell, specifically for memory management.
I created some PS apps with GUI and now I use runspaces to avoid hang of the GUI when big operations are running.
My problem is that I can't manage to dispose my runspaces when they are finished.
Regarding the code below, it's a simple script to try to understand how I can dispose of my runspaces. I tried to incorporate a unique runspace to monitor and dispose the app runspaces, I inspired myself from a proxb's script (huge fan btw) but it doesn't seems to work.
Everytime I execute the runspace I gain 10Mb of memory, huge leak!
Can you help me to resolve this problem please ?
[xml]$xaml = #'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Runspace" FontFamily="Calibri" Height="400" Width="400" FontSize="14">
<Grid Margin="10,10,10,10">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="10"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox x:Name="TB_Results" Grid.Row="0" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"/>
<Button x:Name="BT_Run" Grid.Row="2" Content="Run" Height="30"/>
</Grid>
</Window>
'#
# Inits
Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName PresentationCore
Add-Type -AssemblyName WindowsBase
$gui = [hashtable]::Synchronized(#{})
$reader = (New-Object Xml.XmlNodeReader $xaml)
$gui.Window = [Windows.Markup.XamlReader]::Load($reader)
$xaml.SelectNodes("//*[#*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach-Object { $gui.Add($_.Name,$gui.Window.FindName($_.Name)) }
$Script:Jobs = [system.collections.arraylist]::Synchronized((New-Object System.Collections.ArrayList))
$Script:JobCleanup = [hashtable]::Synchronized(#{ Host = $host })
# Test function
function RunFunc {
$newRunspace = [runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable("gui",$gui)
$Code = {
$memupdate = "Memory: $($(Get-Process -Id $PID).PrivateMemorySize64 / 1mb) mb"
$gui.Window.Dispatcher.invoke("Normal",[action]{
$gui.TB_Results.Text = "$memupdate`r`n" + $gui.TB_Results.Text
})
}
$Powershell = [powershell]::Create().AddScript($Code)
$Powershell.Runspace = $newRunspace
[void]$Script:Jobs.Add((
[PSCustomObject]#{
PowerShell = $PowerShell
Runspace = $PowerShell.BeginInvoke()
}
))
}
# Background Runspace to clean up jobs
$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable("jobs",$Script:Jobs)
$Code = {
$JobCleanup = $true
do {
foreach($runspace in $jobs) {
if ($runspace.Runspace.isCompleted) {
$runspace.powershell.EndInvoke($runspace.Runspace) | Out-Null
$runspace.powershell.dispose()
$runspace.Runspace = $null
$runspace.powershell = $null
}
}
$temphash = $jobs.clone()
$temphash | Where-Object { $_.runspace -eq $Null } | ForEach-Object { $jobs.remove($_) }
Start-Sleep -Seconds 1
} while ($JobCleanup)
}
$Powershell = [powershell]::Create().AddScript($Code)
$Powershell.Runspace = $newRunspace
$PowerShell.BeginInvoke()
# gui events
$gui.BT_Run.add_Click({ RunFunc })
$gui.Window.ShowDialog() | Out-Null
When you call $runspace.PowerShell.Dispose(), the PowerShell instance is disposed, but the runspace that it's tied to is not automatically disposed, you'll need to do that first yourself in the cleanup task:
$runspace.powershell.EndInvoke($runspace.Runspace) | Out-Null
$runspace.powershell.Runspace.Dispose() # remember to clean up the runspace!
$runspace.powershell.dispose()
i recently made a robocopy function with PowerShell so i can transfer files over from one computer to another. However, i cannot figure out how to incorporate a progress bar into my script since i did it a bit differently. Here is an excerpt from the script:
$inputXML = #"
<Window x:Class="DataTransferV2.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:DataTransferV2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Background="#FF0D94D2">
<Grid>
<Label x:Name="head_label" Content="Device Data Transfer" HorizontalAlignment="Left" Margin="151,10,0,0" VerticalAlignment="Top" FontSize="18" FontFamily="Segoe UI Black"/>
<Button x:Name="checkall_button" Content="Check All" HorizontalAlignment="Left" Margin="212,49,0,0" VerticalAlignment="Top" Width="75"/>
<CheckBox x:Name="documents_checkBox" Content="Documents" HorizontalAlignment="Left" Margin="57,102,0,0" VerticalAlignment="Top"/>
<Button x:Name="start_button" Content="Start" HorizontalAlignment="Left" Margin="89,246,0,0" VerticalAlignment="Top" Width="88" Height="36"/>
<Button x:Name="cancel_button" Content="Cancel" HorizontalAlignment="Left" Margin="205,246,0,0" VerticalAlignment="Top" Width="88" Height="36"/>
<Button x:Name="exit_button" Content="Exit" HorizontalAlignment="Left" Margin="320,246,0,0" VerticalAlignment="Top" Width="88" Height="36"/>
</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)}
#===========================================================================
# Transfer Documents
#===========================================================================
Function global:Documents_Transfer{
if($WPFdocuments_checkBox.IsChecked -eq $true){
Write-Host "Transferring Documents..."
$quotation = ":"
$source = "$DLetter$quotation\Users\$associd\Documents"
$destination = "C:\Users\$env:USERNAME\Documents"
$copy = "/COPY:DTO"
$files = "*.*"
$backup = "/B"
$reportextrafiles = "/X"
$verboseoutput = "V"
$fullpathname = "FP"
$mirror = "/MIR"
$backupmode = "/ZB"
$subfolders = "/E"
$Retries = "/R:2"
$consolewrite = "/TEE"
robocopy $source $destination $files $backup $reportextrafiles $verboseoutput $fullpathname $mirror $backupmode $subfolders $Retries $consolewrite
Write-Host "Documents Transferred"
}
}
$WPFstart_button.Add_Click({
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
$associd = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the associate ID:", "Verification", "")
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
$DLetter = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the external drive letter:", "Drive", "")
Documents_Transfer
})
#===========================================================================
# Closes the form
#===========================================================================
$WPFexit_button.Add_Click({
$Form.Close()
})
#===========================================================================
# Shows the form
#===========================================================================
$Form.ShowDialog() | out-null
Currently i just have a write-host that attempts to track some progress. Can anyone offer advice as to how i can track this progress? Thanks in advanced!