I'm certain I'm missing something stupid, but I can't get this figured. I have a script that creates a "Please Wait" popup while the code is running. The issue is that I can't get the popup to close after the script is done.
Here is how the popup gets called:
Function CheckShuffle()
{
#Check Selection
If ($Global:x -eq "" -or $Global:x -eq '0')
{
if ( $null -eq ('System.Windows.MessageBox' -as [type]) )
{
Add-Type -AssemblyName PresentationFramework
}
[System.Windows.MessageBox]::Show('You must select your part first', 'Warning', 'OK')
}
Else
{
OpenSplash
#Shuffle
CloseSplash
}
}
Here is the code for the popup:
Function OpenSplash()
{
$Splash = New-Object system.Windows.Forms.Form
$Splash.StartPosition = 'CenterScreen'
$Splash.Size = New-Object System.Drawing.Size(350,200)
$SplashLabel = New-Object System.Windows.Forms.Label
$Splash.Controls.Add($SplashLabel)
$SplashLabel.Location = New-Object System.Drawing.Point(100,60)
$SplashLabel.Font = [System.Drawing.Font]::new("Microsoft Sans Serif", 16, [System.Drawing.FontStyle]::Bold)
$SplashLabel.Text = "Please Wait"
$SplashLabel.AutoSize = $True
$Splash.Visible = $True
$Splash.Update()
}
I've tried a few ways to close it:
Function CloseSplash()
{
$This.Parent.Close()
}
This one one closes the primary form, not the splash screen.
Function CloseSplash()
{
$Splash.Close()
}
That one throws an error: "You cannot call a method on a null valued expression"
It looks like Powershell is forgetting what $Splash is, but I cannot figure out how to tell it.
Thank you all!
Edit: Worth noting, if I put the code all together instead of calling it, the popup closes as intended.
Jeroen answered the question in a comment, and I don't see how to mark that as the answer, so I'm reposting here!
The solution:
I needed to make $splash global. This was super easy, I just had to make it say:
$Global:Splash
Thanks again Jeroen!
Related
I want to make macros using function keys to improve my workflow but the code below doesnt work, i think its quite self explanatory, while the code is running if i press X key, a different text is sent.
$wshell = New-Object -ComObject wscript.shell;
# choose the key you are after
$key = [System.Windows.Input.Key]::LeftCtrl
$isCtrl = [System.Windows.Input.Keyboard]::IsKeyDown($key)
while ($true)
{
if ($isCtrl)
{
$wshell.SendKeys('Thank you for using the service.')
}
}
The code above doesnt work. But if i only use the code below it does send the string as expected.
$wshell = New-Object -ComObject wscript.shell;
sleep 1
$wshell.SendKeys('Digital service desk')
You keep checking the same value inside the loop since $isCtrl is never assigned to after entering the loop.
Change to:
while ($true)
{
$isCtrl = [System.Windows.Input.Keyboard]::IsKeyDown($key)
if ($isCtrl)
{
$wshell.SendKeys('Thank you for using the service.')
}
}
So that you re-check whether control is pushed down every time.
As stated by Mathias R. Jessen i was not updating the value of $isCtrl inside the loop. I'll now post the code if anyone wants to use it.
$wshell = New-Object -ComObject wscript.shell;
Add-Type -AssemblyName WindowsBase
Add-Type -AssemblyName PresentationCore
while ($true)
{
if ([System.Windows.Input.Keyboard]::IsKeyDown([System.Windows.Input.Key]::F2))
{
sleep 1
$wshell.SendKeys('Message after pressing F2')
} elseif ([System.Windows.Input.Keyboard]::IsKeyDown([System.Windows.Input.Key]::F4)){
sleep 1
$wshell.SendKeys('Message after pressing F4')
}
}
I am attempting to create a form in Powershell. It contains a ComboBox dropdown option that I am using as a required field. Until an option is selected, the continue button will be disabled. This is the code for the ComboBox and the button enabling:
$TSTypeBox.Name = "TSType"
$TSTypeBox.Location = New-Object System.Drawing.Point(116,100)
$TSTypeBox.Size = New-Object System.Drawing.Size(145,20)
$TSTypeBox.add_MouseHover($ShowHelp)
$TSTypeBox.DropDownStyle = "DropDownList"
Foreach ($item in ("1","2","3","4","5")) {
$TSTypeBox.Items.Add($item) | Out-Null
}
$TSTypeBox.SelectedItem = $TSLocation
$handler_TSTypeBox_SelectedIndexChanged= {
If (($TSTypeBox.Text) -and ($ComputerNameBox.Text))
{
$OKButton.Enabled = 1
}
Else
{
$OKButton.Enabled = 0
}
}
$TSTypeBox.add_SelectedIndexChanged($handler_TSTypeBox_SelectedIndexChanged)
This code in particular works as intended so I'm not worried about that. I am here about the $TSTypeBox.SelectedItem = $TSLocation line that I included. I have code elsewhere that pulls the IP address of the computer the program is being run on, which is then matched against an if/elseif/else statement to determine if the computer belongs to 1 or to 2, which are options that you can see were added to the ComboBox in the code above.
That if/else statement updated the $TSLocation variable which I then use to force the selection of one of the dropdown options in the ComboBox. This works as well, but unfortunately it does not enable the continue button as I would like. I had a hard time looking up issues about this because its super particular and I am probably doing this incorrectly (I have very little experience with Powershell scripting). If you have any additional questions about this please let me know. Thanks!
Ok, this might illustrate your problem.
Just because you set the SelectedItem value to something, doesn't mean the SelectedIndex changes
#
Add-Type -AssemblyName System.Windows.Forms -ErrorAction Stop
#
$TSLocation = '2'
#
$form = New-Object System.Windows.Forms.Form
$form.Text = "Test"
$form.MinimumSize = '430,495'
$form.MaximumSize = '430,545'
$form.StartPosition = 'CenterScreen'
#
# Add form objects
#
$TSTypeBox = New-Object System.Windows.Forms.ComboBox
$TSTypeBox.Name = "TSType"
$TSTypeBox.Location = '116,100'
$TSTypeBox.Size = '145,20'
$TSTypeBox.add_MouseHover($ShowHelp)
$TSTypeBox.DropDownStyle = "DropDownList"
Foreach ($item in ("1","2","3","4","5")) {
$TSTypeBox.Items.Add($item) | Out-Null
}
$ComputerNameBox = New-Object System.Windows.Forms.TextBox
$ComputerNameBox.Location = '120,20'
$ComputerNameBox.Size = '120,17'
$ComputerNameBox.Text = 'test'
$OutputBox = New-Object System.Windows.Forms.TextBox
$OutputBox.Location = '120,240'
$OutputBox.Size = '120,17'
$OkButton = New-Object System.Windows.Forms.Button
$OkButton.Location = '120,200'
$OkButton.Size = '54,24'
$OkButton.Text = 'OK'
$form.controls.AddRange(#($TSTypeBox,$OkButton,$ComputerNameBox,$OutputBox))
#
# Main Script goes here
#
$handler_TSTypeBox_SelectedIndexChanged= {
$OutputBox.Text = "SelectedIndex is " + $TSTypeBox.SelectedIndex
If (($TSTypeBox.Text) -and ($ComputerNameBox.Text))
{
$OKButton.Enabled = 1
}
Else
{
$OKButton.Enabled = 0
}
}
$TSTypeBox.add_SelectedIndexChanged($handler_TSTypeBox_SelectedIndexChanged)
#
$TSTypeBox.SelectedIndex = $TSTypeBox.FindStringExact($TSLocation)
#
# Show form
$form.ShowDialog() | Out-Null
$form.Dispose()
# End
If in doubt, always best to simplify your script and add debug ,logging or output that shows what values are changing
Now that the problem is clear - this article points you in the right direction:
How do I set the selected item in a comboBox to match my string using C#?
I'm trying to check an item into a ListBox after clicking a Radio Button, but no success.
Specifically, I'm trying to check a Active Directory Group already listed.
I made this piece of script:
$Action = foreach ($SecurityGroup in $ADSecurityGroup)
{
$SecurityGroup.Name -eq "AD Example"
{
[void] $ADGroups.Items.Add($SecurityGroup.Name, $True)
}
}
And after added to Add_Click of Raddio Button:
$RadioButton.Add_Click($Action)
Someone could help me?
Here is an example I provide to another OP, though it's using a list box, and a comboxbox, the premise for doing this via a button in the same.
Populate ComboBox2 depending on ComboBox1 selection
Another example of Comboxbox to a list
(You could just change the combobox to whatever form element you choose) :
Add-Type -AssemblyName System.Drawing
$ComboBox = New-Object System.Windows.Forms.ComboBox
$ComboBox.Location = New-Object System.Drawing.Point(10,10)
$ComboBox.Items.AddRange(#("One","Two"))
$RichTextBox = New-Object System.Windows.Forms.RichTextBox
$RichTextBox.Location = New-Object System.Drawing.Point(10,40)
$Form = New-Object System.Windows.Forms.Form
$Form.Controls.Add($ComboBox)
$Form.Controls.Add($RichTextBox)
$ComboBox.Add_TextChanged({
# Code here
switch($ComboBox.Text){
"One" {$RichTextBox.Text = "This is one"}
"Two" {$RichTextBox.Text = "This is two"}
}
})
$Form.ShowDialog()
As for this...
Specifically, I'm trying to check an Active Directory Group already
listed.
... you just do validation before your add another item.
I achieved what I was looking for with this:
$RadioButton.Add_Click({
for ($i = 0; $i -lt $ADGroups.Items.Count; $i++)
{
If ($ADGroups.Items[$i] -match 'AD Group that I wanted to change status')
{
$ADGroups.SetItemChecked($i, $true)
}
}
})
I publish it in case it is useful to someone.
Thanks a lot!
I have a DataGridView filled with rows with the ability to select and delete a row using Delete key.
There is a confirmation message box popping up when Delete is pressed, asking Yes or No to proceed with the deletion.
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = '800,800'
$Form.text = "Form"
$Form.TopMost = $false
$DataGridView1 = New-Object system.Windows.Forms.DataGridView
$DataGridView1.BackColor = "#f7f7f7"
$DataGridView1.width = 771
$DataGridView1.height = 716
$DataGridView1.Anchor = 'top,right,bottom,left'
$DataGridView1.location = New-Object System.Drawing.Point(15,68)
$import = New-Object system.Windows.Forms.Button
$import.text = "import"
$import.width = 60
$import.height = 30
$import.location = New-Object System.Drawing.Point(25,22)
$import.Font = 'Microsoft Sans Serif,10'
$save = New-Object system.Windows.Forms.Button
$save.text = "save"
$save.width = 60
$save.height = 30
$save.location = New-Object System.Drawing.Point(125,22)
$save.Font = 'Microsoft Sans Serif,10'
$Form.controls.AddRange(#($DataGridView1,$import,$save))
$import.Add_Click({ importXML })
$save.Add_Click({ saveXML })
$DataGridView1.Add_UserDeletingRow({ message })
$DataGridView1.AutoSizeColumnsMode = 16
Function importXML(){
$xml_input = Get-FileName
$ds = New-Object System.Data.Dataset
$ds.ReadXml($xml_input)
$DataGridView1.DataSource = $ds.Tables[0]
}
Function message(){
$msgBoxInput = [System.Windows.Forms.MessageBox]::Show("Proceed with the deletion?","Delete confirmation","YesNo","Question")
if ($msgBoxInput -eq "YES" )
{
[System.Windows.Forms.MessageBox]::Show("The selected row will be deleted")
}
else
{
#stop the deletion
}
}
Function saveXML(){
$xml_output = Save-FileName
$DataGridView1.DataSource.writexml($xml_output)
}
[void]$Form.ShowDialog()
Everything is working perfectly except after else. I have no idea on how to abort the deletion event.
Any suggestion?
In the MSDN examples, we see them do this in c# by setting e as a reference to the current event, and then setting e.Cancel equal to true, which allows us to cancel the event as covered here. The syntax looks like this:
private void DataGridView1_UserDeletingRow(object sender,
DataGridViewRowCancelEventArgs e){
e.Cancel = true; //Cancel the event
}
Well, in PowerShell if we try to add an event handler in this way, we'll get errors, because event handler methods generally only allow us to specify one overload which is the scriptblock to run on the event.
It turns out that it's deceptively easy to reference the current event, fortunately! To cancel the Deletion, simply add this to your add_UserDeletingRow() scriptblock.
else
{
#stop the deletion
$PSItem.Cancel=$true
}
You could also use the $_ current item syntax as well, which would look like
else
{
#stop the deletion
$_.Cancel=$true
}
Any time you're adding an event handler and need to refer to the event itself with PowerShell( and there are LOTs of Events like this, look at all of them for DataGridView alone!) you'll use $_ or $PSItem. So in those examples from MSDN, if you see them referencing the current event with e or something similar, just substitute $_ or $PSItem and you'll be good to go.
I have this script to launch IE, navigate to a page and search for some text:
$ie = new-object -com "InternetExplorer.Application"
$ie.Visible = $true
$ie.Navigate("http://www.google.com")
$doc = $ie.Document
if ($doc -eq $null)
{
Write-Host "The document is null."
return
}
$tb1 = $doc.getElementsByName("q") # a text box
$tb1.value = "search text";
$btn = $doc.getElementsByName("btnG")
$btn.click()
I save this as a ps1 file and run it from the command line... but the document object returned by $ie.Document is always null.
What am I doing wrong?
Also, when I run the script line by line in interpreter mode, the document is returned, but the next line $tb = $doc.getElementsByName("q") errors with this: Property 'Value' cannot be found on this object; make sure it exists and is settable.
How do I set the value of the text box, then?
You need to check if IE was done loading the page before $doc assignment. For example,
while ($ie.busy) {
#Sleep a bit
}
I tried the same code for entering the search text and button click but that did not work. So, ended up modiying your code to
$ie = new-object -com "InternetExplorer.Application"
$ie.Visible = $true
$ie.Navigate("http://www.google.com")
While ($ie.Busy) {
Sleep 2
}
$doc = $ie.Document
$btns = $doc.getElementsByTagName("input")
$SearchText = $btns | ? { $_.Name -eq "q" }
$SearchText.value = "search text"
$SearchButton = $btns | ? { $_.Name -eq "btnG" }
$SearchButton.click()
I believe there are two issues that I can see. First, Ravikahth's suggestion to add the ability to wait for the page to finish loading is important. If you do not wait for the page to load (i.e. $ie.busy -eq $false), then you will not get the full document.
Second, for whatever reason, Google decided to add multiple input fields with the name of "q." You can add a second condition to Ravikanth's query as stated below:
$SearchText = $btns | ? { $_.Name -eq "q" -and $_.Type -eq "text"}