I have created a gui with powershell that allows me to copy files from inside folders into one place .
Example you have a folder A and in folder A there is Folder B and C inside all these folders you have a bunch of logfiles named logfile + date.log
My app can hunt through all the folders and move copy the log files to a single location.
The script works but when it is getting a list of files to copy it is slow and says "Not responding" at the top of the window next to where it says "Copy Files To One Directory"
I have added the script can you guys have a look and let me know how to speed it up.
The functions it hik are causing the problem are after the "These are the function I seems to be having a problem with"
Thank for the help.
The code
#------------------------------------------------------------------------
# Source File Information (DO NOT MODIFY)
# Source ID: 3020bcd1-4cba-4aca-968e-b4bc4ca05d76
# Source File: Copy\Copy.pfproj
#------------------------------------------------------------------------
#========================================================================
# Generated On: 11/15/2011 5:11 PM
# Generated By: justin
#========================================================================
#----------------------------------------------
#region Import Assemblies
#----------------------------------------------
[void][Reflection.Assembly]::Load("System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
[void][Reflection.Assembly]::Load("System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
[void][Reflection.Assembly]::Load("System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
[void][Reflection.Assembly]::Load("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
[void][Reflection.Assembly]::Load("System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
[void][Reflection.Assembly]::Load("System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
[void][Reflection.Assembly]::Load("System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
#endregion Import Assemblies
#Define a Param block to use custom parameters in the project
#Param ($CustomParameter)
function Main {
Param ([String]$Commandline)
#Note: This function starts the application
#Note: $Commandline contains the complete argument string passed to the packager
#Note: $Args contains the parsed arguments passed to the packager (Type: System.Array)
#Note: To get the script directory in the Packager use: Split-Path $hostinvocation.MyCommand.path
#Note: To get the console output in the Packager (Windows Mode) use: $ConsoleOutput (Type: System.Collections.ArrayList)
#TODO: Initialize and add Function calls to forms
if((Call-MainForm_pff) -eq "OK")
{
}
$global:ExitCode = 0 #Set the exit code for the Packager
}
#region Call-MainForm_pff
function Call-MainForm_pff
{
#----------------------------------------------
#region Import the Assemblies
#----------------------------------------------
[void][reflection.assembly]::Load("System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
[void][reflection.assembly]::Load("System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
[void][reflection.assembly]::Load("System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
[void][reflection.assembly]::Load("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
[void][reflection.assembly]::Load("System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
[void][reflection.assembly]::Load("System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
[void][reflection.assembly]::Load("System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
#endregion Import Assemblies
#----------------------------------------------
#region Generated Form Objects
#----------------------------------------------
[System.Windows.Forms.Application]::EnableVisualStyles()
$CopyFilesToOneDirectoy = New-Object System.Windows.Forms.Form
$labelExtension = New-Object System.Windows.Forms.Label
$labelFileName = New-Object System.Windows.Forms.Label
$Extension = New-Object System.Windows.Forms.TextBox
$FileName = New-Object System.Windows.Forms.TextBox
$buttonSetDestination = New-Object System.Windows.Forms.Button
$buttonSetSource = New-Object System.Windows.Forms.Button
$Dlabel = New-Object System.Windows.Forms.Label
$Slabel = New-Object System.Windows.Forms.Label
$DestinationFolder = New-Object System.Windows.Forms.TextBox
$SourceFolder = New-Object System.Windows.Forms.TextBox
$progress = New-Object System.Windows.Forms.ProgressBar
$buttonCopyFiles = New-Object System.Windows.Forms.Button
$sourcebrowserdialog = New-Object System.Windows.Forms.FolderBrowserDialog
$destinationbrowserdialog = New-Object System.Windows.Forms.FolderBrowserDialog
$InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
#endregion Generated Form Objects
#----------------------------------------------
# User Generated Script
#----------------------------------------------
$OnLoadFormEvent={
}
$buttonSetSource_Click={
$result = $sourcebrowserdialog.ShowDialog()
if ($result -eq 'Ok'){
$SourceFolder.Text =$sourcebrowserdialog.SelectedPath
}
else{
$SourceFolder.Text = ''
}
}
$buttonSetDestination_Click={
$result = $destinationbrowserdialog.ShowDialog()
if ($result -eq 'Ok'){
$DestinationFolder.Text =$destinationbrowserdialog.SelectedPath
}
else{
$DestinationFolder.Text = ''
}
}
$buttonCopyFiles_Click={
#TODO: Place custom script here
$progress.Value = 0
$CName = $FileName.Text
$CExtension = "." + $Extension.Text
$CSource = $SourceFolder.Text
$CDestination = $DestinationFolder.Text
CopyfFiles $CSource $CDestination $CName $CExtension
}
# --End User Generated Script--
#----------------------------------------------
#region Generated Events
#----------------------------------------------
$Form_StateCorrection_Load=
{
#Correct the initial state of the form to prevent the .Net maximized form issue
$CopyFilesToOneDirectoy.WindowState = $InitialFormWindowState
}
$Form_StoreValues_Closing=
{
#Store the control values
$script:MainForm_Extension = $Extension.Text
$script:MainForm_FileName = $FileName.Text
$script:MainForm_DestinationFolder = $DestinationFolder.Text
$script:MainForm_SourceFolder = $SourceFolder.Text
}
$Form_Cleanup_FormClosed=
{
#Remove all event handlers from the controls
try
{
$buttonSetDestination.remove_Click($buttonSetDestination_Click)
$buttonSetSource.remove_Click($buttonSetSource_Click)
$buttonCopyFiles.remove_Click($buttonCopyFiles_Click)
$CopyFilesToOneDirectoy.remove_Load($OnLoadFormEvent)
$CopyFilesToOneDirectoy.remove_Load($Form_StateCorrection_Load)
$CopyFilesToOneDirectoy.remove_Closing($Form_StoreValues_Closing)
$CopyFilesToOneDirectoy.remove_FormClosed($Form_Cleanup_FormClosed)
}
catch [Exception]
{ }
}
#endregion Generated Events
#----------------------------------------------
#region Generated Form Code
#----------------------------------------------
#
# CopyFilesToOneDirectoy
#
$CopyFilesToOneDirectoy.Controls.Add($labelExtension)
$CopyFilesToOneDirectoy.Controls.Add($labelFileName)
$CopyFilesToOneDirectoy.Controls.Add($Extension)
$CopyFilesToOneDirectoy.Controls.Add($FileName)
$CopyFilesToOneDirectoy.Controls.Add($buttonSetDestination)
$CopyFilesToOneDirectoy.Controls.Add($buttonSetSource)
$CopyFilesToOneDirectoy.Controls.Add($Dlabel)
$CopyFilesToOneDirectoy.Controls.Add($Slabel)
$CopyFilesToOneDirectoy.Controls.Add($DestinationFolder)
$CopyFilesToOneDirectoy.Controls.Add($SourceFolder)
$CopyFilesToOneDirectoy.Controls.Add($progress)
$CopyFilesToOneDirectoy.Controls.Add($buttonCopyFiles)
$CopyFilesToOneDirectoy.ClientSize = '458, 245'
$CopyFilesToOneDirectoy.Name = "CopyFilesToOneDirectoy"
$CopyFilesToOneDirectoy.StartPosition = 'CenterScreen'
$CopyFilesToOneDirectoy.Text = "Copy Files To One Directoy"
$CopyFilesToOneDirectoy.add_Load($OnLoadFormEvent)
#
# labelExtension
#
$labelExtension.AutoSize = $True
$labelExtension.Font = "Microsoft Sans Serif, 9.75pt, style=Bold"
$labelExtension.Location = '349, 54'
$labelExtension.Name = "labelExtension"
$labelExtension.Size = '75, 16'
$labelExtension.TabIndex = 11
$labelExtension.Text = "Extension"
#
# labelFileName
#
$labelFileName.AutoSize = $True
$labelFileName.Font = "Microsoft Sans Serif, 9.75pt, style=Bold"
$labelFileName.Location = '12, 54'
$labelFileName.Name = "labelFileName"
$labelFileName.Size = '79, 16'
$labelFileName.TabIndex = 10
$labelFileName.Text = "File Name"
#
# Extension
#
$Extension.Font = "Microsoft Sans Serif, 9.75pt"
$Extension.Location = '349, 74'
$Extension.Name = "Extension"
$Extension.Size = '93, 22'
$Extension.TabIndex = 9
#
# FileName
#
$FileName.Font = "Microsoft Sans Serif, 9.75pt"
$FileName.Location = '12, 74'
$FileName.Name = "FileName"
$FileName.Size = '300, 22'
$FileName.TabIndex = 8
#
# buttonSetDestination
#
$buttonSetDestination.Location = '349, 173'
$buttonSetDestination.Name = "buttonSetDestination"
$buttonSetDestination.Size = '93, 23'
$buttonSetDestination.TabIndex = 7
$buttonSetDestination.Text = "Set Destination"
$buttonSetDestination.TextAlign = 'MiddleLeft'
$buttonSetDestination.UseVisualStyleBackColor = $True
$buttonSetDestination.add_Click($buttonSetDestination_Click)
#
# buttonSetSource
#
$buttonSetSource.Location = '349, 123'
$buttonSetSource.Name = "buttonSetSource"
$buttonSetSource.Size = '93, 23'
$buttonSetSource.TabIndex = 6
$buttonSetSource.Text = "Set Source"
$buttonSetSource.UseVisualStyleBackColor = $True
$buttonSetSource.add_Click($buttonSetSource_Click)
#
# Dlabel
#
$Dlabel.AutoSize = $True
$Dlabel.Font = "Microsoft Sans Serif, 9.75pt, style=Bold"
$Dlabel.Location = '12, 154'
$Dlabel.Name = "Dlabel"
$Dlabel.Size = '135, 16'
$Dlabel.TabIndex = 5
$Dlabel.Text = "Destination Folder"
#
# Slabel
#
$Slabel.AutoSize = $True
$Slabel.Font = "Microsoft Sans Serif, 9.75pt, style=Bold"
$Slabel.Location = '12, 104'
$Slabel.Name = "Slabel"
$Slabel.Size = '106, 16'
$Slabel.TabIndex = 4
$Slabel.Text = "Source Folder"
#
# DestinationFolder
#
$DestinationFolder.Font = "Microsoft Sans Serif, 9.75pt"
$DestinationFolder.Location = '12, 174'
$DestinationFolder.Name = "DestinationFolder"
$DestinationFolder.Size = '300, 22'
$DestinationFolder.TabIndex = 3
$DestinationFolder.Text = "c:\td"
#
# SourceFolder
#
$SourceFolder.Font = "Microsoft Sans Serif, 9.75pt"
$SourceFolder.Location = '12, 124'
$SourceFolder.Name = "SourceFolder"
$SourceFolder.Size = '300, 22'
$SourceFolder.TabIndex = 2
$SourceFolder.Text = "c:\ts"
#
# progress
#
$progress.Location = '12, 212'
$progress.Name = "progress"
$progress.Size = '300, 23'
$progress.TabIndex = 1
#
# buttonCopyFiles
#
$buttonCopyFiles.Location = '349, 212'
$buttonCopyFiles.Name = "buttonCopyFiles"
$buttonCopyFiles.Size = '93, 23'
$buttonCopyFiles.TabIndex = 0
$buttonCopyFiles.Text = "Copy Files"
$buttonCopyFiles.UseVisualStyleBackColor = $True
$buttonCopyFiles.add_Click($buttonCopyFiles_Click)
#
# sourcebrowserdialog
#
$sourcebrowserdialog.RootFolder = 'MyComputer'
$sourcebrowserdialog.ShowNewFolderButton = $False
#
# destinationbrowserdialog
#
$destinationbrowserdialog.RootFolder = 'MyComputer'
$destinationbrowserdialog.ShowNewFolderButton = $False
#endregion Generated Form Code
#----------------------------------------------
#Save the initial state of the form
$InitialFormWindowState = $CopyFilesToOneDirectoy.WindowState
#Init the OnLoad event to correct the initial state of the form
$CopyFilesToOneDirectoy.add_Load($Form_StateCorrection_Load)
#Clean up the control events
$CopyFilesToOneDirectoy.add_FormClosed($Form_Cleanup_FormClosed)
#Store the control values when form is closing
$CopyFilesToOneDirectoy.add_Closing($Form_StoreValues_Closing)
#Show the Form
return $CopyFilesToOneDirectoy.ShowDialog()
}
#endregion
#--------------------------------------------------------------------------------------------------------------------------------------------------
# These are the function I seems to be havig a problem with
#Sample function that provides the location of the script
function Get-ScriptDirectory
{
if($hostinvocation -ne $null)
{
Split-Path $hostinvocation.MyCommand.path
}
else
{
Split-Path $script:MyInvocation.MyCommand.Path
}
}
function ListOfFiles ($Source,$Name,$Extension){
$FileListArray = #()
foreach($file in Get-ChildItem $Source -Force -Recurse | Where-Object {$_.name -like $Name -and $_.Extension -eq $Extension})
{
If ($FileListArray -match "$file")
{
$NewFileName = $file.Directory.Name + "." + $file.Name
$FileListArray += ,#($file,$file.FullName,$NewFileName)
}
Else
{
$FileListArray += ,#($file,$file.FullName,$file)
}
}
Return $FileListArray
}
Function CopyfFiles ($Source,$Destination,$Name,$Extension){
$ListFiles = #(listOfFiles -Source $Source -Name $Name -Extension $Extension)
$ListFilesTemp = #()
Foreach ($CF in $ListFiles)
{
$Check = $Destination + "\"+ $CF[2]
#If The File is Not Already In The Destination Add It To The Temp Array
IF (Test-Path -Path "$Check")
{}
Else
{$ListFilesTemp += ,#($CF[0],$CF[1],$CF[2])}
$ListFiles = $ListFilesTemp
}
$progress.Minimum = 0
$progress.Maximum = $ListFiles.Count
$progress.Step = 1
Foreach ($File in $ListFiles)
{
$FileDestination = $Destination + "\"+ $File[2]
Copy-Item -Path $File[1] -Destination $FileDestination
$progress.PerformStep()
}
}
#Sample variable that provides the location of the script
[string]$ScriptDirectory = Get-ScriptDirectory
#--------------------------------------------------------------------------------------------------------------------------------------------------
#endregion
#Start the application
Main ($CommandLine)
<code>
"Not Responding" means the thread isn't waiting for windowing input (in your example the thread is running the powershell code). I don't think there is a trivial way to create new threads in Powershell. However, you might be able to make your GUI more responsive using PowerBoots and powershell jobs like in this example.
You are declaring local variables inside a function (Call-MainForm_pff) and using these variables in other function. That will not work. See sample code. Only $Var2 will be displayed.
#=================
Function One-Set-Value {
$Var1 = "This is a test."
}
Function Two-Display-Value
{
$Var2 = "This is a second test."
Write-Host "Var2" $Var2
Write-Host "Var1" $Var1
}
One-Set-Value
Two-Display-Value
#=================
Convert these variables in to functions. This will work with return values.
Related
I have not been able to find any information on this so I'm not sure its entirely possible. I'm currently trying to integrate the WebView2 control into my PowerShell Winform GUI. I can see while debugging my script that the webView2 control is created, but nothing ever loads. I have the WebView2 runtime installed.
This is what I have so far:
function Show-test-WebView2Control_psf {
#----------------------------------------------
#region Import the Assemblies
#----------------------------------------------
[void][reflection.assembly]::LoadFile('C:\Tests\Assemblies\Debug\Microsoft.Web.WebView2.WinForms.dll')
[void][reflection.assembly]::LoadFile('C:\Tests\Assemblies\Microsoft.Web.WebView2.Core.dll')
[void][reflection.assembly]::LoadFile('C:\Tests\Assemblies\Microsoft.Web.WebView2.Wpf.dll')
[void][reflection.assembly]::Load('System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')
[void][reflection.assembly]::Load('System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
#endregion Import Assemblies
#----------------------------------------------
#region Generated Form Objects
#----------------------------------------------
[System.Windows.Forms.Application]::EnableVisualStyles()
$form1 = New-Object 'System.Windows.Forms.Form'
$buttonRefresh = New-Object 'System.Windows.Forms.Button'
$buttonGo = New-Object 'System.Windows.Forms.Button'
$textbox1 = New-Object 'System.Windows.Forms.TextBox'
[Microsoft.Web.WebView2.WinForms.WebView2] $webview = New-Object 'Microsoft.Web.WebView2.WinForms.WebView2'
$InitialFormWindowState = New-Object 'System.Windows.Forms.FormWindowState'
#endregion Generated Form Objects
#----------------------------------------------
# User Generated Script
#----------------------------------------------
$form1_Load={
#TODO: Initialize Form Controls here
$webview.Source = ([uri]::new($textbox1.Text))
$webview.Visible = $true
}
$webview_Ready = {
$webview.NavigateToString("https://www.google.com")
}
$webview_CoreWebView2Ready={
$webview.Source = [System.Uri]$textbox1.Text
}
$buttonGo_Click={
#TODO: Place custom script here
$webview.Source = [System.Uri] $textbox1.Text;
}
$webview_SourceChanged={
$form1.Text = $webview.Source.AbsoluteUri;
}
# --End User Generated Script--
#----------------------------------------------
#region Generated Events
#----------------------------------------------
$Form_StateCorrection_Load=
{
#Correct the initial state of the form to prevent the .Net maximized form issue
$form1.WindowState = $InitialFormWindowState
}
$Form_Cleanup_FormClosed=
{
#Remove all event handlers from the controls
try
{
$buttonGo.remove_Click($buttonGo_Click)
$webview.remove_CoreWebView2Ready($webview_CoreWebView2Ready)
$webview.remove_SourceChanged($webview_SourceChanged)
$form1.remove_Load($form1_Load)
$form1.remove_Load($Form_StateCorrection_Load)
$form1.remove_FormClosed($Form_Cleanup_FormClosed)
}
catch { Out-Null <# Prevent PSScriptAnalyzer warning #> }
}
#endregion Generated Events
#----------------------------------------------
#region Generated Form Code
#----------------------------------------------
$form1.SuspendLayout()
#
# form1
#
$form1.Controls.Add($buttonRefresh)
$form1.Controls.Add($buttonGo)
$form1.Controls.Add($textbox1)
$form1.Controls.Add($webview)
$form1.AutoScaleDimensions = New-Object System.Drawing.SizeF(6, 13)
$form1.AutoScaleMode = 'Font'
$form1.ClientSize = New-Object System.Drawing.Size(619, 413)
$form1.Name = 'form1'
$form1.Text = 'Form'
$form1.add_Load($form1_Load)
#
# buttonRefresh
#
$buttonRefresh.Location = New-Object System.Drawing.Point(13, 13)
$buttonRefresh.Name = 'buttonRefresh'
$buttonRefresh.Size = New-Object System.Drawing.Size(75, 23)
$buttonRefresh.TabIndex = 3
$buttonRefresh.Text = 'Refresh'
$buttonRefresh.UseVisualStyleBackColor = $True
#
# buttonGo
#
$buttonGo.Location = New-Object System.Drawing.Point(538, 9)
$buttonGo.Name = 'buttonGo'
$buttonGo.Size = New-Object System.Drawing.Size(75, 23)
$buttonGo.TabIndex = 2
$buttonGo.Text = 'Go'
$buttonGo.UseVisualStyleBackColor = $True
$buttonGo.add_Click($buttonGo_Click)
#
# textbox1
#
$textbox1.Location = New-Object System.Drawing.Point(96, 13)
$textbox1.Name = 'textbox1'
$textbox1.Size = New-Object System.Drawing.Size(435, 20)
$textbox1.TabIndex = 1
$textbox1.Text = 'https://www.bing.com'
#
# webview
#
$webview.Location = New-Object System.Drawing.Point(0, 49)
$webview.Name = 'webview'
$webview.Size = New-Object System.Drawing.Size(619, 364)
$webview.TabIndex = 0
$webview.ZoomFactor = 1
$webview.add_SourceChanged($webview_SourceChanged)
$form1.ResumeLayout()
#endregion Generated Form Code
#----------------------------------------------
#Save the initial state of the form
$InitialFormWindowState = $form1.WindowState
#Init the OnLoad event to correct the initial state of the form
$form1.add_Load($Form_StateCorrection_Load)
#Clean up the control events
$form1.add_FormClosed($Form_Cleanup_FormClosed)
#Show the Form
return $form1.ShowDialog()
} #End Function
#Call the form
Show-test-WebView2Control_psf | Out-Null
Is there a way to do this? If so, can someone please tell me what I'm doing wrong?
Trying out your code on my own I had to resolve two issues:
From the webview2 nuget package (for reference I used the WebView2 nuget 1.0.781-prerelease package) I copied the winforms and core DLL from the net45 folder and I copied the x64 webview2loader.dll all into the wv2 folder. You need to ensure the webview2loader.dll of the appropriate CPU architecture is copied next to the core dll.
The default WebView2 user data folder is in the folder containing the host executable. In this case the host executable is powershell and the containing folder isn't writable by normal processes. So you need to change the user data folder which you can do via the CoreWebView2CreationProperties UserDataFolder property before the WebView2 is initialized.
I have the modified code below. Additional work is required to resize and close the WebView2 and resolve other issues.
function Show-test-WebView2Control_psf {
#----------------------------------------------
#region Import the Assemblies
#----------------------------------------------
# I've put the following files in `C:\users\davris\tmp\wv2`
# Microsoft.Web.WebView2.Core.dll
# Microsoft.Web.WebView2.WinForms.dll
# WebView2Loader.dll
[void][reflection.assembly]::LoadFile('C:\users\davris\tmp\wv2\Microsoft.Web.WebView2.WinForms.dll')
[void][reflection.assembly]::LoadFile('C:\users\davris\tmp\wv2\Microsoft.Web.WebView2.Core.dll')
[void][reflection.assembly]::Load('System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')
[void][reflection.assembly]::Load('System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
#endregion Import Assemblies
#----------------------------------------------
#region Generated Form Objects
#----------------------------------------------
[System.Windows.Forms.Application]::EnableVisualStyles()
$form1 = New-Object 'System.Windows.Forms.Form'
$buttonRefresh = New-Object 'System.Windows.Forms.Button'
$buttonGo = New-Object 'System.Windows.Forms.Button'
$textbox1 = New-Object 'System.Windows.Forms.TextBox'
[Microsoft.Web.WebView2.WinForms.WebView2] $webview = New-Object 'Microsoft.Web.WebView2.WinForms.WebView2'
$webview.CreationProperties = New-Object 'Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties'
$webview.CreationProperties.UserDataFolder = "C:\users\davris\tmp\wv2\data";
$InitialFormWindowState = New-Object 'System.Windows.Forms.FormWindowState'
#endregion Generated Form Objects
#----------------------------------------------
# User Generated Script
#----------------------------------------------
$form1_Load={
#TODO: Initialize Form Controls here
$webview.Source = ([uri]::new($textbox1.Text))
$webview.Visible = $true
}
$buttonGo_Click={
#TODO: Place custom script here
$webview.Source = [System.Uri] $textbox1.Text;
}
$webview_SourceChanged={
$form1.Text = $webview.Source.AbsoluteUri;
}
# --End User Generated Script--
#----------------------------------------------
#region Generated Events
#----------------------------------------------
$Form_StateCorrection_Load=
{
#Correct the initial state of the form to prevent the .Net maximized form issue
$form1.WindowState = $InitialFormWindowState
}
$Form_Cleanup_FormClosed=
{
#Remove all event handlers from the controls
try
{
$buttonGo.remove_Click($buttonGo_Click)
$webview.remove_SourceChanged($webview_SourceChanged)
$form1.remove_Load($form1_Load)
$form1.remove_Load($Form_StateCorrection_Load)
$form1.remove_FormClosed($Form_Cleanup_FormClosed)
}
catch { Out-Null <# Prevent PSScriptAnalyzer warning #> }
}
#endregion Generated Events
#----------------------------------------------
#region Generated Form Code
#----------------------------------------------
$form1.SuspendLayout()
#
# form1
#
$form1.Controls.Add($buttonRefresh)
$form1.Controls.Add($buttonGo)
$form1.Controls.Add($textbox1)
$form1.Controls.Add($webview)
$form1.AutoScaleDimensions = New-Object System.Drawing.SizeF(6, 13)
$form1.AutoScaleMode = 'Font'
$form1.ClientSize = New-Object System.Drawing.Size(619, 413)
$form1.Name = 'form1'
$form1.Text = 'Form'
$form1.add_Load($form1_Load)
#
# buttonRefresh
#
$buttonRefresh.Location = New-Object System.Drawing.Point(13, 13)
$buttonRefresh.Name = 'buttonRefresh'
$buttonRefresh.Size = New-Object System.Drawing.Size(75, 23)
$buttonRefresh.TabIndex = 3
$buttonRefresh.Text = 'Refresh'
$buttonRefresh.UseVisualStyleBackColor = $True
#
# buttonGo
#
$buttonGo.Location = New-Object System.Drawing.Point(538, 9)
$buttonGo.Name = 'buttonGo'
$buttonGo.Size = New-Object System.Drawing.Size(75, 23)
$buttonGo.TabIndex = 2
$buttonGo.Text = 'Go'
$buttonGo.UseVisualStyleBackColor = $True
$buttonGo.add_Click($buttonGo_Click)
#
# textbox1
#
$textbox1.Location = New-Object System.Drawing.Point(96, 13)
$textbox1.Name = 'textbox1'
$textbox1.Size = New-Object System.Drawing.Size(435, 20)
$textbox1.TabIndex = 1
$textbox1.Text = 'https://www.bing.com'
#
# webview
#
$webview.Location = New-Object System.Drawing.Point(0, 49)
$webview.Name = 'webview'
$webview.Size = New-Object System.Drawing.Size(619, 364)
$webview.TabIndex = 0
$webview.ZoomFactor = 1
$webview.add_SourceChanged($webview_SourceChanged)
$form1.ResumeLayout()
#endregion Generated Form Code
#----------------------------------------------
#Save the initial state of the form
$InitialFormWindowState = $form1.WindowState
#Init the OnLoad event to correct the initial state of the form
$form1.add_Load($Form_StateCorrection_Load)
#Clean up the control events
$form1.add_FormClosed($Form_Cleanup_FormClosed)
#Show the Form
return $form1.ShowDialog()
} #End Function
#Call the form
Show-test-WebView2Control_psf | Out-Null
How can one save and load the properties of GUI objects from a CliXml file? Saving mwe is below, with failed attempt to load commented out. directly importing changes object type from System.Windows.Forms.Button to System.Management.Automation.PSObject. Attempts to loop over the saved properties failed.
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
Remove-Variable * -ErrorAction SilentlyContinue
$form = New-Object system.Windows.Forms.Form
$form.ClientSize = New-Object System.Drawing.Point(100,100)
$form.text = "Form"
$form.TopMost = $false
$form.FormBorderStyle = "FixedSingle"
$form.MaximizeBox = $false
$TestButton = New-Object system.Windows.Forms.Button
$TestButton.text = "Test"
$TestButton.width = 85
$TestButton.height = 30
$TestButton.location = New-Object System.Drawing.Point(0,0)
$TestButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12)
$TestButton.Enabled = $true
$path = "./vars/test.xml"
$TestButton | Export-CliXml $path
$TestButton.Enabled = $false
#$TestButton.GetType().FullName #For comp below
#$TestButton = Import-CliXml $path #Import the saved properties - intention is that this enables button again
#$TestButton.GetType() #The import changes the type. How to load saved properties and avoid this?
$form.Controls.Add($TestButton)
[void]$form.ShowDialog()
Why not "serialize" and save it as a PowerShell expression:
{
New-Object system.Windows.Forms.Button -Property #{
text = "Test"
width = 85
height = 30
location = New-Object System.Drawing.Point(0,0)
Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12)
Enabled = $true
}
} | Out-File .\Vars\Test.ps1
Note: using the outer curly brackets will validate the expression, but you might also just use (here) quotes
And then load it using dot-sourcing:
$TestButton = . .\Vars\Test.ps1
I have code that will start process in tray (task bar). After right click on tray icon it will show menu. After clicking on first menu item winform window starts. This winform shows status about the notepad process. My goal is to change the tray icon based on the status of notepad (if notepad is running then show online.ico otherwise show offline.ico). If I understand correct then my code is starting/stopping System.Windows.Forms.Timer every time winform window is opened/closed and I'm not sure if this is the best possible approach. My guess is that I need to somehow start timer "outside" of OnMenuItem1ClickEventFn so it can somehow reload *.ico files. Following script is heavily inspired by this site:
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
function Test-Notepad {
[bool](Get-Process -Name 'notepad' -ErrorAction SilentlyContinue)
}
function OnMenuItem1ClickEventFn () {
# Build Label object
$Label = New-Object System.Windows.Forms.Label
$Label.Name = "labelName"
$Label.AutoSize = $True
# Set and start timer
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 1000
$timer.Add_Tick({
if ($Label){
$Label.Text = if (Test-Notepad) { "Notepad is running" } else { "Notepad is NOT running" }
}
})
$timer.Start()
# Build Form object
$Form = New-Object System.Windows.Forms.Form
$Form.Text = "My Form"
$Form.Size = New-Object System.Drawing.Size(200,200)
$Form.StartPosition = "CenterScreen"
$Form.Topmost = $True
$Form.Add_Closing({ $timer.Dispose() }) # Dispose() also stops the timer.
$Form.Controls.Add($Label) # Add label to form
$form.ShowDialog()| Out-Null # Show the Form
}
function OnMenuItem4ClickEventFn () {
$Main_Tool_Icon.Visible = $false
$window.Close()
Stop-Process $pid
}
function create_taskbar_menu{
# Create menu items
$Main_Tool_Icon = New-Object System.Windows.Forms.NotifyIcon
$Main_Tool_Icon.Text = "Icon Text"
$Main_Tool_Icon.Icon = $icon
$Main_Tool_Icon.Visible = $true
$MenuItem1 = New-Object System.Windows.Forms.MenuItem
$MenuItem1.Text = "Menu Item 1"
$MenuItem2 = New-Object System.Windows.Forms.MenuItem
$MenuItem2.Text = "Menu Item 2"
$MenuItem3 = New-Object System.Windows.Forms.MenuItem
$MenuItem3.Text = "Menu Item 3"
$MenuItem4 = New-Object System.Windows.Forms.MenuItem
$MenuItem4.Text = "Exit"
# Add menu items to context menu
$contextmenu = New-Object System.Windows.Forms.ContextMenu
$Main_Tool_Icon.ContextMenu = $contextmenu
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem1)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem2)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem3)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem4)
$MenuItem4.add_Click({OnMenuItem4ClickEventFn})
$MenuItem1.add_Click({OnMenuItem1ClickEventFn})
}
$Current_Folder = split-path $MyInvocation.MyCommand.Path
# Add assemblies for WPF and Mahapps
[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('presentationframework') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('System.Drawing') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('WindowsFormsIntegration') | out-null
# [System.Reflection.Assembly]::LoadFrom("Current_Folder\assembly\MahApps.Metro.dll") | out-null
# Choose an icon to display in the systray
$icon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/online.ico")
# use this icon when notepad is not running
# $icon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/offline.ico")
create_taskbar_menu
# Make PowerShell Disappear - Thanks Chrissy
$windowcode = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
$asyncwindow = Add-Type -MemberDefinition $windowcode -name Win32ShowWindowAsync -namespace Win32Functions -PassThru
$null = $asyncwindow::ShowWindowAsync((Get-Process -PID $pid).MainWindowHandle, 0)
# Use a Garbage colection to reduce Memory RAM
# https://dmitrysotnikov.wordpress.com/2012/02/24/freeing-up-memory-in-powershell-using-garbage-collector/
# https://learn.microsoft.com/fr-fr/dotnet/api/system.gc.collect?view=netframework-4.7.2
[System.GC]::Collect()
# Create an application context for it to all run within - Thanks Chrissy
# This helps with responsiveness, especially when clicking Exit - Thanks Chrissy
$appContext = New-Object System.Windows.Forms.ApplicationContext
[void][System.Windows.Forms.Application]::Run($appContext)
EDITED: working solution based on #BACON answer
# Toggle following two lines
Set-StrictMode -Version Latest
# Set-StrictMode -Off
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
function Test-Notepad {
[bool](Get-Process -Name 'notepad' -ErrorAction SilentlyContinue)
}
function OnMenuItem1ClickEventFn () {
# Build Form object
$Form = New-Object System.Windows.Forms.Form
$Form.Text = "My Form"
$Form.Size = New-Object System.Drawing.Size(200,200)
$Form.StartPosition = "CenterScreen"
$Form.Topmost = $True
$Form.Controls.Add($Label) # Add label to form
$form.ShowDialog()| Out-Null # Show the Form
}
function OnMenuItem4ClickEventFn () {
$Main_Tool_Icon.Visible = $false
[System.Windows.Forms.Application]::Exit()
}
function create_taskbar_menu{
# Create menu items
$MenuItem1 = New-Object System.Windows.Forms.MenuItem
$MenuItem1.Text = "Menu Item 1"
$MenuItem2 = New-Object System.Windows.Forms.MenuItem
$MenuItem2.Text = "Menu Item 2"
$MenuItem3 = New-Object System.Windows.Forms.MenuItem
$MenuItem3.Text = "Menu Item 3"
$MenuItem4 = New-Object System.Windows.Forms.MenuItem
$MenuItem4.Text = "Exit"
# Add menu items to context menu
$contextmenu = New-Object System.Windows.Forms.ContextMenu
$Main_Tool_Icon.ContextMenu = $contextmenu
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem1)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem2)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem3)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem4)
$MenuItem4.add_Click({OnMenuItem4ClickEventFn})
$MenuItem1.add_Click({OnMenuItem1ClickEventFn})
}
$Current_Folder = split-path $MyInvocation.MyCommand.Path
# Add assemblies for WPF and Mahapps
[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('presentationframework') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('System.Drawing') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('WindowsFormsIntegration') | out-null
# [System.Reflection.Assembly]::LoadFrom("Current_Folder\assembly\MahApps.Metro.dll") | out-null
# Choose an icon to display in the systray
$onlineIcon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/online.ico")
# use this icon when notepad is not running
$offlineIcon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/offline.ico")
$Main_Tool_Icon = New-Object System.Windows.Forms.NotifyIcon
$Main_Tool_Icon.Text = "Icon Text"
$Main_Tool_Icon.Icon = if (Test-Notepad) { $onlineIcon } else { $offlineIcon }
$Main_Tool_Icon.Visible = $true
# Build Label object
$Label = New-Object System.Windows.Forms.Label
$Label.Name = "labelName"
$Label.AutoSize = $True
# Initialize the timer
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 1000
$timer.Add_Tick({
if ($Label){
$Label.Text, $Main_Tool_Icon.Icon = if (Test-Notepad) {
"Notepad is running", $onlineIcon
} else {
"Notepad is NOT running", $offlineIcon
}
}
})
$timer.Start()
create_taskbar_menu
# Make PowerShell Disappear - Thanks Chrissy
$windowcode = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
$asyncwindow = Add-Type -MemberDefinition $windowcode -name Win32ShowWindowAsync -namespace Win32Functions -PassThru
$null = $asyncwindow::ShowWindowAsync((Get-Process -PID $pid).MainWindowHandle, 0)
# Use a Garbage colection to reduce Memory RAM
# https://dmitrysotnikov.wordpress.com/2012/02/24/freeing-up-memory-in-powershell-using-garbage-collector/
# https://learn.microsoft.com/fr-fr/dotnet/api/system.gc.collect?view=netframework-4.7.2
[System.GC]::Collect()
# Create an application context for it to all run within - Thanks Chrissy
# This helps with responsiveness, especially when clicking Exit - Thanks Chrissy
$appContext = New-Object System.Windows.Forms.ApplicationContext
try
{
[System.Windows.Forms.Application]::Run($appContext)
}
finally
{
foreach ($component in $timer, $Main_Tool_Icon, $offlineIcon, $onlineIcon, $appContext)
{
# The following test returns $false if $component is
# $null, which is really what we're concerned about
if ($component -is [System.IDisposable])
{
$component.Dispose()
}
}
Stop-Process -Id $PID
}
You already have code to set the icon used by the NotifyIcon...
$Main_Tool_Icon.Icon = $icon
...and to define the icons to use...
# Choose an icon to display in the systray
$icon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/online.ico")
# use this icon when notepad is not running
# $icon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/offline.ico")
...and to periodically test if Notepad is running and respond appropriately...
$timer.Add_Tick({
if ($Label){
$Label.Text = if (Test-Notepad) { "Notepad is running" } else { "Notepad is NOT running" }
}
})
You just need to combine them with a couple additional tweaks...
Store each icon in its own variable with a descriptive name so its easy to switch between them.
$Main_Tool_Icon needs to be defined outside the scope of create_taskbar_menu so it can be accessed inside of OnMenuItem1ClickEventFn. I moved the creation and initialization to just before the call to create_taskbar_menu, but you could also initialize it inside of create_taskbar_menu or some other function.
That ends up looking like this...
# Choose an icon to display in the systray
$onlineIcon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/online.ico")
# use this icon when notepad is not running
$offlineIcon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/offline.ico")
$Main_Tool_Icon = New-Object System.Windows.Forms.NotifyIcon
$Main_Tool_Icon.Text = "Icon Text"
$Main_Tool_Icon.Icon = if (Test-Notepad) { $onlineIcon } else { $offlineIcon }
$Main_Tool_Icon.Visible = $true
create_taskbar_menu
...and this...
$timer.Add_Tick({
if ($Label){
# Change the text and icon with one test
$Label.Text, $Main_Tool_Icon.Icon = if (Test-Notepad) {
"Notepad is running", $onlineIcon
} else {
"Notepad is NOT running", $offlineIcon
}
}
})
You'll see that an icon is selected based on the result of Test-Notepad both when $Main_Tool_Icon is initialized and when the Tick event is raised.
As for disposing $timer when $Form is closing...
$Form.Add_Closing({ $timer.Dispose() })
...that is almost an appropriate place to do that, however...
The Closing event is raised essentially when the form has been requested to close; it provides an opportunity for that request to be canceled. The Closed event would be more appropriate because it is raised when the form has actually been closed.
The documentation states that both the Closing and Closed events are obsolete. Use the FormClosed event instead.
Also, in OnMenuItem4ClickEventFn you are calling $window.Close() even though $window is not defined; I think you meant $Form.Close(). An alternative that I think is a bit cleaner would be to have clicking the Exit menu item merely exit the Windows Forms application loop...
function OnMenuItem4ClickEventFn () {
$Main_Tool_Icon.Visible = $false
[System.Windows.Forms.Application]::Exit()
}
...and then you can put your cleanup/teardown code in a finally block at the end of the script...
try
{
# This call returns when [System.Windows.Forms.Application]::Exit() is called
[System.Windows.Forms.Application]::Run($appContext)
}
finally
{
# $timer would also have to be defined at the script scope
# (outside of OnMenuItem1ClickEventFn) for this to work
$timer.Dispose()
# $Form, $Label, $Main_Tool_Icon, $onlineIcon, etc. would all be candidates for disposal...
# Exit the entire PowerShell process
Stop-Process $pid
}
The following is complete code that incorporates the changes mentioned above and works for me...
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
function Test-Notepad {
[bool](Get-Process -Name 'notepad' -ErrorAction SilentlyContinue)
}
function OnMenuItem1ClickEventFn () {
$timer.Start()
# Build Form object
$Form = New-Object System.Windows.Forms.Form
$Form.Text = "My Form"
$Form.Size = New-Object System.Drawing.Size(200,200)
$Form.StartPosition = "CenterScreen"
$Form.Topmost = $True
$Form.Add_Closing({ $timer.Dispose() }) # Dispose() also stops the timer.
$Form.Controls.Add($Label) # Add label to form
$form.ShowDialog()| Out-Null # Show the Form
}
function OnMenuItem4ClickEventFn () {
$Main_Tool_Icon.Visible = $false
[System.Windows.Forms.Application]::Exit()
}
function create_taskbar_menu{
# Create menu items
$MenuItem1 = New-Object System.Windows.Forms.MenuItem
$MenuItem1.Text = "Menu Item 1"
$MenuItem2 = New-Object System.Windows.Forms.MenuItem
$MenuItem2.Text = "Menu Item 2"
$MenuItem3 = New-Object System.Windows.Forms.MenuItem
$MenuItem3.Text = "Menu Item 3"
$MenuItem4 = New-Object System.Windows.Forms.MenuItem
$MenuItem4.Text = "Exit"
# Add menu items to context menu
$contextmenu = New-Object System.Windows.Forms.ContextMenu
$Main_Tool_Icon.ContextMenu = $contextmenu
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem1)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem2)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem3)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($MenuItem4)
$MenuItem4.add_Click({OnMenuItem4ClickEventFn})
$MenuItem1.add_Click({OnMenuItem1ClickEventFn})
}
$Current_Folder = split-path $MyInvocation.MyCommand.Path
# Add assemblies for WPF and Mahapps
[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('presentationframework') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('System.Drawing') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('WindowsFormsIntegration') | out-null
# [System.Reflection.Assembly]::LoadFrom("Current_Folder\assembly\MahApps.Metro.dll") | out-null
# Choose an icon to display in the systray
$onlineIcon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/online.ico")
# use this icon when notepad is not running
$offlineIcon = [System.Drawing.Icon]::ExtractAssociatedIcon("$Current_Folder/icons/offline.ico")
$Main_Tool_Icon = New-Object System.Windows.Forms.NotifyIcon
$Main_Tool_Icon.Text = "Icon Text"
$Main_Tool_Icon.Icon = if (Test-Notepad) { $onlineIcon } else { $offlineIcon }
$Main_Tool_Icon.Visible = $true
# Build Label object
$Label = New-Object System.Windows.Forms.Label
$Label.Name = "labelName"
$Label.AutoSize = $True
# Initialize the timer
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 1000
$timer.Add_Tick({
if ($Label){
$Label.Text, $Main_Tool_Icon.Icon = if (Test-Notepad) {
"Notepad is running", $onlineIcon
} else {
"Notepad is NOT running", $offlineIcon
}
}
})
create_taskbar_menu
# Make PowerShell Disappear - Thanks Chrissy
$windowcode = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
$asyncwindow = Add-Type -MemberDefinition $windowcode -name Win32ShowWindowAsync -namespace Win32Functions -PassThru
$null = $asyncwindow::ShowWindowAsync((Get-Process -PID $pid).MainWindowHandle, 0)
# Use a Garbage colection to reduce Memory RAM
# https://dmitrysotnikov.wordpress.com/2012/02/24/freeing-up-memory-in-powershell-using-garbage-collector/
# https://learn.microsoft.com/fr-fr/dotnet/api/system.gc.collect?view=netframework-4.7.2
[System.GC]::Collect()
# Create an application context for it to all run within - Thanks Chrissy
# This helps with responsiveness, especially when clicking Exit - Thanks Chrissy
$appContext = New-Object System.Windows.Forms.ApplicationContext
try
{
[System.Windows.Forms.Application]::Run($appContext)
}
finally
{
foreach ($component in $timer, $form, $Main_Tool_Icon, $offlineIcon, $onlineIcon, $appContext)
{
# The following test returns $false if $component is
# $null, which is really what we're concerned about
if ($component -is [System.IDisposable])
{
$component.Dispose()
}
}
Stop-Process -Id $PID
}
I'm getting an intermittent error with this method of changing a forms text label according to the selected item in a Listview box.
Example code as below, changing the entry will intermittently give:
Cannot index into a null array.
At C:\temp\test.ps1:62 char:5
+ $SelectedPath.Text = $VMsListBox.SelectedItems.SubItems[1].Text
# Import namespaces
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Demo'
$form.Size = '580,545'
$form.StartPosition = 'CenterScreen'
$form.FormBorderStyle = 'FixedSingle'
$form.MaximizeBox = $false
# Listview box to display found open files
$VMsListBox = New-Object System.Windows.Forms.ListView
$VMsListBox.View = [System.Windows.Forms.View]::Details
$VMsListBox.Location = '15,120'
$VMsListBox.size = '435,10'
$VMsListBox.Height = 250
$VMsListBox.Columns.Add('Name') | Out-Null
$VMsListBox.Columns.Add('Path') | Out-Null
$VMsListBox.FullRowSelect = $true
$VMsListBox.MultiSelect = $false
# Selected file label
$SelectedFnameLbl = New-Object System.Windows.Forms.Label
$SelectedFnameLbl.Location = '10,25'
$SelectedFnameLbl.Size = '80,19'
$SelectedFnameLbl.Text = 'File Name:'
# Selected file name
$SelectedFname = New-Object System.Windows.Forms.Label
$SelectedFname.Location = '100,25'
$SelectedFname.Size = '300,19'
$SelectedFname.Text = 'n/a'
$SelectedFname.AutoEllipsis = $true
# Path Label
$SelectedFileLbl = New-Object System.Windows.Forms.Label
$SelectedFileLbl.Location = '10,45'
$SelectedFileLbl.Size = '80,19'
$SelectedFileLbl.Text = 'File Path:'
# Selected filepath
$SelectedPath = New-Object System.Windows.Forms.Label
$SelectedPath.Location = '100,45'
$SelectedPath.Size = '300,19'
$SelectedPath.Text = 'n/a'
$SelectedPath.AutoEllipsis = $true
$form.Controls.AddRange(#($VMsListBox,$SelectedFileLbl,$SelectedPath,$SelectedFnameLbl,$SelectedFname))
# Populate ListView
$Files = Get-ChildItem -Path 'c:\temp' -File
$Files | ForEach-Object {
$Entry = New-Object System.Windows.Forms.ListViewItem($_.Name) -ErrorAction Stop
$Entry.SubItems.Add($_.FullName) | Out-Null
$VMsListBox.Items.Add($Entry) | Out-Null
}
$VMsListBox_SelectedIndexChanged={
$SelectedFname.Text = $VMsListBox.SelectedItems.Text
$SelectedPath.Text = $VMsListBox.SelectedItems.SubItems[1].Text
Write-Host "Entry changed"
}
$VMsListBox.Add_SelectedIndexChanged($VMsListBox_SelectedIndexChanged)
# Show form
$form.ShowDialog() | Out-Null
$form.Dispose()
Can anyone point me where I'm going wrong please? Or is there a better way of doing this
Ok, I found this
Apparently retained legacy behaviour and the fix is to check for null:
$VMsListBox_SelectedIndexChanged={
If($VMsListbox.SelectedItems -ne $Null){
$SelectedFname.Text = $VMsListBox.SelectedItems.Text
$SelectedPath.Text = $VMsListBox.SelectedItems.SubItems[1].Text
Write-Host "Entry changed"
}
}
i am unable to figure out the mistake that i am doing.The following script will call a bunch of batch files and while the batch files are doing there job, the progress will be shown in a progress bar along with the name of the script. What i want to achieve is to display a custom message during the installation process. I am not able to find out the mistake, any help is deeply appreciated.
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") | Out-Null
Set-Location $PSScriptRoot
#Call scripts for installation
$ScriptsHome = Get-Item '.\test\forPS\*'
#Define Form
# Init Form
$Form = New-Object System.Windows.Forms.Form
$Form.width = 1000
$Form.height = 200
$Form.Text = "** Installation in Progress-PLEASE DO NOT CLOSE THIS WINDOW**"
$Form.Font = New-Object System.Drawing.Font("Times New Roman" ,12, [System.Drawing.FontStyle]::Regular)
$Form.MinimizeBox = $False
$Form.MaximizeBox = $False
$Form.WindowState = "Normal"
$Form.StartPosition = "CenterScreen"
$Form.Opacity = .8
$Form.BackColor = "Gray"
#Define ICON for Form
$Icon = New-Object System.Drawing.Graphics (".\ICON.jpg")
$Form.Icon = $Icon
# Init ProgressBar
$ProgressBar = New-Object System.Windows.Forms.ProgressBar
$ProgressBar.Maximum = $ScriptsHome.Count
$ProgressBar.Minimum = 0
$ProgressBar.Location = new-object System.Drawing.Size(10,70)
$ProgressBar.size = new-object System.Drawing.Size(967,10)
$Form.Controls.Add($ProgressBar)
$Form.Controls.Add($Messages)
#Running Script Name
$Label = New-Object System.Windows.Forms.Label
$Label.AutoSize = $true
$Label.Location = New-Object System.Drawing.Point(10,50)
$Form.Controls.Add($Label)
#Define Array messages
#Array
$Messages = #("Preparing to install patch set..Preparing to stop all related processes",
"Upgrading the application",
"Copying the missing folder",
"Applying the patch",
"Starting all previously stopped Services",
"Checkcing healthyness of the system after the installation this is may take up to half an hour...",
"Rebooting Server"
)
$Messages = New-Object System.Windows.Forms.Label
$Messages.AutoSize = $true
$Messages.Location = New-Object System.Drawing.Point(10,50)
$Form.Controls.Add($Messages)
# Add_Shown action
$ShownFormAction = {
$Form.Activate()
foreach ($script in $ScriptsHome) {
$ProgressBar.Increment(1)
#$Messages.Text = $Messages[$Messages]
$Label.Text = "$($script.Name)"
Start-Process $script.FullName -Wait -WindowStyle Hidden
}
$Form.Dispose()
}
$Form.Add_Shown($ShownFormAction)
# Show Form
$Form.ShowDialog()
Thanks in advance.
You're reusing the same variable name for the list of messages and the label showing the message itself:
$Messages = #("Preparing to install patch set..Preparing to stop all related processes",
"Upgrading the application",
"Copying the missing folder",
"Applying the patch",
"Starting all previously stopped Services",
"Checkcing healthyness of the system after the installation this is may take up to half an hour...",
"Rebooting Server"
)
$Messages = New-Object System.Windows.Forms.Label
$Messages.AutoSize = $true
$Messages.Location = New-Object System.Drawing.Point(10,50)
Rename one of them (ie. use $MessageLabel for the label):
$MessageLabel = New-Object System.Windows.Forms.Label
$MessageLabel.AutoSize = $true
$MessageLabel.Location = New-Object System.Drawing.Point(10,50)
Since you increment the ProgressBar by 1 every step, you can reuse the progress bar value to index into the $Messages array:
foreach ($script in $ScriptsHome) {
$ProgressBar.Increment(1)
$MessageLabel.Text = $Messages[$ProgressBar.Value - 1]
$Label.Text = "$($script.Name)"
Start-Process $script.FullName -Wait -WindowStyle Hidden
}