views:

58

answers:

2

I am trying to loop through some controls in a Powershell / WPF application. Nothing fancy, just setting some text on a mouseEnter event.

It works fine if I don't loop the code:

$reader = New-Object System.Xml.XmlNodeReader $xaml 
$d = [Windows.Markup.XamlReader]::Load($reader) 

$d.FindName("GridOne").add_mouseEnter({ 
        $d.FindName("HelpText").Content = "One"
     })

$d.FindName("GridTwo").add_mouseEnter({ 
        $d.FindName("HelpText").Content = "Two"
     })

$d.FindName("GridThree").add_mouseEnter({ 
        $d.FindName("HelpText").Content = "Three"
     })

$d.FindName("GridFour").add_mouseEnter({ 
        $d.FindName("HelpText").Content = "Four"
     })     

But if I try the same thing as a loop it sets all the controls MouseEnter events to set the text to "Four", the last element of the array:

$reader = New-Object System.Xml.XmlNodeReader $xaml 
$d = [Windows.Markup.XamlReader]::Load($reader) 

$arrControls = ("One","Two","Three","Four")

foreach ($control in $arrControls) { 
     $d.FindName("Grid$control").add_mouseEnter({ 
        $d.FindName("HelpText").Content = $control
     })
}

Anyone have any thoughts why this is, and how I can fix it?

Thanks,

Ben

OK - This is even weirder...

Tried to address this using a solution along the lines of Kent's suggestion. I got the same using $localControl, so thought I'd try using an array to ensure each entry was distinct:

$i = 0
$localControl = @()
foreach ($control in $arrControls) { 
    $localControl += $control
    write-host "$d.FindName('Grid$control').add_mouseEnter({ $d.FindName('HelpText').Content = $control })"
     $d.FindName("Grid$control").add_mouseEnter({ 
        $d.FindName("HelpText").Content = $localControl[$i]
        $i = $i + 1
     })
}

The behaviour I know get is that each time I mouseOver a control, the text just increments through the array one step. For example, the first control I hover over will output "One", the next will output "Two" and so on, until my array is exhausted when it just outputs null.

This "One", "Two", "Three", "Four" output order is the same regardless of the order I hover over the controls.

... Wait a minute. Have put the $i = $i + 1 in the MouseEnter!

Amended to:

$i = 0
$localControl = @()
foreach ($control in $arrControls) { 
    $localControl += $control
    write-host "$d.FindName('Grid$control').add_mouseEnter({ $d.FindName('HelpText').Content = $control })"
     $d.FindName("Grid$control").add_mouseEnter({ 
        $d.FindName("HelpText").Content = $localControl[$i]
     })
     $i = $i + 1
}

Sets all the outputs to null.

A: 

Take a local copy of $control:

foreach ($control in $arrControls) { 
     $localControl = $control
     $d.FindName("Grid$control").add_mouseEnter({ 
        $d.FindName("HelpText").Content = $localControl
     })
}

All references to $control are being resolved after your loop has completed, so they all refer to the last item in your collection.

HTH, Kent

Kent Boogaart
I got the same with $localControl, so tried an array. This gave the output above...
Ben
+1  A: 

You need to make a closure from the scriptblocks. What you are facing to is like this:

$i = 1
$block1 = { write-host $i }
$i = 2
$block2 = { write-host $i }
& $block1
& $block2

#---------------- after a change...

$i = 1
$block1 = { write-host $i }.GetNewClosure()
$i = 2
$block2 = { write-host $i }.GetNewClosure()
$i = 3
$block3 = { write-host $i }.GetNewClosure()
& $block1
& $block2
& $block3

So if you make a closure, it could help:

foreach ($control in $arrControls) { 
     $d.FindName("Grid$control").add_mouseEnter({ 
        $d.FindName("HelpText").Content = $control
     }.GetNewClosure())
}

Variables in scriptblock are not bound to the scope where there were created. Instead they are evaluated in current scope:

PS> $var = 'at beginning'
PS> $writer = { write-host $var }
PS> function test {
  $var = 1
  & $writer
  & { 
    $var = 10
    & $writer 
  }
}
PS> test
1
10
PS> & $writer
at beginning
stej
That's fantastic - had me stumped! Thanks Stej.
Ben
You are welcome :) Just curious.. why don't you use some dsl like PowerBoots/WPK?
stej