Reputation: 2110
I'm writing a script for a user that concatenates several PDF files and appends tabular data as a text file. Now the problem is that the user can manually name the text files so that the script has to verify which PDF file belongs to the data. Usually it would do so by name, but since the user can (and will obviously) change the name of the PDF, the script shall ask the user to pick the correct PDF to merge.
I've written a function in my script, that uses Winforms to display a Listbox with the available PDF files and the user should pick one.
function Select-Rechnung
{
param
(
[string] $Rechnung,
[string[]] $PdfFiles
)
$form = New-Object System.Windows.Forms.Form
$form.Text = "Rechnung wählen"
$form.Size = New-Object System.Drawing.Size(640,320)
$form.StartPosition = "CenterScreen"
$form.KeyPreview = $true
$form.Add_KeyDown({if($_.KeyCode -eq "Enter") { $x = $PdfFiles[$ListBox.SelectedIndex]; $form.Close() }})
$form.Add_KeyDown({if($_.KeyCode -eq "Escape") { $form.Close() }})
$OkButton = New-Object System.Windows.Forms.Button
$OkButton.Location = New-Object System.Drawing.Size(240,240)
$OkButton.Size = New-Object System.Drawing.Size(75,23)
$OkButton.Text = "OK"
$OkButton.Add_Click({ $x = $PdfFiles[$ListBox.SelectedIndex]; $form.Close() })
$form.Controls.Add($OkButton)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(325,240)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Abbrechen"
$CancelButton.Add_Click({$form.Close()})
$form.Controls.Add($CancelButton)
$Label = New-Object System.Windows.Forms.Label
$Label.Location = New-Object System.Drawing.Size(10,20)
$Label.Size = New-Object System.Drawing.Size(600,20)
$Label.Text = [string]::Format("Für die Rechnung {0} wurden mehrere mögliche Dateien gefunden. Bitte auswählen:", $Rechnung)
$form.Controls.Add($Label)
$ListBox = New-Object System.Windows.Forms.ListBox
$ListBox.Location = New-Object System.Drawing.Size(10,40)
$ListBox.Size = New-Object System.Drawing.Size(600, 20)
$ListBox.Height = 200
foreach($pdfFile in $PdfFiles)
{
[void] $ListBox.Items.Add($pdfFile)
}
$form.Controls.Add($ListBox)
$form.TopMost = $true
$form.Add_Shown({$form.Activate()})
[void] $form.ShowDialog()
$x
}
Now within the KeyDown
handler or the Click
handler, the function should assign the selected PDF file to the variable $x
. I've checked that the $PdfFiles
are correctly handed to the function and that during the handlers execution, the $PdfFiles[$ListBox.SelectedIndex]
actually has the correct string value. However, when I access $x
after the forms ShowDialog
has been processed, it is empty and thus the function's return value is empty.
Why won't it assign the value (that it correctly evaluates during the handler) to my variable and return it?
Upvotes: 1
Views: 2679
Reputation: 2110
I did it now by working around the problem. In the KeyDown
and respectivlely the Click
handler I'm no longer setting the variable directly but as:
$form.Add_KeyDown({
if($_.KeyCode -eq "Enter")
{
if($ListBox.SelectedIndex -ge 0)
{
$form.DialogResult = System.Windows.Forms.DialogResult]::OK;
}
$form.Close()
}
})
And in the end I do check for the DialogResult
:
[string]$x = $null
if($form.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK)
{
$x = $PdfFiles[$ListBox.SelectedIndex]
}
return $x
That did result in the correct value being returned.
Upvotes: 2
Reputation: 35398
Try it by introducing a reference type like a hashtable and use it to modify and return the value like so
function Select-Rechnung
{
param
(
[string] $Rechnung,
[string[]] $PdfFiles
)
$x = @{ Value = '' }
...
$OkButton.Add_Click({ $x.Value = $PdfFiles[$ListBox.SelectedIndex];
...
$x.Value
}
You could also achieve your goal by accessing the $x
variable of the outer scope with
Set-Variable -Name x -Scope 1 $PdfFiles[$ListBox.SelectedIndex]
or one of the other scope modifiers (global
or script
- see about scopes) if you want to avoid the extra hash table.
Upvotes: 4
Reputation: 3236
I suggest you to change scope level for $x variable to at least script scope, so it would be available everywhere within your script. Right now it's only "lives" inside script block of handle event. Change every $x variable to $script:x
function Select-Rechnung
{
param
(
[string] $Rechnung,
[string[]] $PdfFiles
)
$form = New-Object System.Windows.Forms.Form
$form.Text = "Rechnung wählen"
$form.Size = New-Object System.Drawing.Size(640,320)
$form.StartPosition = "CenterScreen"
$form.KeyPreview = $true
$form.Add_KeyDown({if($_.KeyCode -eq "Enter") { $script:x = $PdfFiles[$ListBox.SelectedIndex]; $form.Close() }})
$form.Add_KeyDown({if($_.KeyCode -eq "Escape") { $form.Close() }})
$OkButton = New-Object System.Windows.Forms.Button
$OkButton.Location = New-Object System.Drawing.Size(240,240)
$OkButton.Size = New-Object System.Drawing.Size(75,23)
$OkButton.Text = "OK"
$OkButton.Add_Click({ $script:x = $PdfFiles[$ListBox.SelectedIndex]; $form.Close() })
$form.Controls.Add($OkButton)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(325,240)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Abbrechen"
$CancelButton.Add_Click({$form.Close()})
$form.Controls.Add($CancelButton)
$Label = New-Object System.Windows.Forms.Label
$Label.Location = New-Object System.Drawing.Size(10,20)
$Label.Size = New-Object System.Drawing.Size(600,20)
$Label.Text = [string]::Format("Für die Rechnung {0} wurden mehrere mögliche Dateien gefunden. Bitte auswählen:", $Rechnung)
$form.Controls.Add($Label)
$ListBox = New-Object System.Windows.Forms.ListBox
$ListBox.Location = New-Object System.Drawing.Size(10,40)
$ListBox.Size = New-Object System.Drawing.Size(600, 20)
$ListBox.Height = 200
foreach($pdfFile in $PdfFiles)
{
[void] $ListBox.Items.Add($pdfFile)
}
$form.Controls.Add($ListBox)
$form.TopMost = $true
$form.Add_Shown({$form.Activate()})
[void] $form.ShowDialog()
$script:x
}
It's now working for me with that way:
Select-Rechnung -Rechnung 'string' -PdfFiles 'file1.pdf','file2.pdf','file3.pdf'
Output:
file3.pdf
Upvotes: 2