Populate ComboBox2 depending on ComboBox1 selection - powershell

Few days of searching and trying multiple options, but nothing is working actually. With the previous code posted.
I've imported all my objects from XAML (all set in variables), I don't know if that would be a problem for that. I don't think since everything else seems to work properly.
I just want my Tab2CBB2 to show values depending on the selection of Tab1CBB1. Anyone could help ? (I haven't paste the entire code, neither the paths but you can probably help me with that). Note that those are two of my multiple tries. Thanks
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$xaml = #" ...
"#
#Read XAML (Parse it)
$reader=(New-Object System.Xml.XmlNodeReader $XAML)
$Window=[Windows.Markup.XamlReader]::Load( $reader )
$listQA14 = Get-ChildItem $pathQA14 -name
$listQA15 = Get-ChildItem $pathQA15 -name
$listDBQA = Get-ChildItem $pathDBQA -name
$Tab1CBB1_SelectedIndexChanged= {
$Tab1CBB2.Items.Clear() # Clear the list
$Tab1CBB2.Text = $null # Clear the current entry
Switch ($Tab1CBB1.Text) {
'QA14' {
$ListQA14 | ForEach { $Tab1CBB2.Items.Add($_) }
}
'QA15' {
$ListQA15 | ForEach { $Tab1CBB2.Items.Add($_) }
}
'ARIELDBQA' {
$ListDBQA | ForEach { $Tab1CBB2.Items.Add($_) }
}
}
}
$Tab1CBB1.add_SelectedIndexChanged($Tab1CBB1_SelectedIndexChanged)
#Displays the Window
$Window.ShowDialog() | out-null
Here is another option tried :
#ComboBox1------
$ItemsCBB1 = #('ARIELDBQA','QA14','QA15')
foreach ($Item in $ItemsCBB1) {
$Tab1CBB1.Items.Add($Item)
}
#ComboBox2-------
if ($Tab1CBB1.SelectedItem -eq 'QA14') {
$Tab1CBB2.Items.Clear()
foreach ($Serie in $listQA14) {
$Tab1CBB2.Items.Add($Serie)
}
}
elseif ($Tab1CBB1.SelectedItem -eq 'QA15') {
$Tab1CBB2.Items.Clear()
foreach ($Serie in $listQA15) {
$Tab1CBB2.Items.Add($Serie)
}
}
elseif ($Tab1CBB1.SelectedItem -eq 'listDBQA') {
$Tab1CBB2.Items.Clear()
foreach ($Serie in $listDBQA) {
$Tab1CBB2.Items.Add($Serie)
}
}
Tried this also : $Selection = $Tab1CBB1.SelectedItem.ToString()
Variable $selection put after 'if', but not working
Note that when I indicate what the current selection would be, it is working properly. The problem seems to come from 'recording' the selection a the time of clicking... Thnaks !

Basically, all you have to do is inside the $Tab1CBB1_SelectedIndexChanged scriptblock use the various lists with script scoping.
Without that, the variables are unknown inside the script block.
$Tab1CBB1_SelectedIndexChanged = {
$Tab1CBB2.Items.Clear() # Clear the list
$Tab1CBB2.Text = $null # Clear the current entry
switch ($Tab1CBB1.Text) {
'QA14' { $script:ListQA14 | ForEach-Object { $Tab1CBB2.Items.Add($_) } }
'QA15' { $script:ListQA15 | ForEach-Object { $Tab1CBB2.Items.Add($_) } }
'ARIELDBQA' { $script:ListDBQA | ForEach-Object { $Tab1CBB2.Items.Add($_) } }
}
}
Another method could be to dynamically get the items to enter in the combobox, especially since these are file lists and can change while your form is being used:
$Tab1CBB1_SelectedIndexChanged = {
$Tab1CBB2.Items.Clear() # Clear the list
$Tab1CBB2.Text = $null # Clear the current entry
switch ($Tab1CBB1.Text) {
'QA14' { $path = $script:pathQA14 ; break }
'QA15' { $path = $script:pathQA15 ; break }
'ARIELDBQA' { $path = $script:pathDBQA }
}
Get-ChildItem -Path $path -Name | ForEach-Object { $Tab1CBB2.Items.Add($_) }
}

A simple example of what you seem to be after is something like this.
### Powershell populate ComboBox 2 based on ComboBox 1 Selection
Add-type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$form1 = New-Object 'System.Windows.Forms.Form'
$labelDoubleClickOnAnyItem = New-Object 'System.Windows.Forms.Label'
$listbox2 = New-Object 'System.Windows.Forms.ListBox'
$listbox1 = New-Object 'System.Windows.Forms.ListBox'
$buttonOK = New-Object 'System.Windows.Forms.Button'
$form1_Load={
$items=1..9 |
ForEach-Object {"List item $_"}
$listbox1.Items.AddRange($items)
}
$listbox1_MouseDoubleClick=[System.Windows.Forms.MouseEventHandler]{
$listbox2.Items.Add($listbox1.SelectedItem)
$listbox1.Items.Remove($listbox1.SelectedItem)
}
$listbox2_MouseDoubleClick=[System.Windows.Forms.MouseEventHandler]{
$listbox1.Items.Add($listbox2.SelectedItem)
$listbox2.Items.Remove($listbox2.SelectedItem)
}
$form1.Controls.Add($labelDoubleClickOnAnyItem)
$form1.Controls.Add($listbox2)
$form1.Controls.Add($listbox1)
$form1.Controls.Add($buttonOK)
$form1.AcceptButton = $buttonOK
$form1.ClientSize = '378, 388'
$form1.FormBorderStyle = 'FixedDialog'
$form1.MaximizeBox = $False
$form1.MinimizeBox = $False
$form1.Name = 'form1'
$form1.StartPosition = 'CenterScreen'
$form1.Text = 'Form'
$form1.add_Load($form1_Load)
$labelDoubleClickOnAnyItem.Font = 'Microsoft Sans Serif, 9.75pt, style=Bold, Italic'
$labelDoubleClickOnAnyItem.Location = '13, 13'
$labelDoubleClickOnAnyItem.Name = 'labelDoubleClickOnAnyItem'
$labelDoubleClickOnAnyItem.Size = '353, 41'
$labelDoubleClickOnAnyItem.TabIndex = 3
$labelDoubleClickOnAnyItem.Text = 'Double click on any item to move it from one box to the other.'
$listbox2.FormattingEnabled = $True
$listbox2.Location = '200, 67'
$listbox2.Name = 'listbox2'
$listbox2.Size = '166, 277'
$listbox2.Sorted = $True
$listbox2.TabIndex = 2
$listbox2.add_MouseDoubleClick($listbox2_MouseDoubleClick)
$listbox1.FormattingEnabled = $True
$listbox1.Location = '12, 67'
$listbox1.Name = 'listbox1'
$listbox1.Size = '167, 277'
$listbox1.Sorted = $True
$listbox1.TabIndex = 1
$listbox1.add_MouseDoubleClick($listbox1_MouseDoubleClick)
$buttonOK.Anchor = 'Bottom, Right'
$buttonOK.DialogResult = 'OK'
$buttonOK.Location = '291, 353'
$buttonOK.Name = 'buttonOK'
$buttonOK.Size = '75, 23'
$buttonOK.TabIndex = 0
$buttonOK.Text = '&OK'
$form1.ShowDialog()
Or even this using just the ComboBox elements
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = New-Object System.Drawing.Point(498,459)
$Form.text = 'Form'
$Form.TopMost = $false
$Form.StartPosition = 'CenterScreen'
$ComboBox1 = New-Object system.Windows.Forms.ComboBox
$ComboBox1.text = ''
$ComboBox1.width = 216
$ComboBox1.height = 75
#('ComboxItem1','ComboxItem2','ComboxItem3') |
ForEach-Object {[void] $ComboBox1.Items.Add($_)}
$ComboBox1.location = New-Object System.Drawing.Point(31,41)
$ComboBox1.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$ComboBox2 = New-Object system.Windows.Forms.ComboBox
$ComboBox2.text = ''
$ComboBox2.width = 213
$ComboBox2.height = 38
$ComboBox2.location = New-Object System.Drawing.Point(32,73)
$ComboBox2.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
Function Add-ItemToComboBox2
{
$ComboBox2.Items.Add($ComboBox1.SelectedItem)
$ComboBox2.Refresh()
}
$Form.controls.AddRange(
#(
$ComboBox1,
$ComboBox2
)
)
$ComboBox1.Add_SelectedIndexChanged({ Add-ItemToComboBox2 })
[void]$Form.ShowDialog()
You have not stated what UX/UI tool you are using to design and implement your
GUI. Yet, hard coding is also just a challenge. So consider using one of these for your UX/UI effort. Your UX/UI should just work, whether you have PowerShell code behind it (or any other language). Your PowerShell code should just work, regardless of the UX/UI that may be implemented. Your UX/UI code should be separate from your ops code. You call your ops code from the UX/UI.
Free:
https://poshgui.com
https://www.youtube.com/results?search_query=poshgui
https://visualstudio.microsoft.com/vs/community
Be sure to thoroughly read the licensing agreement for VS.
https://www.youtube.com/results?search_query=vs+2019+community+winforms
Purchase
https://ironmansoftware.com/psscriptpad
https://www.youtube.com/results?search_query=psscriptpad
https://ironmansoftware.com/powershell-pro-tools
https://www.youtube.com/results?search_query=powershell-pro-tools
https://www.sapien.com/software/powershell_studio
https://www.youtube.com/results?search_query=Sapien%27s+powershell+studio++winforms
https://visualstudio.microsoft.com/vs
https://www.youtube.com/results?search_query=vs+2019+community+winforms

Related

Powershell TextBox automatically update file contents

Anyone can help me fix my code below. I am displaying a log file in the textbox, but the textbox wont update automatically when the log file is updated. I tried using a timer but it wont work since it will refresh the whole form and the input file name will be reset too, so it will return a null value. Please go to "viewComo" function.
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
[System.Windows.Forms.Application]::EnableVisualStyles()
#2. Instantiate a Form Object
#-----------------------------------------------------------------------------------------
$Form_MAIN = New-Object system.Windows.Forms.Form;
$Form_MAIN.ClientSize = New-Object System.Drawing.Point(1024,1000);
$Form_MAIN.StartPosition = "manual";
$Form_MAIN.Location = New-Object System.Drawing.Size(600,300);
$Form_MAIN.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#6699CC");
$Form_MAIN.text = "DataCenter Operation Applications";
$Form_MAIN.TopMost = $false;
$Form_MAIN.AutoSize = $false;
$Form_MAIN.AutoScale = $false;
$Form_MAIN.MaximizeBox = $false;
#3. Build the Form Components
#-----------------------------------------------------------------------------------------
$MainMenu = New-Object System.Windows.Forms.MenuStrip;
$Menu_File = New-Object System.Windows.Forms.ToolStripMenuItem("File");
$SubMenu_Open = New-Object System.Windows.Forms.ToolStripMenuItem("Open");
$SubMenu_Save = New-Object System.Windows.Forms.ToolStripMenuItem("Save");
$SubMenu_Exit = New-Object System.Windows.Forms.ToolStripMenuItem("Exit");
$Menu_DCApp = New-Object System.Windows.Forms.ToolStripMenuItem("DCApp");
$SubMenu_Reports_Checker = New-Object System.Windows.Forms.ToolStripMenuItem("Reports_Checker");
$SubMenu_Reports_Transfer = New-Object System.Windows.Forms.ToolStripMenuItem("Reports_Transfer");
$SubMenu_COB_Monitoring = New-Object System.Windows.Forms.ToolStripMenuItem("COB_Monitoring");
$SubMenu_AD_AccountLock_Checker = New-Object System.Windows.Forms.ToolStripMenuItem("AD_AccountLock_Checker");
$Menu_Help = New-Object System.Windows.Forms.ToolStripMenuItem("Help");
$Menu_About = New-Object System.Windows.Forms.ToolStripMenuItem("About");
$GB_Top = New-Object system.Windows.Forms.Groupbox;
$GB_Top.height = 330;
$GB_Top.width = 990;
$GB_Top.text = "Como Checker, Please input the Service name below";
$GB_Top.location = New-Object System.Drawing.Point(15,30);
$GB_Top.Font = New-Object System.Drawing.Font('Calibri',12);
$TB_Top_Input = New-Object system.Windows.Forms.TextBox;
$TB_Top_Input.multiline = $true;
$TB_Top_Input.width = 840;
$TB_Top_Input.height = 30;
$TB_Top_Input.location = New-Object System.Drawing.Point(10,25);
$TB_Top_Input.Font = New-Object System.Drawing.Font('Calibri',10);
$TB_Top_Button = New-Object system.Windows.Forms.Button;
$TB_Top_Button.Text = "Search COMO";
$TB_Top_Button.width = 124;
$TB_Top_Button.height = 30;
$TB_Top_Button.location = New-Object System.Drawing.Point(850,25);
$TB_Top_Button.Add_Click({checkComoFiles})
$RB_LabelQuery = New-Object System.Windows.Forms.Label;
$RB_LabelQuery.Text = "Choose the Range to query:";
$RB_LabelQuery.AutoSize = $true;
$RB_LabelQuery.width = 104;
$RB_LabelQuery.height = 10;
$RB_LabelQuery.location = New-Object System.Drawing.Point(10,55);
$RB_LabelQuery.Font = New-Object System.Drawing.Font('Callibri',9);
$RB_Option1 = New-Object system.Windows.Forms.RadioButton;
$RB_Option1.Text = "Last 1 Hour";
$RB_Option1.AutoSize = $true;
$RB_Option1.width = 104;
$RB_Option1.height = 10;
$RB_Option1.location = New-Object System.Drawing.Point(170,55);
$RB_Option1.Font = New-Object System.Drawing.Font('Callibri',9);
$RB_Option2 = New-Object system.Windows.Forms.RadioButton;
$RB_Option2.Text = "Last 3 Hours";
$RB_Option2.AutoSize = $true;
$RB_Option2.width = 104;
$RB_Option2.height = 10;
$RB_Option2.location = New-Object System.Drawing.Point(270,55);
$RB_Option2.Font = New-Object System.Drawing.Font('Callibri',9);
$RB_Option3 = New-Object system.Windows.Forms.RadioButton;
$RB_Option3.Text = "Last 24 Hours";
$RB_Option3.AutoSize = $true;
$RB_Option3.width = 104;
$RB_Option3.height = 10;
$RB_Option3.location = New-Object System.Drawing.Point(360,55);
$RB_Option3.Font = New-Object System.Drawing.Font('Callibri',9);
$RB_LabelOutput = New-Object System.Windows.Forms.Label;
$RB_LabelOutput.Text = "Result:";
$RB_LabelOutput.AutoSize = $true;
$RB_LabelOutput.width = 104;
$RB_LabelOutput.height = 10;
$RB_LabelOutput.location = New-Object System.Drawing.Point(10,85);
$RB_LabelOutput.Font = New-Object System.Drawing.Font('Calibri',12);
#$TB_Top_Output = New-Object system.Windows.Forms.TextBox;
#$TB_Top_Output.multiline = $true;
#$TB_Top_Output.width = 970;
#$TB_Top_Output.height = 200;
#$TB_Top_Output.location = New-Object System.Drawing.Point(10,110);
$dataGridView = New-Object System.Windows.Forms.DataGridView
$dataGridView.Size=New-Object System.Drawing.Size(970,200)
$dataGridView.Location = New-Object System.Drawing.Point(10,110);
$dataGridView.BackgroundColor = "White";
#$form.Controls.Add($dataGridView)
$dataGridView.ColumnCount = 1
$dataGridView.ColumnHeadersVisible = $true
$dataGridView.Columns[0].Name = "Como Name"
$datagridview.Columns[0].Width = 300;
$datagridview.Font = New-Object System.Drawing.Font('Courier New',9);
$datagridview.Add_CellMouseDoubleClick({viewComoGrid})
$GB_Bottom = New-Object system.Windows.Forms.Groupbox;
$GB_Bottom.height = 600;
$GB_Bottom.width = 990;
$GB_Bottom.text = "View COMO";
$GB_Bottom.location = New-Object System.Drawing.Point(15,380);
$GB_Bottom.Font = New-Object System.Drawing.Font('Calibri',12);
$TB_Bottom_Input = New-Object system.Windows.Forms.TextBox;
$TB_Bottom_Input.multiline = $true;
$TB_Bottom_Input.width = 840;
$TB_Bottom_Input.height = 30;
$TB_Bottom_Input.location = New-Object System.Drawing.Point(10,20);
$TB_Bottom_Input.Font = New-Object System.Drawing.Font('Calibri',10);
$TB_Bottom_Button = New-Object system.Windows.Forms.Button;
$TB_Bottom_Button.Text = "View COMO";
$TB_Bottom_Button.width = 124;
$TB_Bottom_Button.height = 30;
$TB_Bottom_Button.location = New-Object System.Drawing.Point(850,20);
$TB_Bottom_Button.Font = New-Object System.Drawing.Font('Calibri',10);
$TB_Bottom_Button.Add_Click({viewComo})
$RB_LabelOutput_Bottom = New-Object System.Windows.Forms.Label;
$RB_LabelOutput_Bottom.Text = "Result:";
$RB_LabelOutput_Bottom.AutoSize = $true;
$RB_LabelOutput_Bottom.width = 104;
$RB_LabelOutput_Bottom.height = 10;
$RB_LabelOutput_Bottom.location = New-Object System.Drawing.Point(10,55);
$RB_LabelOutput_Bottom.Font = New-Object System.Drawing.Font('Calibri',12);
$TB_Top_Output_Bottom = New-Object system.Windows.Forms.TextBox;
$TB_Top_Output_Bottom.multiline = $true;
$TB_Top_Output_Bottom.size = New-Object System.Drawing.Size(970,510)
$TB_Top_Output_Bottom.ReadOnly = $True
#$TB_Top_Output_Bottom.width = 970;
#$TB_Top_Output_Bottom.height = 510;
$TB_Top_Output_Bottom.location = New-Object System.Drawing.Size(10,80);
$TB_Top_Output_Bottom.ScrollBars = 'Both'
$TB_Top_Output_Bottom.Font = New-Object System.Drawing.Font('Courier New',9);
#4. Add the Components to the Group Boxes
#-----------------------------------------------------------------------------------------
$GB_Top.controls.AddRange(#($RB_LabelQuery, $RB_Option1,$RB_Option2,$RB_Option3,$TB_Top_Input, $TB_Top_Button, $RB_LabelOutput, $dataGridView));
$GB_Bottom.controls.AddRange(#($L_Bottom_Output,$B_Enter, $TB_Bottom_Input, $TB_Bottom_Button, $RB_LabelOutput_Bottom, $TB_Top_Output_Bottom ));
#4. Add the Components to the Form
#-----------------------------------------------------------------------------------------
$Menu_File.DropDownItems.Add($SubMenu_Open);
$Menu_File.DropDownItems.Add($SubMenu_Save);
$Menu_File.DropDownItems.Add($SubMenu_Exit);
$Menu_DCApp.DropDownItems.Add($SubMenu_Reports_Checker);
$Menu_DCApp.DropDownItems.Add($SubMenu_Reports_Transfer);
$Menu_DCApp.DropDownItems.Add($SubMenu_COB_Monitoring);
$Menu_DCApp.DropDownItems.Add($SubMenu_AD_AccountLock_Checker);
$MainMenu.Items.Add($Menu_File);
$MainMenu.Items.Add($Menu_DCApp);
$MainMenu.Items.Add($Menu_Help);
$MainMenu.Items.Add($Menu_About);
$Form_MAIN.controls.AddRange(#($MainMenu,$TB_Output));
#4.1 Add the Group Boxes to the Form
#-----------------------------------------------------------------------------------------
$Form_MAIN.controls.AddRange(#($GB_Top,$GB_Bottom));
#5. Code the Event Handlers
#-----------------------------------------------------------------------------------------
$SubMenu_Open.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n Open File."; })
$SubMenu_Save.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n Save File."; })
$SubMenu_Exit.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n Exit Program."; })
$SubMenu_Reports_Checker.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n Reports_Checker"; })
$SubMenu_Reports_Transfer.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n Reports_Transfer"; })
$SubMenu_COB_Monitoring.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n COB_Monitoring"; })
$SubMenu_AD_AccountLock_Checker.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n AD_AccountLock_Checker"; })
$Menu_DCApp.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n Options!"; })
$Menu_Help.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n Help me!"; })
$Menu_About.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n All about ME."; })
function checkComoFiles {
$servicename= $TB_Top_Input.Text
$dataGridView.Rows.Clear()
$TB_Top_Input.Clear()
if ($RB_Option1.Checked) {
$hours = 1
$ResultQuery = Get-ChildItem -Path C:\Users\user\Downloads\ -File | Where-Object {($_.LastWriteTime - gt (Get-Date (Get-Date).AddHours(-$hours))) } |
ForEach-Object { $_ | Select-String -List -Pattern $servicename -SimpleMatch |
Select-Object -first 1 -ExpandProperty FileName}
}
if ($RB_Option2.Checked) {
$hours = 3
$ResultQuery = Get-ChildItem -Path C:\Users\user\Downloads\ -File | Where-Object {($_.LastWriteTime -gt (Get-Date (Get-Date).AddHours(-$hours))) } |
ForEach-Object { $_ | Select-String -List -Pattern $servicename -SimpleMatch |
Select-Object -first 1 -ExpandProperty FileName}
}
if ($RB_Option3.Checked) {
$hours = 24
$ResultQuery = Get-ChildItem -Path C:\Users\user\Downloads\ -File | Where-Object {($_.LastWriteTime -gt (Get-Date (Get-Date).AddHours(-$hours))) } |
ForEach-Object { $_ | Select-String -List -Pattern $servicename -SimpleMatch |
Select-Object -first 1 -ExpandProperty FileName}
}
$dataGridView.Columns[0].Name = $servicename
$rows = #($ResultQuery)
foreach ($row in $rows)
{
$dataGridView.Rows.Add($row)
}
}
Function viewComo {
$comoName = $TB_Bottom_Input.Text
$openComolive = Get-Content C:\Users\user\Downloads\$comoName
$TB_Top_Output_Bottom.Lines = $openComolive
$TB_Bottom_Input.Clear()
}
Function viewComoGrid {
$rowIndex = $datagridview.CurrentRow.Index
$columnIndex = $datagridview.CurrentCell.ColumnIndex
#Write-Host $rowIndex
#Write-Host $columnIndex
#Write-Host $datagridview.Rows[$rowIndex].Cells[0].value
#Write-Host $datagridview.Rows[$rowIndex].Cells[$columnIndex].value
$comoNameGrid = $datagridview.Rows[$rowIndex].Cells[0].value
$openComoliveGrid = Get-Content C:\Users\user\Downloads\$comoNameGrid
$TB_Top_Output_Bottom.Lines = $openComoliveGrid
$TB_Bottom_Input.Clear()
}
#6. Display Form
#-----------------------------------------------------------------------------------------
$Form_Main.ShowDialog();
Im trying to use below timer in my above code but it wont work since the whole form will be refresh
$timer = new-object Windows.Forms.Timer
$timer.Interval=10000
$timer.add_Tick({$TB_Top_Output_Bottom.Lines = Get-Content C:\Users\user\Downloads\$logname | Out-String; $TB_Top_Output_Bottom.Refresh()})
$timer.Start()
Original Version:
This isn't exactly intended to be an answer!
I think mklement0's answer is probably a safer way of getting the job done. But I remember reading about FileSystemWatcher some years ago, and having never used it before, wanted to give it a try.
Found this C# FileSystemWatcher answer, and figured out how to recreate the work in PowerShell.
Found this interesting use of SynchronizingObject for System.Timers.Timer that appears to allow Timer's Elapsed event to to run in the same thread as a control or form, and discovered that FileSystemWatcher also has a SynchronizingObject Property.
This seems to work flawlessly when editing and saving MyLogFile.TXT with NotePad.exe, but when I load MyLogFile.TXT in VSCode, the script either crashes or stops working. I think VSCode is locking the file and preventing the script from reading it, but I'm not really sure.
I would like to stress that this is an experiment, and this is outside my experience, use with with caution.
Basic code for setting up a Form for testing FileSystemWatcher:
using namespace System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms
[Application]::EnableVisualStyles()
$FilePathToWatch = $PSScriptRoot
$FileNameToWatch = "MyLogFile.TXT"
$FilePathNameToWatch = Join-Path -Path $FilePathToWatch -ChildPath $FileNameToWatch
$Form_MAIN = [Form]#{
AutoSize = $false
AutoScale = $false
BackColor = '0x6699CC'
ClientSize = "1024,1000"
Location = "600,300"
MaximizeBox = $false
StartPosition = "manual"
Text = "DataCenter Operation Applications"
Topmost = $true
}
$TextBox_Output = [TextBox]#{
Anchor = 'Top, Left, Bottom, Right'
Location = '12, 12'
Multiline = $true
Name = 'TextBox_Output'
Size = "$($Form_MAIN.ClientSize.Width - 24), $($Form_MAIN.ClientSize.Height - 24)"
Text = Get-Content -Raw $FilePathNameToWatch
}
$Form_Main.Controls.Add($TextBox_Output)
Code for setting up FileSystemWatcher, ending with ShowDialog() to open the form, $watcher.Dispose() to, as mklement0 pointed out, to stop $watcher from continuing to fire after form closes.:
[IO.FileSystemWatcher]$watcher = [IO.FileSystemWatcher]#{
Path = $FilePathToWatch
NotifyFilter = [IO.NotifyFilters]::LastWrite
Filter = $FileNameToWatch
SynchronizingObject = $TextBox_Output
}
$watcher.Add_Changed({
$TextBox_Output.Text = Get-Content -Raw $FilePathNameToWatch
})
$watcher.EnableRaisingEvents = $true
$null = $Form_Main.ShowDialog()
$watcher.Dispose()
UPDATED Version:
Walk through of changes from above code, full code listed in order in following sections:
Add function GetLogFileContent for safely reading the file, or returning an empty string when the file doesn't exist.
using namespace System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms
[Application]::EnableVisualStyles()
$FilePathToWatch = $PSScriptRoot
$FileNameToWatch = "MyLogFile.TXT"
$FilePathNameToWatch = Join-Path -Path $FilePathToWatch -ChildPath $FileNameToWatch
function GetLogFileContent {
param ( [Parameter(Mandatory = $true, Position = 0)][string]$FilePathName )
if(Test-Path -PathType Leaf -LiteralPath $FilePathName) { Get-Content -Raw $FilePathName } else { '' }
}
Added code for Form's FormClosing event to shutdown $watcher so it no longer is firing events and properly disposed.
$Form_MAIN = [Form]#{
AutoSize = $false
AutoScale = $false
BackColor = '0x6699CC'
ClientSize = "1024,1000"
Location = "600,300"
MaximizeBox = $false
StartPosition = "manual"
Text = "DataCenter Operation Applications"
Topmost = $true
}
$Form_MAIN.Add_FormClosing({
$watcher.Dispose()
})
The TextBox Text property is assigned results of call to GetLogFileContent function.
$TextBox_Output = [TextBox]#{
Anchor = 'Top, Left, Bottom, Right'
Location = '12, 12'
Multiline = $true
Name = 'TextBox_Output'
Size = "$($Form_MAIN.ClientSize.Width - 24), $($Form_MAIN.ClientSize.Height - 24)"
Text = GetLogFileContent $FilePathNameToWatch
}
$Form_Main.Controls.Add($TextBox_Output)
$watcher's NotifyFilter property now set for checking for both FileName and LastWrite. Just a reminder, SynchronizingObject set to the TextBox so it can be updated on the same thread.
[IO.FileSystemWatcher]$watcher = [IO.FileSystemWatcher]#{
Path = $FilePathToWatch
NotifyFilter = [IO.NotifyFilters]::FileName -bor [IO.NotifyFilters]::LastWrite
Filter = $FileNameToWatch
SynchronizingObject = $TextBox_Output
}
Added Deleted and Renamed events to $watcher to catch deletions and renames, and using GetLogFileContent function to populate the textbox Text property.
$watcher.Add_Changed({
$TextBox_Output.Text = GetLogFileContent $FilePathNameToWatch
})
$watcher.Add_Deleted({
$TextBox_Output.Text = ''
})
$watcher.Add_Renamed({
$TextBox_Output.Text = if($_.Name -eq $FileNameToWatch) { GetLogFileContent $FilePathNameToWatch } else { '' }
})
Watcher.Dispose() removed from end of code (taken care of in FormClosing event).
$watcher.EnableRaisingEvents = $true
$null = $Form_Main.ShowDialog()
Note:
This answer addresses the question as asked.
Darin's helpful answer shows an alternative approach that doesn't unconditionally, periodically re-read the log file (based on a timer), but uses an event-based file-system watcher to only update re-read the file if and when it changes.
This is more elegant and generally preferable, except if the log file is updated very frequently, in which case you'd need a throttling mechanism (which the timer-based approach implicitly provides, though one could be implemented with the file-watcher approach too).
There is no obvious problem with your approach:
The System.Windows.Forms.Timer type fires its events on the GUI thread and therefore allows modifying the form state.
While script blocks ({ ... }) that are passed as event delegates in .add_<eventName>() calls run in a child scope of the script, thanks to PowerShell's dynamic scoping you can still read the variables from the script scope.
Since WinForms is in control of the event loop when you show a form modally with .ShowDialog(), it is sufficient to assign new content to the .Lines property of your text-box control: the control should refresh automatically (and even an explicit .Refresh() call should not make the whole form refresh).
The following is a self-contained proof of concept:
A background job is started that writes the current timestamp to a given file every second.
The WinForms code uses a timer event to read that file and update its multi-line text box control with it.
using namespace System.Windows.Forms
using namespace System.Drawing
Add-Type -AssemblyName System.Windows.Forms
# Create the form.
$form = [Form] #{
Text = "Textbox Timer-Based Refresh Demo"
Size = [Size]::new(380,200)
StartPosition = "CenterScreen"
}
# Create the textbox and add it to the form.
$form.Controls.AddRange(#(
($textBox = [TextBox] #{
Location = [Point]::new(10, 10)
Size = [Size]::new(320, 90)
MultiLine = $true
})
))
# Create a timer that fires every second, and
# reads the then-current file content.
$timer = [Timer]::new()
$timer.InterVal = 1000
$timer.add_Tick({
$textBox.Lines = Get-Content -Raw $logName
})
$timer.Start()
# The log file to read.
$logName = 't.txt'
# Create a background job that updates the log file every second.
$jb = Start-Job {
while ($true) {
Get-Date > "$using:PWD/$using:logName"
Start-Sleep 1
}
}
# Show the form modally.
$null = $form.ShowDialog()
# Clean up
$timer.Dispose()
$jb | Remove-Job -Force

PowerShell checkbox controls button function

This is my very first school project in PowerShell. Its going to be simple. Type path and press button to Get-Acl, which works, but I want to have an option for recurse. Which also works, but I have not been able to make a checkbox determine what function button has.. Specifically The checkboxstatechanged is what I need help with.
function ChckBx_chkd {
$table = Get-ChildItem -directory -path $Textbox1.Text -recurse
$Rtbx = #()
foreach ($dir in $table)
{
$Access = (Get-Acl $dir.FullName).Access
$Rtbx += $dir.PSPath
$Rtbx += $Access | Format-Table | out-string
}
}
function ChkcBx_unchkd {
$Rtbx.Text = (Get-Acl $Textbox1.Text).Access| Format-Table| Out-String
}
$Form = New-Object System.Windows.Forms.Form
#Själva Formen
$Form.Text = "ACL GUI"
$Font = New-Object System.Drawing.Font("Lucida Console",8)
$Form.Font = $Font
$Form.Width = 1000
$Form.Height = 500
$Rtbx = New-Object System.Windows.Forms.RichTextBox
$Rtbx.Location = '200, 100'
$Rtbx.Width = 750
$Rtbx.Font = $Font
$Rtbx.Height = 300
$Rtbx.Font
$Form.Controls.Add($Rtbx)
$Chkbx1 = New-Object System.Windows.Forms.CheckBox
$Chkbx1.Location = '50, 150'
$Chkbx1.Text = "undermappar"
$Chkbx1.Checked = $true
$Chkbx1.Add_CheckStateChanged({
if($Chkbx1.checked) {
$Btn1Click = ChckBx_chkd
}
else
{
$Btn1Click = ChkcBx_unchkd
}
})
$Form.Controls.Add($Chkbx1)
$Textbox1 = New-Object System.Windows.Forms.TextBox
$Textbox1.Location = '50, 50'
$Textbox1.Width = 700
$Form.Controls.Add($Textbox1)
$Btn1 = New-Object System.Windows.Forms.Button
$Btn1.Location = '50, 100'
$Btn1.Text = "Get ACL"
$Btn1.Font =
$Btn1.Width = 100
$Btn1.Add_Click($Btn1Click)
$Form.Controls.Add($Btn1)
$Form.ShowDialog()
I have to say that with the code formatted as it is, it makes it very hard to see where the functions and loops start and end. Pick a style a stick with it.
Your function names are spelt oddly which is confusing ChckBx_unchkd and ChkcBx_chkd - why reverse ck in the prefix?
Your ChkcBx_chkd code doesn't appear to work - you're redefining the variable $Rtbx as an array where you want to set the .Text property.
You have no default value set so $Btn1Click is undefined until you change the checkbox state at least once.
Your assignment of $Btn1Click = ChckBx_chkd actually calls that function at that point where you appear be using it as a reference which you then call later. You should do $Btn1Click = "ChckBx_chkd" (i.e. a string name of the function) then later use Invoke-Expression $Btn1Click
The scope of your variables is not the same. Changing $Btn1Click does not affect the value. Write-Host is your friend - print the variables out to help you debug what values they have. I've used Get-Variable/Set-Variable so I can use the variables in the global scope.
Revised code
function ChckBx_chkd {
Write-Host "ChckBx_chkd called"
$table = Get-ChildItem -directory -path $Textbox1.Text -recurse
$Rtbx.Text = ""
foreach ($dir in $table) {
$Access = (Get-Acl $dir.FullName).Access
$Rtbx.Text += $dir.PSPath
$Rtbx.Text += $Access | Format-Table | out-string
}
}
function ChkcBx_unchkd {
Write-Host "ChkcBx_unchkd called"
$Rtbx.Text = (Get-Acl $Textbox1.Text).Access | Format-Table| Out-String
}
Set-Variable -Name Btn1Click -Value "ChckBx_chkd" -Scope Global
$Font = New-Object System.Drawing.Font("Lucida Console",8)
$Form = New-Object System.Windows.Forms.Form
$Form.Text = "ACL GUI"
$Form.Font = $Font
$Form.Width = 1000
$Form.Height = 500
$Rtbx = New-Object System.Windows.Forms.RichTextBox
$Rtbx.Location = '200, 100'
$Rtbx.Width = 750
$Rtbx.Font = $Font
$Rtbx.Height = 300
$Rtbx.Font
$Form.Controls.Add($Rtbx)
$Chkbx1 = New-Object System.Windows.Forms.CheckBox
$Chkbx1.Location = '50, 150'
$Chkbx1.Text = "undermappar"
$Chkbx1.Checked = $true
$Chkbx1.Add_CheckStateChanged({
Write-Host $Chkbx1.checked $(Get-Variable -Name Btn1Click -Scope Global).Value
if ($Chkbx1.checked) {
Set-Variable -Name Btn1Click -Value "ChckBx_chkd" -Scope Global
} else {
Set-Variable -Name Btn1Click -Value "ChkcBx_unchkd" -Scope Global
}
Write-Host $Chkbx1.checked $(Get-Variable -Name Btn1Click -Scope Global).Value
})
$Form.Controls.Add($Chkbx1)
$Textbox1 = New-Object System.Windows.Forms.TextBox
$Textbox1.Location = '50, 50'
$Textbox1.Width = 700
$Form.Controls.Add($Textbox1)
$Btn1 = New-Object System.Windows.Forms.Button
$Btn1.Location = '50, 100'
$Btn1.Text = "Get ACL"
$Btn1.Font =
$Btn1.Width = 100
$Btn1.Add_Click({Invoke-Expression $(Get-Variable -Name Btn1Click -Scope Global).Value})
$Form.Controls.Add($Btn1)
$Form.ShowDialog()

Powershell Update an already active form

So I'm trying to insert red dots into some kind of map, which I do with creating new pictureboxes after reading their coordinates out of an txt-file.
My goal now is to remove or create new boxes, while the form.ShowDialog() was already used.
I found a way of closing the whole form and running everything again, which kind of works but is very ugly in my opinion. Was wondering if there is another way of checking if new coordinates have been added to the txt-file or removed and if that is the case, creating or removing the corresponding boxes.
(I tried .Refresh but that seems to do nothing at all)
function MakeForm {
[void][reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.Application]::EnableVisualStyles();
$form = new-object Windows.Forms.Form
$form.Text = "Image Viewer"
$form.Width = 270;
$form.Height = 270;
$path = (Get-Item "Insert Path here")
$path2 = (Get-Item "Insert Path here")
$img = [System.Drawing.Image]::Fromfile($path);
$img2 = [System.Drawing.Image]::FromFile($path2);
$test = Get-Content -Path "Insert Path here"
$test
$pictureBoxes = New-Object 'System.Collections.Generic.List[Windows.Forms.PictureBox]'
$i=1
Foreach ($Line in $test){
if ($Line -like "*in 2*"){
$i++
$Split1= $Line.Split(" ")
$x=$Split1[5]
$y=$Split1[6]
$pictureBox = New-Object Windows.Forms.PictureBox
$pictureBox.Width = $img.Size.Width;
$pictureBox.Height = $img.Size.Height;
$pictureBox.Location = New-object System.Drawing.Size($x,$y)
$pictureBox.Image = $img;
$form.Controls.Add($pictureBox)
$pictureBoxes.Add($pictureBox)
Write-Host $pictureBoxes[$i]
$form.Add_Shown( { $form.Activate() } )
}
}
$pictureBox20 = New-Object Windows.Forms.PictureBox
$pictureBox20.Width = $img2.Size.Width;
$pictureBox20.Height = $img2.Size.Height;
$pictureBox20.Image = $img2;
$form.Controls.Add($pictureBox20)
$button1 = New-Object System.Windows.Forms.Button
$button1.Width=25
$button1.Height=223
$button1.Location = New-Object System.Drawing.Point(223,0)
$form.Controls.Add($button1)
$button1.Add_Click({
#Button for removing a box for testing purposes
$form.Controls.Remove($pictureBoxes[3])
})
$button2 = New-Object System.Windows.Forms.Button
$button2.Width=25
$button2.Height=223
$button2.Location = New-Object System.Drawing.Point(260,0)
$form.Controls.Add($button2)
$button2.Add_Click({
#This Button should refresh the whole form, if possible without doing everything again
$form.Close()
$form.Dispose()
MakeForm
})
$form.Add_Shown( { $form.Activate() } )
$form.ShowDialog()
}
MakeForm

PS Forms - Unhandled Exception using BREAK in ForEach loop

Following on from a previous question
The actual code is a lot more complex, but the following is working example of my loop:
Add-Type -AssemblyName System.Windows.Forms
$source = '\\servera\files'
$destination = '\\server b\files'
$form = New-Object System.Windows.Forms.Form
$CopyOutput = New-Object System.Windows.Forms.Label
$CopyOutput.Location = '10,15'
$CopyOutput.Size = '350,20'
$form.Text = "$DomainName Folder/Archive Copy"
$form.Size = '380,130'
$form.CancelButton = $ExitButton
$form.Add_FormClosing({
$script:CancelLoop = $true
})
$StartButton = New-Object System.Windows.Forms.Button
$StartButton.Name = 'StartButton'
$StartButton.Location = '10,50'
$StartButton.Size = '75,23'
$StartButton.Text = 'Start Copy'
$StartButton.Enabled = $true
$PauseButton = New-Object System.Windows.Forms.Button
$PauseButton.Location = '100,50'
$PauseButton.Size = '75,23'
$PauseButton.Text = 'Pause Copy'
$PauseButton.Enabled = $true
$StopButton = New-Object System.Windows.Forms.Button
$StopButton.Location = '190,50'
$StopButton.Size = '75,23'
$StopButton.Text = 'Stop Copy'
$StopButton.Enabled = $true
$ExitButton = New-Object System.Windows.Forms.Button
$ExitButton.Name = 'ExitButton'
$ExitButton.Location = '280,50'
$ExitButton.Size = '75,23'
$ExitButton.Text = 'Exit'
$ExitButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.Controls.AddRange(#($StartButton,$PauseButton,$StopButton,$ExitButton,$CopyOutput))
$StopButton.Add_Click({
$script:PauseToggle = $false
$script:CancelLoop = $true
})
$PauseButton.Add_Click({
# Boolean change value to true/false
$script:PauseToggle = !$script:PauseToggle
})
$StartButton.Add_Click({
$script:CancelLoop = $false
$script:PauseToggle = $false
$StopButton.Enabled = $true
$StartButton.Enabled = $false
Get-ChildItem -LiteralPath $Source -Recurse -File | ForEach {
Copy-Item -LiteralPath $_.FullName -Destination $Destination
$CopyOutput.Text = ('Copying' + $_.FullName)
[System.Windows.Forms.Application]::DoEvents()
If($script:CancelLoop -eq $true) {
$CopyOutput.Text = 'Cancel copy'
#Exit the loop
Break;
}
If ($script:PauseToggle) {
$CopyOutput.Text = 'Paused'
Do {
[System.Windows.Forms.Application]::DoEvents()
} Until (!$script:PauseToggle)
}
}
$CancelButton.Enabled = $false
$StartCopyButton.Enabled = $true
})
$form.ShowDialog()
$form.Dispose()
All the articles I can find say that this code should work ok - can anyone advise how to avoid the unhandled exception which results when the "Stop" button is pressed?
All the articles I can find say that this code should work ok - can anyone advise how to avoid the unhandled exception which results when the "Stop" button is pressed?
From what I can see in my research, this is an issue with using BREAK in forms. I did however find a workaround from here
Using this commandlet will stop the current pipeline instead of using BREAK
Filter Stop-Pipeline {
$sp = { Select-Object -First 1 }.GetSteppablePipeline($MyInvocation.CommandOrigin)
$sp.Begin($true)
$sp.Process(0)
}

Powershell - DataViewGrid - Column Autosize

I am a complete novice when it comes to .NET and powershell and was wondering if you guys could assist. I am generating a Data Grid from a .CSV on a form and would like the grid to auto size columns accordingly. Also if I could lock the columns/rows from user adjustment that would be amazing.
Clear-Host
Function Populate-CycleCountDataGrid {
$InventoryListArray = New-Object System.Collections.ArrayList
$Script:InventoryList = #(Import-CSV C:\File.csv | Write-Output)
$InventoryListArray.AddRange($Script:InventoryList)
$CycleCountDataGrid.DataSource = $InventoryListArray
}
Function GenerateForm {
$objForm = New-Object System.Windows.Forms.Form
$InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
$RefreshButton_Click = {
Populate-CycleCountDataGrid
}
# Form Setup
#*******************************************************************************************\
$OnLoadForm_StateCorrection= { $objForm.WindowState = $InitialFormWindowState }
$objForm.Text = "CycleCount"
$objForm.Name = "CycleCount"
$objForm.Size = New-Object System.Drawing.Size(600,480)
$objForm.StartPosition = 0
$objForm.AutoSize = $False
$objForm.MinimizeBox = $False
$objForm.MaximizeBox = $False
$objForm.WindowState = "Normal"
# DataGrid
#*******************************************************************************************\
$CycleCountDataGrid = New-Object System.Windows.Forms.DataGrid
$CycleCountDataGrid.Location = New-Object System.Drawing.Size(0,0)
$CycleCountDataGrid.Size = New-Object System.Drawing.Size(592,400)
$CycleCountDataGrid.AutoSize = $False
$CycleCountDataGrid.AllowSorting = $False
$CycleCountDataGrid.ReadOnly = $True
$CycleCountDataGrid.CaptionText = "Inventory List"
$CycleCountDataGrid.HeaderFont = New-Object System.Drawing.Font("Verdana",8.25,1,3,0)
$CycleCountDataGrid.HeaderForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0)
$CycleCountDataGrid.Font = New-Object System.Drawing.Font("Verdana",8.25,[System.Drawing.FontStyle]::Bold)
$CycleCountDataGrid.BackColor = [System.Drawing.Color]::FromArgb(255,0,160,250)
$CycleCountDataGrid.AlternatingBackColor = [System.Drawing.Color]::FromArgb(255,133,194,255)
$CycleCountDataGrid.Name = "CycleCountDataGrid"
$CycleCountDataGrid.DataBindings.DefaultDataSourceUpdateMode = 0
$objForm.Controls.Add($CycleCountDataGrid)
#*******************************************************************************************/
# Refresh Button
#*******************************************************************************************\
$RefreshButton = New-Object System.Windows.Forms.Button
$RefreshButton.Location = New-Object System.Drawing.Size(0,400)
$RefreshButton.Size = New-Object System.Drawing.Size(590,45)
$RefreshButton.Name = "RefreshButton"
$RefreshButton.Text = "Refresh"
$RefreshButton.UseVisualStyleBackColor = $True
$RefreshButton.add_Click($RefreshButton_Click)
$RefreshButton.DataBindings.DefaultDataSourceUpdateMode = 0
$objForm.Controls.Add($RefreshButton)
#*******************************************************************************************/
$objForm.Topmost = $True
$objForm.Add_Shown({$objForm.Activate()})
$objForm.FormBorderStyle = 'Fixed3D'
$objForm.MaximizeBox = $False
$objForm.Add_FormClosing([System.Windows.Forms.FormClosingEventHandler]{
if ($objForm.DialogResult -eq "Cancel") {}
})
$InitialFormWindowState = $objForm.WindowState
$objForm.add_Load($OnLoadForm_StateCorrection)
$objForm.ShowDialog()
#*******************************************************************************************/
}
GenerateForm
Add the following code:
$CycleCountDataGrid.Columns | Foreach-Object{
$_.AutoSizeMode = [System.Windows.Forms.DataGridViewAutoSizeColumnMode]::AllCells
}
Change your control to a system.windows.forms.datagridview rather than just a datagrid. Then you have access to $CycleCountDataGrid.columns
Each column has a width property. The answer above will try to autosize each column but you could specify each one if you like.
$CycleCountDatarid.columns[0].width = 200
100 is the default
The secret to Autosizing a Windows.Forms.Datagrid is that it has a private method 'ColAutoResize' you can invoke with Reflection:
Function AutoResizeColumns([System.Windows.Forms.DataGrid] $dg1){
[System.Reflection.BindingFlags] $F = 'static','nonpublic','instance'
$ColAutoResizeMethod = $dg1.GetType().GetMethod('ColAutoResize', $F)
If($ColAutoResizeMethod) {
For ([int]$i = $dg1.FirstVisibleColumn; $i -lt $dg1.VisibleColumnCount; $i++){
$ColAutoResizeMethod.Invoke($dg1, $i) | Out-Null
}
}
}
Once you have that function, you can add it to the DataGrid's VisibleChanged and DataSourceChanged events so drawing and refreshing the DataGrid will invoke AutoResizeColumns:
$objForm.Controls["CycleCountDataGrid"].add_DatasourceChanged({ AutoResizeColumns $objForm.Controls["CycleCountDataGrid"] } )
$objForm.Controls["CycleCountDataGrid"].add_VisibleChanged({ AutoResizeColumns $objForm.Controls["CycleCountDataGrid"] } )
$objForm.ShowDialog() | Out-Null
There's probably a cleaner way to do this, but it's working for me.