Powershell pass a function as a parameter - powershell

I am a newbie to the world of programming and I am trying to create a form using functions to create the buttons and labels etc. The form is created with the exception that the functions passed to on button click events are not being passed correctly. For example I have a function to create a button....
function new_btn ($name, $parent, $x, $y, $l, $h, $text, $onClick){
$object = New-Object System.Windows.Forms.Button
$object.Location = New-Object System.Drawing.Point($x, $y)
$Object.Size = New-Object System.Drawing.Size($l, $h)
$Object.Text = $text
$object.add_Click({$onClick})
New-Variable $name -Value $object -Scope global
(Get-Variable $parent).Value.Controls.Add((Get-Variable $name).value)
}
I then have the function that I want to run on the button click.....
function msg {
[System.Windows.Forms.MessageBox]::Show("We are proceeding with next step.")
}
I then call the function and feed it the parameters.......
new_btn getdbslist tab1 20 50 69 23 "Get DB's" msg
This produces the button as expected and adds it to tab1, but the on click event will not work, nothing happens at all. Any help would be very appreciated!

You are just passing a string. Instead pass a script block:
new_btn getdbslist tab1 20 50 69 23 'Get DBs' {
[System.Windows.Forms.MessageBox]::Show("We are proceeding with next step.")
}
And in your new_btn function you probably just need to use
$object.add_Click($onClick)
If you really want to pass a string, then you probably need to use the following:
$object.add_Click({ & $onClick })

Related

How to Clear a Chart form

i'm currently working on a project (WOL) where i have a program that launch as a window form and have few components which one of them is a chart form.
Basically, the program can re-launch multiple times as the user wants but i'm trying to clear/reset the chart form (that is in Pie style) at each launch = delete the previous datas of the chart.
My code is way too long so i will post only the concerned parts
$WOL_W = New-Object System.Windows.Forms.Form
.... (skipping useless code part)
function Chart {
param(
[Parameter(Mandatory)]
[PSCustomObject[]]
$Results
)
$G_Graphique = New-object System.Windows.Forms.DataVisualization.Charting.Chart
$G_Graphique.Location = New-Object System.Drawing.Point(300,350)
$WOL_W.Controls.Add($G_Graphique)
$Wol_W.Add_Shown({$Graphique.Activate()})
$G_Graphique.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right -bor [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
# Objet Zone Graphique (ZG_)
$ZG_GraphArea = New-Object System.Windows.Forms.DataVisualization.Charting.ChartArea
$G_Graphique.ChartAreas.Add($ZG_GraphArea)
[void]$G_Graphique.Titles.Add("RĂ©ussite du WOL")
$G_Graphique.BackColor = [System.Drawing.Color]::White
$G_Graphique.BorderColor = 'Black'
$G_Graphique.BorderDashStyle = 'Solid'
[void]$G_Graphique.Series.Add("Data")
$G_Graphique.Series["Data"].ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Pie
$G_Graphique.Series["Data"].Points.DataBindXY($Results.Keys,$Results.Values)
}
Note : i've tried to use the followings command in another function (function of the creation for the main window) :
$G_Graphique.ChartAreas.Clear()
$Wol_W.Controls.Remove($G_Graphique)
Which failed as i want.

clearing a textbox which is in a function

i have a function that create a textbox (info box) and can be called from other functions or from buttons.
function InfoBox ($x,$y,$text){
$TextboxInfoBox = New-Object System.Windows.Forms.TextBox
$TextboxInfoBox.Location = New-Object System.Drawing.Size($x,$y)
$TextboxInfoBox.Size = New-Object System.Drawing.Size(200,800)
# Readonly Textbox.
$TextboxInfoBox.Enabled = $true
$TextboxInfoBox.Text = $text
$TabPanelButtons.Controls.Add($TextboxInfoBox)
}
After i called the textbox once, i am not able to overwrite it with another info message.
the first time i call with all the parameters ($x, $y & $text).
Infobox -x '100' -y '150' -text 'This is the first message'
The second time i only want to give another text. How can i clear the textbox and give only a new text like:
Infobox -text "This is the second message"
Any Ideas?
As commenter wrote, the function has to output the TextBox object so you can reference it later to change the text:
function InfoBox ($x,$y,$text){
$TextboxInfoBox = New-Object System.Windows.Forms.TextBox
$TextboxInfoBox.Location = New-Object System.Drawing.Size($x,$y)
$TextboxInfoBox.Size = New-Object System.Drawing.Size(200,800)
# Readonly Textbox.
$TextboxInfoBox.Enabled = $true
$TextboxInfoBox.Text = $text
$TabPanelButtons.Controls.Add($TextboxInfoBox)
$TextboxInfoBox # Output
}
Contrary to most other programming languages, in PowerShell you normally don't need to use the return statement, except for early return from a function. Simply referring to a variable by name on its own line will output its value from the function.
Now you can store the output in a variable and change the text:
$box = Infobox -x 100 -y 150 -text 'This is the first message'
$box.Text = 'This is the second message'

AutoIt _Timer_SetTimer - Additional callback arguments

Using AutoIt 3, is there a way to pass additional arguments to the callback method in the _Timer_SetTimer function?
Here's my use case (main loop) :
For $item In $items
_Timer_SetTimer(0, $timeOffset, "MyMethod")
Next
Callback method :
Func MyMethod($hWnd, $iMsg, $iTimerID, $iTime)
_Timer_KillTimer ( $hWnd, $iTimerID )
// Do something on $item
EndFunc
I tried using a Global variable, but every single instance of MyMethod then uses the last value. I did it this way :
Global $currentItem
For $item In $items
$currentItem = $item
_Timer_SetTimer(0, $timeOffset, "MyMethod")
Next
Func MyMethod($hWnd, $iMsg, $iTimerID, $iTime)
_Timer_KillTimer ( $hWnd, $iTimerID )
$item = $currentItem
// Do something on $item
EndFunc
So, am I doing it wrong or is there a way to pass argument directly? Thanks.
If your delayed calls are ordered, you could still use a Global variable to store the values in an Array :
Global $values[0]
For $item In $items
_ArrayAdd($values, $item)
_Timer_SetTimer(0, $timeOffset, "MyMethod")
Next
Func MyMethod($hWnd, $iMsg, $iTimerID, $iTime)
_Timer_KillTimer ( $hWnd, $iTimerID )
_ArrayReverse($values)
$item = _ArrayPop($values)
_ArrayReverse($values)
// Do something on $item
EndFunc
The double reverse and pop are there to simulate a FIFO queue

PowerShell GUI observer several socket-connections

I have found lots of examples that are all very long - but I think it can be done in a lot shorter way.
I nee a GUI that displays and enables several options of the socket connections I need to adimn.
In the beginning I think I have:
# Load external assemblies
[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void][Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$OnLoadForm_StateCorrection = {
$form1.WindowState = $InitialFormWindowState
}
$myGUI = New-Object System.Windows.Forms.Form
$myGUI.Text = "Socket-Traffic"
$myGUI.Name = "myGUI"
$myGUI.DataBindings.DefaultDataSourceUpdateMode = 0
$myGUI.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,255)
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 332
$System_Drawing_Size.Height = 264
$myGUI.ClientSize = $System_Drawing_Size
Now I have the list of Hashes containing all relevant information of the variables and the connections. The groupe just has to be able to access their hash:
$Sockets = $HASH1,$HASH2,$HASH3,$HASH4 # all hasches have a [string] ID: $HASH1.ID = $port
$n = 0 # do I need that?
foreach ($h in $Sockets) {
makeTab $myGUI $h $n
$n++
}
the function that I have in mind should start like that:
function makeTab {
param (
[parameter(Mandatory=$true)]
[PSObject] $gui,
[parameter(Mandatory=$true)]
[PSObject] $hashTable, # with all info to connect and vars.
[parameter(Mandatory=$true)]
[int] $noTab
)
... ??
}
Each Socket-Tab has to have these internal groupes:
all function calls behind a buttonclick like:
$x_OnClick = { Write-host "Button clicked, do..." }
1) Send-a-Line Groupe
a) Line to enter test meant to sent.
b) Button to send # no 'cancle'
2) Login-Groupe:
a) status: Login=Green.ico, Connected=Orange.ico, nothing=red.ico
b) Button: Login (connect & login)
c) Button: Logout (Logout & disconnect)
3) Logging-Groupe:
a) last 2 Lines been sent
b) last 2 line received
4) Status Groupe
a) text lines with var. info. about the connection
b) text lines with var. info. about the connection
...
Global - Finally
a) Button to Exit the script with logout & disconnect all socket connections...
May s.o. can draft an example? After that I can determine the size and the place of the various and the buttons within a group.
Thank you very much in advance!
Gooly
If you are able to use PowerSHell 3.0 or higher, you can use WPF, where forms are just XML-like text. You can create an XML form in visual designer, like Visual Studio Express.
Like this: TreeView Example

Powershell Progress Bar in Windows Forms

I'm trying to add a progress bar to a form in powershell. I do not want to use PowerShell's Write-Progress cmdlet (because when I run the script from command line, it shows a text-based progress bar and I always want a form/graphic based bar).
I've tried this and it seems to work(found online):
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
$form_main = New-Object System.Windows.Forms.Form
$progressBar1 = New-Object System.Windows.Forms.ProgressBar
$timer1 = New-Object System.Windows.Forms.Timer
$timer1_OnTick = {
$progressBar1.PerformStep()
}
$form_main.Text = 'ProgressBar demo'
$progressBar1.DataBindings.DefaultDataSourceUpdateMode = 0
$progressBar1.Step = 1
$progressBar1.Name = 'progressBar1'
$form_main.Controls.Add($progressBar1)
$timer1.Interval = 100
$timer1.add_tick($timer1_OnTick)
$timer1.Start()
$form_main.ShowDialog()| Out-Null
However, I do not want an event to update the progress bar (as does $timer1_OnTic in the example above) I want to update it myself by making calls throughout my script such as:
$progressBar1.PerformStep()
Or
$progressBar1.Value = 10
So it seems I need some sort of background worker that updates the progress bar whenever I make calls to PerformStep() or change the value of the progressBar
Calling ShowDialog stops all processing inside the script until the form is closed.
If I understand correctly, you should be able to change ShowDialog() to Show(), which will display the Dialog without blocking your script. You can then continue execution and update the progress bar.
You may be disappointed in the lack of interactivity of the form though.
A method I have had some success with is to use a child runspace for the GUI (in this case WPF) so it doesn't lock the script. Data can be accessed in both the parent and sub runspaces via the session state proxy.
e.g.
# define the shared variable
$sharedData = [HashTable]::Synchronized(#{});
$sharedData.Progress = 0;
$sharedData.state = 0;
$sharedData.EnableTimer = $true;
# Set up the runspace (STA is required for WPF)
$rs = [RunSpaceFactory]::CreateRunSpace();
$rs.ApartmentState = "STA";
$rs.ThreadOptions = "ReuseThread";
$rs.Open();
# configure the shared variable as accessible from both sides (parent and child runspace)
$rs.SessionStateProxy.setVariable("sharedData", $sharedData);
# define the code to run in the child runspace
$script = {
add-Type -assembly PresentationFramework;
add-Type -assembly PresentationCore;
add-Type -assembly WindowsBase;
[xml]$xaml = #"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
MaxHeight="100" MinHeight="100" Height="100"
MaxWidth="320" MinWidth="320" Width="320"
WindowStyle="ToolWindow">
<Canvas Grid.Row="1">
<TextBlock Name="ProgressText" Canvas.Top="10" Canvas.Left="20">Hello world</TextBlock>
<ProgressBar Name="ProgressComplete" Canvas.Top="30" Canvas.Left="20" Width="260" Height="20" HorizontalAlignment="Center" Value="20" />
</Canvas>
</Window>
"#
# process the xaml above
$reader = New-Object System.Xml.XmlNodeReader $xaml;
$dialog = [Windows.Markup.XamlReader]::Load($reader);
# get an handle for the progress bar
$progBar = $dialog.FindName("ProgressComplete");
$progBar.Value = 0;
# define the code to run at each interval (update the bar)
# DON'T forget to include a way to stop the script
$scriptBlock = {
if ($sharedData.EnableTimer = $false) {
$timer.IsEnabled = $false;
$dialog.Close();
}
$progBar.value = $sharedData.Progress;
}
# at the timer to run the script on each 'tick'
$dialog.Add_SourceInitialized( {
$timer = new-Object System.Windows.Threading.DispatherTimer;
$timer.Interface = [TimeSpan]"0:0:0.50";
$timer.Add_Tick($scriptBlock);
$timer.Start();
if (!$timer.IsEnabled) {
$dialog.Close();
}
});
# Start the timer and show the dialog
&$scriptBlock;
$dialog.ShowDialog() | out-null;
}
$ps = [PowerShell]::Create();
$ps.Runspace = $rs;
$ps.AddScript($script).BeginInvoke();
# if you want data from your GUI, you can access it through the $sharedData variable
Write-Output $sharedData;
If you try this code, once the dialog is displayed you can change the progress bar by setting the value of $sharedData.Progress
This has allowed me to write plenty of dialogs for tools, I'm constrained by our infrastructure to use powershell from within a runspace and WPF seems to work much better than forms.
Have a look at Posh Progress Bar it has horizontal, vertical and circle progress bars.