Powershell GUI strip menu item OnClick event - powershell

I am practicing building a GUI for an application I built and am struggling a little bit with stripMenus. For my 3rd menu item I am trying to list my socials and OnClick have it open up youtube for example. I am just not completely familiar with the syntax of it and it is shockingly hard to find documentation regarding this online.
Add-Type -AssemblyName System.Windows.Forms
$form=New-Object System.Windows.Forms.Form
$form.StartPosition='CenterScreen'
$MenuBar = New-Object System.Windows.Forms.MenuStrip
$fileToolStripMenuItem = new-object System.Windows.Forms.ToolStripMenuItem
$editionToolStripMenuItem = new-object System.Windows.Forms.ToolStripMenuItem
$socialToolStripMenuItem = new-object System.Windows.Forms.ToolStripMenuItem
$YtToolStripMenuItem = new-object System.Windows.Forms.ToolStripMenuItem
$Form.Controls.Add($MenuBar)
$MenuBar.Items.AddRange(#(
$fileToolStripMenuItem,
$editionToolStripMenuItem,
$socialToolStripMenuItem))
$fileToolStripMenuItem.Name = "fileToolStripMenuItem"
$fileToolStripMenuItem.Size = new-object System.Drawing.Size(35, 20)
$fileToolStripMenuItem.Text = "&File"
$editionToolStripMenuItem.Name = "editionToolStripMenuItem"
$editionToolStripMenuItem.Size = new-object System.Drawing.Size(51, 20)
$editionToolStripMenuItem.Text = "&Edition"
$socialToolStripMenuItem.DropDownItems.AddRange(#($YtToolStripMenuItem))
$socialToolStripMenuItem.Name = "socialToolStripMenuItem"
$socialToolStripMenuItem.Size = new-object System.Drawing.Size(67, 20)
$socialToolStripMenuItem.Text = "&Socials"
$YtToolStripMenuItem.Name = "YtToolStripMenuItem"
$YtToolStripMenuItem.Size = new-object System.Drawing.Size(152, 22)
$YtToolStripMenuItem.Text = "&YouTube"
Below is where I would like to learn the syntax to execute powershell commands
function OnClick_YtToolStripMenuItem($Sender,$e){
#powershell -w h -NoP -NonI -Exec Bypass Start-Process https://www.youtube.com"
[void][System.Windows.Forms.MessageBox]::Show("Subscribe to my youtube")
}
$YtToolStripMenuItem.Add_Click( { OnClick_YtToolStripMenuItem $YtToolStripMenuItem $EventArgs} )
$form.ShowDialog()

I just needed to get rid of the event args as they were not needed. The following snippet is how I was able to get it working:
$YtToolStripMenuItem.Name = "YtToolStripMenuItem"
$YtToolStripMenuItem.Size = new-object System.Drawing.Size(152, 22)
$YtToolStripMenuItem.Text = "&YouTube"
function OnClick_YtToolStripMenuItem() {
Start-Process 'https://www.youtube.com/iamjakoby?sub_confirmation=1'
}
$YtToolStripMenuItem.Add_Click({ OnClick_YtToolStripMenuItem })

Related

PowerShell GUI - Setting "button.Add_MouseEnter" actions in a function

I'm creating a small PowerShell GUI with a buttons to quickly run various powershell commands. I've got the general layout how I want it, but now I'm trying to simplify my code by using a button building function.
I don't have an issue setting assorted traits like "Button.Text", "Button.SetToolTip", or "Button.Add_Click" in a separate function, but I'm having issues setting the "Button.Add_MouseEnter" and "Button.Add_MouseLeave" aspects in a stand-alone function. The code compiles fine, but when I hover over the button in the gui I get the following error (similar errors for lines 8-10)
"The property 'BackColor' cannot be found on this object. Verify that the property exists and can be set.
At line:7 char:30
$tButton.Add_MouseEnter({$tButton.BackColor = '#990000'})
CategoryInfo : InvalidOperation: (:) [], RuntimeException
FullyQualifiedErrorId : PropertyNotFound"
I tried creating the button object fully in the function and returning the button, which would set all the button traits correctly but still gives the same error when the mouse passes over. I think I may need to pass the button object to the function/return the button differently (depending on which code 1st or 2nd), but I don't know. I thought perhaps binding the parameters with [CmdletBinding()] but the error still comes up (I probably don't understand that as well as I should) Any insight would be appreciated
===== Initial Code =====
`
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
Clear-Host
function Set-Hover ($tButton){
$tButton.Add_MouseEnter({$tButton.BackColor = '#990000'})
$tButton.Add_MouseEnter({$tButton.ForeColor = '#000000'})
$tButton.Add_MouseLeave({$tButton.BackColor = '#000000'})
$tButton.Add_MouseLeave({$tButton.ForeColor = '#FFFFFF'})
}
$Window = New-Object System.Windows.Forms.Form
$Window.ClientSize = New-Object System.Drawing.Size(100, 50)
$Global:Button = New-Object 'system.Windows.Forms.Button'
$Button.Size = New-Object System.Drawing.Point(75,40)
$Button.Location = New-Object System.Drawing.Point(5, 5)
$Button.text = "Button"
$Button.BackColor = '#000000'
$Button.ForeColor = '#FFFFFF'
Set-Hover -tButton $Button
$Window.Controls.Add($Button)
<# The code works when the Mouse_Enter/Leave lines are here, not in a function
$Button.Add_MouseEnter({$Button.BackColor = '#990000'})
$Button.Add_MouseEnter({$Button.ForeColor = '#000000'})
$Button.Add_MouseLeave({$Button.BackColor = '#000000'})
$Button.Add_MouseLeave({$Button.ForeColor = '#FFFFFF'})
#>
$Window.ShowDialog()
`
===== Second Code =====
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
Clear-Host
function Set-Hover {
$tButton = New-Object 'system.Windows.Forms.Button'
$tButton.Size = New-Object System.Drawing.Point(75,40)
$tButton.Location = New-Object System.Drawing.Point(5, 5)
$tButton.text = "Text"
$tButton.BackColor = '#FFFFFF'
$tButton.ForeColor = '#000000'
$tButton.Add_MouseEnter({$tButton.BackColor = '#990000'})
$tButton.Add_MouseEnter({$tButton.ForeColor = '#000000'})
$tButton.Add_MouseLeave({$tButton.BackColor = '#000000'})
$tButton.Add_MouseLeave({$tButton.ForeColor = '#FFFFFF'})
$tButton
}
$Window = New-Object System.Windows.Forms.Form
$Window.ClientSize = New-Object System.Drawing.Size(100, 50)
$Button1 = Set-Hover
$Window.Controls.Add($Button1)
<# The code works when the Mouse_Enter/Leave lines are here, not in a function
$Button.Add_MouseEnter({$Button.BackColor = '#990000'})
$Button.Add_MouseEnter({$Button.ForeColor = '#000000'})
$Button.Add_MouseLeave({$Button.BackColor = '#000000'})
$Button.Add_MouseLeave({$Button.ForeColor = '#FFFFFF'})
#>
$Window.ShowDialog()
You need to define the button in the form code, not create a new one inside the function when the mouse hovers over it (or away from it).
If you do want to use a function for this, change your function to accept parameters (the first is the button object itself, the second a switch to determine if it is an MouseEnter or MouseLeave event)
Also, better not use the old [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") syntax anymore.
Here's your code revised:
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
function Set-Hover {
param (
[System.Windows.Forms.Button]$ButtonObject,
[switch] $MouseEnter
)
if ($MouseEnter) {
$bkColor = '#990000'
$fgColor = '#000000'
}
else {
$bkColor = '#000000'
$fgColor = '#FFFFFF'
}
$ButtonObject.BackColor = $bkColor
$ButtonObject.ForeColor = $fgColor
}
$Window = New-Object System.Windows.Forms.Form
$Window.Size = New-Object System.Drawing.Size(250, 250)
$Button = New-Object System.Windows.Forms.Button
$Button.Size = New-Object System.Drawing.Point(75,40)
$Button.Location = New-Object System.Drawing.Point(5, 5)
$Button.Text = "Button"
$Button.BackColor = '#000000'
$Button.ForeColor = '#FFFFFF'
# inside an event handler, you can refer to the current object with keyword $this
$Button.Add_MouseEnter({ Set-Hover -ButtonObject $this -MouseEnter })
$Button.Add_MouseLeave({ Set-Hover -ButtonObject $this })
$Window.Controls.Add($Button)
$Window.ShowDialog()
# important: Remove the form from memory when done with it
$Window.Dispose()

Random number generator not working after converting to Exe

I have written a dice roller script. I have one with a single die and one with two dice. they both work in powershell, and the single die version works after converting to .exe using ps2exe. but the two die version runs as an exe but I get the following error "You cannot call a method on a null-valued expression"
Below is the 2 die script that gives the error after converting.
<#
.NAME
Random Dice Roller
#>
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$Display1 = 1,2,3,4,5,6 | Get-Random
$Display2 = 1,2,3,4,5,6 | Get-Random
$LabelImage = [system.drawing.image]::FromFile("f:\psscripts\Die1.png")
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = New-Object System.Drawing.Point(400,300)
$Form.text = "Roll The Dice"
$Form.TopMost = $false
$Form.location = New-Object System.Drawing.Point(1,1)
$Form.StartPosition = "CenterScreen"
$Die1 = New-Object system.Windows.Forms.Label
$Die1.Text = "$Display1"
$Die1.AutoSize = $false
$Die1.width = 200
$Die1.height = 200
$Die1.location = New-Object System.Drawing.Point(1,1)
$Die1.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',150,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold))
$Die1.ForeColor = [System.Drawing.ColorTranslator]::FromHtml("#000000")
$Die1.BackgroundImage = $LabelImage
$Die2 = New-Object system.Windows.Forms.Label
$Die2.Text = "$Display2"
$Die2.AutoSize = $false
$Die2.width = 200
$Die2.height = 200
$Die2.location = New-Object System.Drawing.Point(200,1)
$Die2.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',150,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold))
$Die2.ForeColor = [System.Drawing.ColorTranslator]::FromHtml("#000000")
$Die2.BackgroundImage = $LabelImage
$Button1 = New-Object System.Windows.Forms.Button
$Button1.Location = New-Object System.Drawing.Size(175,220)
$Button1.Size = New-Object System.Drawing.Size(80,50)
$Button1.Text = "Roll Me"
$Button1.Add_Click({Roll})
$Form.controls.AddRange(#($Die1,$Die2,$Button1))
$Die.Add_Click({ Roll })
#region Logic
function Roll{
$Form.Close()
$Form.Dispose()
.\Dice.exe}
#endregion
[void]$Form.ShowDialog()
There are two possible causes here:
Note that there is no safety in wrapping you PowerShell script in an .exe file. In fact the PowerShell script is extracted in a temporary folder and executed from there which might be the cause of your issue (e.g. the relative .\Dice.exe location)
It is unwise to do a $Form.Dispose() from within a form function/event
Remove that from the function and put it outside the form, e.g.:
[void]$Form.ShowDialog(); $Form.Dispose()
(Or do not use the Dispose method at all as PowerShell will usually already takes care of that if you [void] the $From.)
What is your motive for creating an EXE?
An alternative might be to create a CMD file with a Batch bootstrap instead. Create a CMD file with the following code and include your script after this.
<# :
#ECHO OFF
SET f0=%~f0
PowerShell -NoProfile -ExecutionPolicy RemoteSigned -Command ".([scriptblock]::Create((get-content -raw $Env:f0)))"
PAUSE
GOTO :EOF
<#~#>
# Insert PowerShell script after this line.

how to make power shell script from qrcode desktop application

I have created desktop application with generate qr code like below
I want make a script using batch script or powershell with the qrcode.exe based on value enter url and Qr Image Name using command line.the output should be QR Code Image too.
can try with this but first import the QRCodeGenerator before running the script
#Import-Module QRCodeGenerator
[string] $webname, [string] $url, [string] $output = ".\images\"
$webname = 'Google'
$url = 'https://www.google.com'
$outputfolder = $outputfolder + $webname +'.JPG'
New-PSOneQRCodeURI -URI $url -Width 15 -OutPath $output
and then the output result will be showing as JPG type.
Install-Module -Name QRCodeGenerator
Import-Module QRCodeGenerator
Add-Type -assembly System.Windows.Forms
$qr_base_form = New-Object System.Windows.Forms.Form
$qr_base_form.Height = 150
$qr_base_form.Width = 350
$qr_base_form.Text = "QR Code Generator"
$qr_base_form.AutoSize = $true
$qr_label_url = New-Object System.Windows.Forms.Label
$qr_label_url.Location = '10,10'
$qr_label_url.Size = '100,15'
$qr_label_url.Text = "URL:"
$qr_input_url = New-Object System.Windows.Forms.TextBox
$qr_input_url.Location = '10,30'
$qr_input_url.Size = '100,25'
$qr_label_name = New-Object System.Windows.Forms.Label
$qr_label_name.Location = '10,70'
$qr_label_name.Size = '100,15'
$qr_label_name.Text = "Name:"
$qr_input_name = New-Object System.Windows.Forms.TextBox
$qr_input_name.Location = '10,90'
$qr_input_name.Size = '100,25'
$qr_png_viewer = New-Object System.Windows.Forms.PictureBox
$qr_png_viewer.Image = $img
$qr_png_viewer.SizeMode = "Autosize"
$qr_png_viewer.Anchor = "Bottom, left"
$qr_png_viewer.Location = '150,10'
$qr_button_create = New-Object System.Windows.Forms.Button
$qr_button_create.Location = '150,150'
$qr_button_create.Size = '100,25'
$qr_button_create.Text = "Create Code"
$qr_button_create.Add_Click({
$path = "H:\LIVE\" + "$name"+ ".jpg"
$urllink = $qr_input_url.Text
$name = $qr_input_name.Text
New-PSOneQRCodeURI -URI "$urllink" -Width 15 -OutPath "$path"
$img = $path
})
$qr_base_form.Controls.Add($qr_label_url)
$qr_base_form.Controls.Add($qr_input_url)
$qr_base_form.Controls.Add($qr_label_name)
$qr_base_form.Controls.Add($qr_input_name)
$qr_base_form.Controls.Add($qr_png_viewer)
$qr_base_form.Controls.Add($qr_button_create)
$qr_base_form.ShowDialog()
This code should work for you. Just try changing the variables and try running it. GUI is also included.

Powershell multiple dynamic buttons

I built a dynamic Powershell GUI but i have trouble getting my buttons to work correctly.
I created a function to add texboxes and buttons. However they do not correspond correctly at this point. because i'm not sure how to bind the add_click to a specific textbox.
Here is the example code:
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$Form = New-Object System.Windows.Forms.Form
$Form.Size = New-Object System.Drawing.Size(300,300)
$ButtonAdd = New-Object System.Windows.Forms.Button
$ButtonAdd.Location = New-Object System.Drawing.Point(20,20)
$ButtonAdd.Size = New-Object System.Drawing.Size(50,30)
$ButtonAdd.Text = 'Add'
$Form.Controls.Add($ButtonAdd)
$global:Counter = 0
$global:ButtonPosY = 20
$global:DefaultTextValue = ""
$ButtonAdd.Add_Click{Add-Fields}
Function Create-Button {
param ( $ButtonPosY, $TextBoxPosY)
$TextBoxField = New-Object System.Windows.Forms.TextBox
$TextBoxField.Location = New-Object System.Drawing.Point(181,$TextBoxPosY)
$TextBoxField.Size = New-Object System.Drawing.Size(50,30)
$TextBoxField.Text = $global:DefaultTextValue
New-Variable -name TextBoxField$global:Counter -Value $TextBoxField -Scope Global -Force
$Form.controls.Add($((Get-Variable -name TextBoxField$global:Counter).value))
$ButtonClick = New-Object System.Windows.Forms.Button
$ButtonClick.Location = New-Object System.Drawing.Point(100,$global:ButtonPosY)
$ButtonClick.Size = New-Object System.Drawing.Size(50,30)
$ButtonClick.Text = 'Click'
New-Variable -name ButtonClick$global:Counter -Value $ButtonClick -Scope Global -Force
$Form.controls.Add($((Get-Variable -name ButtonClick$global:Counter).value))
$ButtonClick.Add_Click({
$((Get-Variable -name TextBoxField$global:Counter -Scope Global).value).Text = 'hello'
})
}
Function Add-Fields {
$global:Counter = $global:Counter + 1
$global:ButtonPosY = $global:ButtonPosY + 40
Create-Button -TextBoxPosY $global:ButtonPosY -ButtonPosY $global:ButtonPosY
}
Create-Button -TextBoxPosY 21 -ButtonPosY 20
$Form.ShowDialog()
If the click button is pressed each time after new input is added everything works fine. however if multiple input fields are added first and then the click button is pressed the code breaks.
The Problem is here:
$ButtonClick.Add_Click({...})
I don't know how to add the counter (in my case $global:Counter) to the $ButtonClick variable as in $ButtonClick0, $ButtonClick1, ... etc. So right now when i add more buttons by calling the function the input will always be applied to the last added textbox since the add_click is not linked to a individual $ButtonClick0 variable.
How would this be done right?
After some serious reading i figured it out.
A name needs to be added to $ButtonClick
$ButtonClick.Name = $global:Counter
And the Add_Click event should look the following:
$ButtonClick.Add_Click({
[System.Object]$Sender = $args[0]
$((Get-Variable -name ('TextBoxField' + [int]$Sender.Name)).value).Text = 'hello'
})
However i don't know if this is how it should be done. Please post your solution if it looks different.

Tray Icon Context Menu without hidden Form

I've been experimenting with tray icons & context menus in PowerShell for some time. However, i can only get the context menu to work correctly when a Form is called in the same script.
Here is a small example:
Add-Type -AssemblyName "System.Windows.Forms"
$objForm = New-Object System.Windows.Forms.Form
$objNotifyIcon = New-Object System.Windows.Forms.NotifyIcon
$objContextMenu = New-Object System.Windows.Forms.ContextMenu
$objExitMenuItem = New-Object System.Windows.Forms.MenuItem
$objExitMenuItem.Index = 1
$objExitMenuItem.Text = "Exit"
$objExitMenuItem.add_Click({
$objForm.Close()
$objNotifyIcon.visible = $false
})
$objContextMenu.MenuItems.Add($objExitMenuItem) | Out-Null
$objNotifyIcon.Icon = "$PSScriptRoot\win.ico"
$objNotifyIcon.Text = "Context Menu"
$objNotifyIcon.ContextMenu = $objContextMenu
$objForm.ContextMenu = $objContextMenu
#Enabling Icon in Taskbar
$objNotifyIcon.Visible = $true
#Hiding Form as best as possible
$objForm.Visible = $false
$objForm.WindowState = "minimized"
$objForm.ShowInTaskbar = $false
$objForm.add_Closing({ $objForm.ShowInTaskBar = $False })
$objForm.ShowDialog()
As soon as the Form componets are removed, the Context menu wont work correctly.
Does anyone know why you need this Form to be loaded and is there a way around it?
System.Windows.Forms.ApplicationContext is what you need to use to acheive that.