1

I'm making a simple GUI form using PowerShell with System.Windows.Forms.

pic

For each text box - button pair, I would like the button to open the file browser, and once a file is selected, the file path should be written in the corresponding text box.

In the code below, New-TextBox creates a label and a text box, and New-Button creates a button.

3 text boxes with 3 buttons are created using those functions.

I then created the function Set-FileDialogButton. It takes a button and text box as parameters, and should create a button click event that opens the file browser, get the selected file path and write it to the text box from the argument.

The click event and filebrowser works. However, after selecting the file, I get

The property 'Text' cannot be found on this object. Verify that the property exists and can b
e set.
At D:\...\test.ps1:62 char:5
+                 $inputTextBox.Text = $FileBrowser.FileName
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

So I'm assuming I'm doing something wrong when passing the textbox in the function. If I don't wrap the click event in a function, my code works fine.

The code:

Add-Type -assembly System.Windows.Forms
$main_form = New-Object System.Windows.Forms.Form
$main_form.Text ='AP209 Converter GUI'

$main_form.Width = 300
$main_form.Height = 200
$main_form.AutoSize = $true

#Creates a label and text box:
function New-TextBox($labelText, $x, $y, $w){
    $label = New-Object System.Windows.Forms.Label
    $label.Text = $labelText
    $label.Location  = New-Object System.Drawing.Point($x, $y)
    $label.AutoSize = $true
    $main_form.Controls.Add($label)

    $textbox            = New-Object system.Windows.Forms.TextBox
    $textbox.multiline  = $false
    $textbox.width      = $w
    $textbox.height     = 20
    $textbox.location   = New-Object System.Drawing.Point(($x + 50), $y)
    $main_form.Controls.Add($textbox)

    return $textbox
}
#Creates a button with $text as label:
function New-Button($text, $x, $y, $w, $h){
    $button = New-Object System.Windows.Forms.Button
    $button.Location = New-Object System.Drawing.Size($x, $y)
    $button.Size = New-Object System.Drawing.Size($w, $h)
    $button.Text = $text
    $main_form.Controls.Add($button)
    return $button
}

$textBoxW = 100
$labelX = 0
$buttonX = $x + 155

$y = 10
$dy = 30

$buttonW = 25
$buttonH = 20

#Create 3 text boxes and buttons:
$textbox1 = New-TextBox "File 1" $labelX  $y $textBoxW
$button1  = New-Button  "..."    $buttonX $y $buttonW $buttonH
$y = $y + $dy
$textbox2 = New-TextBox "File 2" $labelX  $y $textBoxW
$button2  = New-Button  "..."    $buttonX $y $buttonW $buttonH
$y = $y + $dy
$textbox3 = New-TextBox "File 3" $labelX  $y $textBoxW
$button3  = New-Button  "..."    $buttonX $y $buttonW $buttonH


#Set click event to button:
function Set-FileDialogButton($button, $inputTextBox){
    $button.Add_Click(
        {
            $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{ InitialDirectory = $loadpath}
            $fileBrowserReturned = $FileBrowser.ShowDialog()
            if($fileBrowserReturned -eq "OK"){
                $inputTextBox.Text = $FileBrowser.FileName
                $inputTextBox.SelectionStart = $inputTextBox.Text.Length;
            }                   
        }
    )
}
Set-FileDialogButton $button1 $textbox1
Set-FileDialogButton $button2 $textbox2
Set-FileDialogButton $button3 $textbox3



$main_form.ShowDialog()
2
  • Your code would be easier and far more readable if you just deal with each filebrowse button seperately in an AddClick event. Commented Aug 27, 2019 at 9:21
  • @Scepticalist The idea was to remove repetitive code by wrapping the AddClick in a function, and make it more readable... Commented Aug 27, 2019 at 9:30

2 Answers 2

3

You need to capture your variable references at the time of definition and bind them to the local scope of your event handler. You can do this with GetNewClosure():

#Set click event to button:
function Set-FileDialogButton($button, $inputTextBox){
    $button.Add_Click(
        {
            $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{ InitialDirectory = $loadpath}
            $fileBrowserReturned = $FileBrowser.ShowDialog()
            if($fileBrowserReturned -eq "OK"){
                $inputTextBox.Text = $FileBrowser.FileName
                $inputTextBox.SelectionStart = $inputTextBox.Text.Length;
            }                   
        }.GetNewClosure()
    )
}

With your current code your variables do not get bound until the event handler is executed. Hence the inputTextBox is null.

Sign up to request clarification or add additional context in comments.

Comments

1

Try this:

Add-Type -assembly System.Windows.Forms
$main_form = New-Object System.Windows.Forms.Form
$main_form.Text ='AP209 Converter GUI'

$main_form.Width = 300
$main_form.Height = 200
$main_form.AutoSize = $true

#Creates a label and text box:
function New-TextBox($labelText, $x, $y, $w){
    $label = New-Object System.Windows.Forms.Label
    $label.Text = $labelText
    $label.Location  = New-Object System.Drawing.Point($x, $y)
    $label.AutoSize = $true
    $main_form.Controls.Add($label)

    $textbox            = New-Object system.Windows.Forms.TextBox
    $textbox.multiline  = $false
    $textbox.width      = $w
    $textbox.height     = 20
    $textbox.location   = New-Object System.Drawing.Point(($x + 50), $y)
    $main_form.Controls.Add($textbox)

    return $textbox
}
#Creates a button with $text as label:
function New-Button($text, $x, $y, $w, $h){
    $button = New-Object System.Windows.Forms.Button
    $button.Location = New-Object System.Drawing.Size($x, $y)
    $button.Size = New-Object System.Drawing.Size($w, $h)
    $button.Text = $text
    $main_form.Controls.Add($button)
    return $button
}

$textBoxW = 100
$labelX = 0
$buttonX = $x + 155

$y = 10
$dy = 30

$buttonW = 25
$buttonH = 20

#Create 3 text boxes and buttons:
$textbox1 = New-TextBox "File 1" $labelX  $y $textBoxW
$textbox1.Name = 'textbox1'
$button1  = New-Button  "..."    $buttonX $y $buttonW $buttonH
$button1.Name = 'button1'
$y = $y + $dy
$textbox2 = New-TextBox "File 2" $labelX  $y $textBoxW
$textbox2.Name = 'textbox2'
$button2  = New-Button  "..."    $buttonX $y $buttonW $buttonH
$button2.Name = 'button2'
$y = $y + $dy
$textbox3 = New-TextBox "File 3" $labelX  $y $textBoxW
$textbox3.Name = 'textbox3'
$button3  = New-Button  "..."    $buttonX $y $buttonW $buttonH
$button3.Name = 'button3'


foreach( $control in $main_form.Controls ) {

    if( $control.Name -like 'button*' ) {
        $control.Add_Click(
            {
                $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{ InitialDirectory = $loadpath}
                $fileBrowserReturned = $FileBrowser.ShowDialog()
                if($fileBrowserReturned -eq [System.Windows.Forms.DialogResult]::OK ) {
                    $controlNumber = $this.Name -replace '^\D+(\d+)$', '$1'
                    $textboxName   = 'textbox' + $controlNumber
                    foreach( $control in $this.parent.Controls ) {
                        if( $control.Name -eq $textboxName ) {
                            $control.Text = $FileBrowser.FileName 
                            $control.SelectionStart = $control.Text.Length
                        }
                    }
                }
            } )

    }

}


$main_form.ShowDialog()

2 Comments

The answer @Palle Due works great. I tried this too, but looks like $control.Name -like 'button*' never returns true?
You have to give names to the controls. copy & paste my source complete, it will work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.