PictureBox in the Background? - winforms

I want to display a GIF as background in my Form that I created with PowerShell.
I already figured out how to display the GIF and it works great, but of course all my Buttons and Textboxes etc. disappear behind it.

Background images are assigned via the BackgroundImage property of the form:
Add-Type -Assembly System.Windows.Forms
$form = New-Object Windows.Forms.Form
$img = [Drawing.Image]::FromFile('C:\path\to\your.gif')
$form.BackgroundImage = $img
$form.BackgroundImageLayout = 'Tile'
$form.ShowDialog()
See here for more information about using forms in PowerShell.
For displaying an animated GIF (you should've mentioned that in your question) it seems a PictureBox element is required. You can get it behind other elements by adding it after other elements (new elements are placed behind/below existing elements):
$img = [Drawing.Image]::FromFile('C:\path\to\your.gif')
$picbox = New-Object Windows.Forms.PictureBox
$picbox.Width = $img.Size.Width
$picbox.Height = $img.Size.Height
$picbox.Image = $img
$form.Controls.Add($otherElement)
$form.Controls.Add($yetAnotherElement)
$form.Controls.Add($picbox)
or by sending it to the back via the SendToBack() method:
$img = [Drawing.Image]::FromFile('C:\path\to\your.gif')
$picbox = New-Object Windows.Forms.PictureBox
$picbox.Width = $img.Size.Width
$picbox.Height = $img.Size.Height
$picbox.Image = $img
$form.Controls.Add($picbox)
$form.Controls.Add($otherElement)
$form.Controls.Add($yetAnotherElement)
$picbox.SendToBack()

Related

Powershell WPF observablecollection not updating wpf

I am using a observablecollection to insert data for bindings on wpf GUI but it's not updating.
$global:myCollection = New-Object System.Collections.ObjectModel.ObservableCollection[Object]
[string]$global:MyLabelContent = 'Test'
$myCollection.Insert(0,[ref]$MyLabelContent)
This is the binding function i am using (from link below)
Function Set-Binding {
Param(
$Target,
$TargetProperty,
$Path,
$Source
)
$Binding = New-Object System.Windows.Data.Binding
$Binding.Path = $Path
$Binding.Mode = [System.Windows.Data.BindingMode]::TwoWay
If ($Source)
{
$Binding.Source = $Source
}
[void][System.Windows.Data.BindingOperations]::SetBinding($Target,$TargetProperty,$Binding)
}
Here is the actuall binding call:
Set-Binding -Target $MyLabel -TargetProperty $([System.Windows.Controls.Label]::ContentProperty) -path "[0].Value" -Source $MyCollection
And here's how i change the label content
[string]$global:MyLabelContent = $mystring
If i close and open the window again the label value is correctly set, so the binding is actually working...
What am i doing wrong? i'm already using the same system for menuitems checkboxes and it works.
https://smsagent.blog/2017/02/03/powershell-deepdive-wpf-data-binding-and-inotifypropertychanged/

Playing music in Powershell stops when assigned to button

I'm facing a problem that I cannot get solved or understand with playing music from Powershell, I made a WPF GUI on top of my Powershell script.
It all works perfect except that when I press the play music button I made the music starts but after a few seconds stops.
Or when moving the mouse over the WPF GUI the music stops and I cannot get it solved. When I throw the code for playing the music in the project it works flawless, only when I assign a button to it the problems start.
So I made a stripped down version with a simple old form and a button nothing more, made an add_Click event to connect the button the code and tested again. Same problem again music stops playing either after a few seconds or when you move your mouse over the form.
Now I still had an old Windows 7 machine hanging around with Powershell V2 still on it, and guess what it worked flawlessly! Then I upgraded Powershell v2 to V5 on that machine and I had the same problem as on Win 10 (1909 with PS 5.1) laptop, so something changed with Powershell between V2 and V2 that causes this behavior, but I cannot find what.
Some examples, when I throw these lines of code in the project it works:
Add-Type -AssemblyName presentationcore
$location = (C:\users\myuserid\test.mp3)
$PlaySound = New-Object System.Windows.Media.MediaPlayer
$PlaySound.open($location)
$PlaySound.Play()
But as soon as I assign a button to it the problem as described above appears
So stripped all down to bare bones to rule out as much as I can:
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName presentationcore
# Build Form
$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
# Add Button
$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Size(35,35)
$Button.Size = New-Object System.Drawing.Size(120,23)
$Button.Text = "Play music"
$Form.Controls.Add($Button)
#Add Button event
$Button.Add_Click({
$location = 'D:\test\test.mp3'
$PlaySound = New-Object System.Windows.Media.MediaPlayer
$PlaySound.open($location)
$PlaySound.Play()
})
#Show the Form
$form.ShowDialog()| Out-Null
So when resizing the form when the music plays will cause it to stop 95% of the time. But when I throw the code in for playing the music without the button like this it never breaks.
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName presentationcore
# Build Form
$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
# Add Button
$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Size(35,35)
$Button.Size = New-Object System.Drawing.Size(120,23)
$Button.Text = "Play music"
$Form.Controls.Add($Button)
#Add Button event
$Button.Add_Click({
#Button now does nothing.. and music plays without breaking...ever
})
#Now it will always play to the end no matter what :-S
$location = 'D:\test\test.mp3'
$PlaySound = New-Object System.Windows.Media.MediaPlayer
$PlaySound.open($location)
$PlaySound.Play()
#Show the Form
$form.ShowDialog()| Out-Null
(Posted solution on behalf of the question author, to move it to the answer space).
I fixed the problem myself, the trick is to load the player at the beginning of your script like this:
#Clear the Console
CLS
#Determine Script location
$SCRIPT_PARENT = Split-Path -Parent $MyInvocation.MyCommand.Definition
#Add in the presentation core
Add-Type -AssemblyName presentationframework, presentationcore
#Load music player and set location here!
$location = ($SCRIPT_PARENT + "\Music.mp3")
$PlaySound = New-Object System.Windows.Media.MediaPlayer
###############################################################
# here comes a whole lot of code (XAML for WPF GUI etc etc) #
###############################################################
# Then in your event system only put:
#Play button action
$MainGUI.Playmusic.add_Click({
#Open file and play music
$PlaySound.open($location)
$PlaySound.Play()
})
This solved the playing problem 100%.

How would I open a manpage in GridView by clicking on a WinForms button?

I am trying to open up a manpage (Get-Help alias) when I click on a button using Powershell and WinForms.
I have a text box that allows you to input a cmdlet or help topic and when you press a button, it should open up the manpage documentation in GridView. Currently, it opens the GridView and grabs the correct help docs but something messes up along the way and I think it has to do with interpretation before it is passed off to GridView.
Here is what I have:
[void][Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms')
$form = New-Object Windows.Forms.Form
$input = New-Object Windows.Forms.TextBox
$input.Size = '100,20'
$input.Location = '10,20'
$button = New-Object Windows.Forms.Button
$button.Size = '100,20'
$button.Location = '10,60'
$button.Add_Click({
Invoke-Expression ("man " + ($global:input.Text)) | Out-GridView
})
$form.Controls.AddRange(#($input, $button)
$form.Add_Shown({$form.Activate()})
$form.ShowDialog()
What happens is the GridView opens but the title shows Invoke-Expression ("man " + ($input.Text)) | Out-GridView and the contents are the generic default information for manpages.
I tried to attach the Invoke-Expression to a variable and then pipe the variable out to GridView. I tried to set (Get-Help ($input.Text)) to a variable and then pipe it to GridView. I even tried to initialize the $input.Text property by putting $input.Text = '' just after the $input.Location property.
I really think its how the Powershell engine is interpreting the expression but I don't know how to tell it to work the way I am wanting it to.
What am I doing wrong here?
EDIT: Ok, I just realized something. I think the $input.Text property is not getting populated correctly.
What I did was added [Windows.Forms.MessageBox]::Show($input.Text) in the Click event for $button and commented out the Invoke-Expression. What it should do is open a message box and place within it what is typed in the text box ($input.Text). The message box is blank. I'm thinking that it may have to do with scoping but the $input.Text should be $global and accessible from within the Click event on the button control item.
I messed around with it after typing that last paragraph and I realized that the $input.Text property is populated correctly and is accessible in the $global scope. What I did was add [Windows.Forms.MessageBox]::Show($input.Text) at the very end of the script (after $form.ShowDialog()) and it showed exactly what I typed in the text box.
So, why is it that I can't see the $input text box properties? I haven't had this issue with some of the other WinForms apps I have built in Powershell.
Thanks for the insight.
$input is an automatic variable used in the context of the Powershell pipeline, which is why it was empty. Powershell populates it by itself, therefore overwriting anything you put in it. Rename $input to any other available name and it should work.
E.g.:
[void][Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms')
$form = New-Object Windows.Forms.Form
$box = New-Object Windows.Forms.TextBox
$box.Size = '100,20'
$box.Location = '10,20'
$button = New-Object Windows.Forms.Button
$button.Size = '100,20'
$button.Location = '10,60'
$button.Add_Click({
[Windows.Forms.MessageBox]::Show($box.Text)
Invoke-Expression ("man " + ($box.Text)) | Out-GridView
})
$form.Controls.AddRange(#($box, $button))
$form.Add_Shown({$form.Activate()})
$form.ShowDialog()

Use of CheckedChanged using Windows Forms and Powershell assistance

I'm using WinForms with PowerShell. In my tool, I'd like a checkbox that when checked, will display a message next to it, and when unchecked, it will remove the message.
I've gotten this far (I'm sure there's a much better way to do it). This makes the message pop-up, but it doesn't go away when you uncheck the box. Any help would be appreciated. Thanks!
$Checkbox_Errors.Add_CheckStateChanged({ ### Checkbox_Errors is the name of the checkbox
if ($Checkbox_Errors.Checked -eq $true)
{
$ErrorWarning1 = New-Object System.Windows.Forms.Label
$ErrorWarning1.Text = "WARNING: May take 3-5 Minutes" ### When checked, this is what it should display
$ErrorWarning1.ForeColor = "Red"
$ErrorWarning1.AutoSize = $True
$ErrorWarning1.Location = new-object System.Drawing.Point(170,13)
$groupbox.Controls.Add($ErrorWarning1)
}
})
$Checkbox_Errors.Add_CheckStateChanged({
if ($Checkbox_Errors.Unchecked -eq $true)
{
$ErrorWarning1 = New-Object System.Windows.Forms.Label
$ErrorWarning1.Text = "" ### I attempted this, where it would re-write
$ErrorWarning1.ForeColor = "Red"
$ErrorWarning1.AutoSize = $True
$ErrorWarning1.Location = new-object System.Drawing.Point(170,13)
$groupbox.Controls.Add($ErrorWarning1)
}
})
It would be simpler to create the label with the rest of your form elements. If you're using a designer, you can just drag it on with the rest of the controls. Then set the label's Visible property to $False initially to hide it.
$ErrorWarning1 = New-Object System.Windows.Forms.Label
$ErrorWarning1.Text = "WARNING: May take 3-5 Minutes"
$ErrorWarning1.ForeColor = "Red"
$ErrorWarning1.AutoSize = $True
$ErrorWarning1.Location = new-object System.Drawing.Point(170,13)
$ErrorWarning1.Visible = $False # This line hides the label initially
$groupbox.Controls.Add($ErrorWarning1)
Now in your event handler, instead of generating the label, just show or hide it based on the state of the checkbox:
$ErrorWarning1.Visible = $Checkbox_Errors.Checked
The label will always exist, but it will only be visible when the checkbox is checked.

Need help regarding creating dynamic forms in powershell

Hi i am trying to create a dynamic form in pwoershell , this is a form which has 5 buttons(color names) and each button opens a different text file(such as if red button is clicked,it should open red.txt; here is the full code;
Script Start
$var = "Red","Blue","Yellow","Black","White"
$testForm = New-Object System.Windows.Forms.Form
$testForm.Text = "Color List"
$testForm.AutoSize = $True
$testForm.AutoSizeMode = "GrowAndShrink"
$Font = New-Object System.Drawing.Font("Times New Roman",24, [System.Drawing.FontStyle]::Bold)
$testForm.Font = $Font
$Label = New-Object System.Windows.Forms.Label
$Label.Text = "Select the Color"
$Label.AutoSize = $True
$testForm.Controls.Add($Label)
$x=100
$y=50
foreach($color in $var)
{
$run = New-Object System.Windows.Forms.Button
$run.Location = New-Object System.Drawing.Size($x,$y)
$run.Size = New-Object System.Drawing.Size(100,50)
$run.Text = "$Color"
$run.Add_Click({ Invoke-Expression "notepad C:\Users\User\$color.txt" })
$testForm.Controls.Add($run)
$Font = New-Object System.Drawing.Font("Times New Roman",14,[System.Drawing.FontStyle]::Regular)
$run.font = $Font
$run.AutoSize = $True
$y+=50
}
$testForm.ShowDialog()
END Script
Everything went fine until, when the form opens with buttons, and all the buttons when clicked, open the file "White.txt" since its the last element in the array; is there any way to change the script to make each button open only their respective files and not the last color file?
please do let me know if any further questions or clarifications needed.
Looks like the problem is the Add_Click line. The script block contains a link to the variable $color, rather than evaluating it immediately to create a new "notepad..." string for Invoke-Expression and linking to that. The string for Invoke-Expression will be created when the button is clicked. By this time $color is White 'cos the loop has finished, so all the buttons end up creating a string using White.
You can fix it with a call to GetNewClosure() which will build cause the string for the Invoke-Expression to be created during the loop, not later on when the button is clicked. So change the line to:
$run.Add_Click({ Invoke-Expression "notepad C:\Users\User\$color.txt" }.GetNewClosure())
And it should work as expected.

Resources