2

Since I need to create this pattern multiple times in a WPF window, I created a function like below.

function Group_WPF {
    param (
        [Windows.Controls.Canvas]$canvas,
        [int]$top,
        [ref]$f1,
        [string]$n1,
        [ref]$f2,
        [string]$n2,
        [ref]$isR,
        [ref]$isB,
        [ref]$bu,
    )

    $f1.Value = New-Object Windows.Controls.TextBox
    $f1.Value.Width = 380
    [Windows.Controls.Canvas]::SetLeft($f1.Value, 10)
    [Windows.Controls.Canvas]::SetTop($f1.Value, $top + 25)
    $canvas.Children.Add($f1.Value)

    $f1.Value.Add_MouseDoubleClick({
        param ($source, $e)
        $source.SelectAll()
    })

    $f2.Value = New-Object Windows.Controls.TextBox
    $f2.Value.Width = 380
    [Windows.Controls.Canvas]::SetRight($f2.Value, 10)
    [Windows.Controls.Canvas]::SetTop($f2.Value, $top + 25)
    $canvas.Children.Add($f2.Value)

    $f2.Value.Add_MouseDoubleClick({
        param ($source, $e)
        $source.SelectAll()
    })

    $isR.Value = New-Object Windows.Controls.CheckBox
    $isR.Value.Content = "Re"
    $isR.Value.IsChecked = $true
    [Windows.Controls.Canvas]::SetLeft($isR.Value, 210)
    [Windows.Controls.Canvas]::SetTop($isR.Value, $top + 50)
    $canvas.Children.Add($isR.Value)

    $isB.Value = New-Object Windows.Controls.CheckBox
    $isB.Value.Content = "Bu"
    [Windows.Controls.Canvas]::SetLeft($isB.Value, 290)
    [Windows.Controls.Canvas]::SetTop($isB.Value, $top + 50)
    $canvas.Children.Add($isB.Value)

    $bu.Value = New-Object Windows.Controls.TextBox
    $bu.Value.Width = 380
    $bu.Value.IsReadOnly = $true
    $bu.Value.Background = [System.Windows.Media.Brushes]::Gainsboro
    [Windows.Controls.Canvas]::SetRight($bu.Value, 10)
    [Windows.Controls.Canvas]::SetTop($bu.Value, $top + 50)
    $canvas.Children.Add($bu.Value)

    $bu.Value.Add_MouseDoubleClick({
        param ($source, $e)
        $source.SelectAll()
    })

    $isB.Value.Add_Checked({
        $bu.Value.IsReadOnly = $false
        $bu.Value.Background = [System.Windows.Media.Brushes]::White
        $isR.Value.IsChecked = $false
    })

    $isB.Value.Add_Unchecked({
        $bu.Value.IsReadOnly = $true
        $bu.Value.Background = [System.Windows.Media.Brushes]::Gainsboro
    })

    $isR.Value.Add_Checked({
        $isB.Value.IsChecked = $false
    })
}

All works, except the Add_Checked and Add_Unchecked events. When I tried them, I received the following error:

The property 'IsReadOnly' cannot be found on this object. Verify that the property exists and can be set.
At line:111 char:9
+         $bu.Value.IsReadOnly = $false

The property 'Background' cannot be found on this object. Verify that the property exists and can be set.
At line:112 char:9
+         $bu.Value.Background = [System.Windows.Media.Brushes]::White

The property 'IsChecked' cannot be found on this object. Verify that the property exists and can be set.
At line:113 char:9
+         $isR.Value.IsChecked = $false

Please forgive me for my stupidity. Please guide me how to solve this problem. Sincerely thank you.

3
  • What's the purpose of all the [ref] parameters? Commented Jun 18, 2024 at 22:44
  • @MathiasR.Jessen [ref] parameters help me to set many different variable names and use these variables in specific cases. Commented Jun 19, 2024 at 1:56
  • Use them afterwards? Just output the resulting controls from the function :) Commented Jun 19, 2024 at 8:01

2 Answers 2

2

Because you need the local variables from the caller context (ie: $bu, $isR and $isB) you need to call GetNewClosure:

$isB.Value.Add_Checked({
    $bu.Value.IsReadOnly = $false
    $bu.Value.Background = [System.Windows.Media.Brushes]::White
    $isR.Value.IsChecked = $false
}.GetNewClosure())

$isB.Value.Add_Unchecked({
    $bu.Value.IsReadOnly = $true
    $bu.Value.Background = [System.Windows.Media.Brushes]::Gainsboro
}.GetNewClosure())

$isR.Value.Add_Checked({
    $isB.Value.IsChecked = $false
}.GetNewClosure())

I found this solution here.

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

Comments

1

In addition to Orace's suggestion to close over the referenced variables, I'd suggest getting rid of the [ref] parameters - if you want to reference the instantiated controls afterwards simply output them from the function:

function Group_WPF {
    param (
        [Windows.Controls.Canvas]$canvas,
        [int]$top,
        [string]$n1,
        [string]$n2
    )

    $f1 = New-Object Windows.Controls.TextBox
    $f1.Width = 380
    [Windows.Controls.Canvas]::SetLeft($f1, 10)
    [Windows.Controls.Canvas]::SetTop($f1, $top + 25)
    [void]$canvas.Children.Add($f1)

    $f1.Add_MouseDoubleClick({
        param ($source, $e)
        $source.SelectAll()
    })

    $f2 = New-Object Windows.Controls.TextBox
    $f2.Width = 380
    [Windows.Controls.Canvas]::SetRight($f2, 10)
    [Windows.Controls.Canvas]::SetTop($f2, $top + 25)
    [void]$canvas.Children.Add($f2)

    $f2.Add_MouseDoubleClick({
        param ($source, $e)
        $source.SelectAll()
    })

    $isR = New-Object Windows.Controls.CheckBox
    $isR.Content = "Re"
    $isR.IsChecked = $true
    [Windows.Controls.Canvas]::SetLeft($isR, 210)
    [Windows.Controls.Canvas]::SetTop($isR, $top + 50)
    [void]$canvas.Children.Add($isR)

    $isB = New-Object Windows.Controls.CheckBox
    $isB.Content = "Bu"
    [Windows.Controls.Canvas]::SetLeft($isB, 290)
    [Windows.Controls.Canvas]::SetTop($isB, $top + 50)
    [void]$canvas.Children.Add($isB)

    $bu = New-Object Windows.Controls.TextBox
    $bu.Width = 380
    $bu.IsReadOnly = $true
    $bu.Background = [System.Windows.Media.Brushes]::Gainsboro
    [Windows.Controls.Canvas]::SetRight($bu, 10)
    [Windows.Controls.Canvas]::SetTop($bu, $top + 50)
    [void]$canvas.Children.Add($bu)

    $bu.Add_MouseDoubleClick({
        param ($source, $e)
        $source.SelectAll()
    })

    $isB.Add_Checked({
        $bu.IsReadOnly = $false
        $bu.Background = [System.Windows.Media.Brushes]::White
        $isR.IsChecked = $false
    })

    $isB.Add_Unchecked({
        $bu.IsReadOnly = $true
        $bu.Background = [System.Windows.Media.Brushes]::Gainsboro
    })

    $isR.Add_Checked({
        $isB.IsChecked = $false
    })

    return [pscustomobject]@{
      F1 = $f1
      F2 = $f2
      IsR = $isR
      IsB = $isB
      BU = $bu
    }
}

Not only does this make the function easier to read, it also becomes much easier to invoke:

$myGroup = Group_WPF -top $top -n1 $n1 -n2 $n2

# now you can reference the individual controls via the properties on the output object:
if ($myGroup.IsB.IsChecked) {
  $myGroup.BU.Text
}

1 Comment

It's so beautiful, I didn't know it was possible to do that. You are so kind and thoughtful, thank you so much.

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.